/* * QQQ - Low-code Application Framework for Engineers. * Copyright (C) 2021-2022. Kingsrook, LLC * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States * contact@kingsrook.com * https://github.com/Kingsrook/ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData"; import {QWidgetMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QWidgetMetaData"; import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord"; import {DataGridPro, GridCallbackDetails, GridRowParams, MuiEvent} from "@mui/x-data-grid-pro"; import React, {useEffect, useState} from "react"; import {useNavigate} from "react-router-dom"; import Widget, {AddNewRecordButton, ExportDataButton, HeaderLink, LabelComponent} from "qqq/components/widgets/Widget"; import DataGridUtils from "qqq/utils/DataGridUtils"; import HtmlUtils from "qqq/utils/HtmlUtils"; import Client from "qqq/utils/qqq/Client"; import ValueUtils from "qqq/utils/qqq/ValueUtils"; interface Props { widgetMetaData: QWidgetMetaData; data: any; } RecordGridWidget.defaultProps = {}; const qController = Client.getInstance(); function RecordGridWidget({widgetMetaData, data}: Props): JSX.Element { const [rows, setRows] = useState([]); const [records, setRecords] = useState([] as QRecord[]) const [columns, setColumns] = useState([]); const [allColumns, setAllColumns] = useState([]) const navigate = useNavigate(); useEffect(() => { if (data && data.childTableMetaData && data.queryOutput) { const records: QRecord[] = []; const queryOutputRecords = data.queryOutput.records; if (queryOutputRecords) { for (let i = 0; i < queryOutputRecords.length; i++) { records.push(new QRecord(queryOutputRecords[i])); } } const tableMetaData = new QTableMetaData(data.childTableMetaData); const rows = DataGridUtils.makeRows(records, tableMetaData); ///////////////////////////////////////////////////////////////////////////////// // note - tablePath may be null, if the user doesn't have access to the table. // ///////////////////////////////////////////////////////////////////////////////// const childTablePath = data.tablePath ? data.tablePath + (data.tablePath.endsWith("/") ? "" : "/") : data.tablePath; const columns = DataGridUtils.setupGridColumns(tableMetaData, childTablePath, null, "bySection"); ///////////////////////////////////////////////////////////////////////////////////////////////////////////// // capture all-columns to use for the export (before we might splice some away from the on-screen display) // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// setAllColumns(JSON.parse(JSON.stringify(columns))); //////////////////////////////////////////////////////////////// // do not not show the foreign-key column of the parent table // //////////////////////////////////////////////////////////////// if(data.defaultValuesForNewChildRecords) { for (let i = 0; i < columns.length; i++) { if(data.defaultValuesForNewChildRecords[columns[i].field]) { columns.splice(i, 1); i-- } } } setRows(rows); setRecords(records) setColumns(columns); } }, [data]); const exportCallback = () => { let csv = ""; for (let i = 0; i < allColumns.length; i++) { csv += `${i > 0 ? "," : ""}"${ValueUtils.cleanForCsv(allColumns[i].headerName)}"` } csv += "\n"; for (let i = 0; i < records.length; i++) { for (let j = 0; j < allColumns.length; j++) { const value = records[i].displayValues.get(allColumns[j].field) ?? records[i].values.get(allColumns[j].field) csv += `${j > 0 ? "," : ""}"${ValueUtils.cleanForCsv(value)}"` } csv += "\n"; } const fileName = (data?.label ?? widgetMetaData.label) + " " + ValueUtils.formatDateTimeForFileName(new Date()) + ".csv"; HtmlUtils.download(fileName, csv); } /////////////////// // view all link // /////////////////// const labelAdditionalComponentsLeft: LabelComponent[] = [] if(data && data.viewAllLink) { labelAdditionalComponentsLeft.push(new HeaderLink("View All", data.viewAllLink)); } /////////////////// // export button // /////////////////// let isExportDisabled = true; let tooltipTitle = "Export"; if (data && data.childTableMetaData && data.queryOutput && data.queryOutput.records && data.queryOutput.records.length > 0) { isExportDisabled = false; if(data.totalRows && data.queryOutput.records.length < data.totalRows) { tooltipTitle = "Export these " + data.queryOutput.records.length + " records." if(data.viewAllLink) { tooltipTitle += "\nClick View All to export all records."; } } } labelAdditionalComponentsLeft.push(new ExportDataButton(() => exportCallback(), isExportDisabled, tooltipTitle)) //////////////////// // add new button // //////////////////// const labelAdditionalComponentsRight: LabelComponent[] = [] if(data && data.canAddChildRecord) { let disabledFields = data.disabledFieldsForNewChildRecords; if(!disabledFields) { disabledFields = data.defaultValuesForNewChildRecords; } labelAdditionalComponentsRight.push(new AddNewRecordButton(data.childTableMetaData, data.defaultValuesForNewChildRecords, "Add new", disabledFields)) } const handleRowClick = (params: GridRowParams, event: MuiEvent, details: GridCallbackDetails) => { (async () => { const qInstance = await qController.loadMetaData() const tablePath = qInstance.getTablePathByName(data.childTableMetaData.name) if(tablePath) { navigate(`${tablePath}/${params.id}`); } })(); }; return ( (params.indexRelativeToCurrentPage % 2 === 0 ? "even" : "odd")} onRowClick={handleRowClick} // getRowHeight={() => "auto"} // maybe nice? wraps values in cells... // components={{Toolbar: CustomToolbar, Pagination: CustomPagination, LoadingOverlay: Loading}} // pinnedColumns={pinnedColumns} // onPinnedColumnsChange={handlePinnedColumnsChange} // pagination // paginationMode="server" // rowsPerPageOptions={[20]} // sortingMode="server" // filterMode="server" // page={pageNumber} // checkboxSelection rowCount={data && data.totalRows} // onPageSizeChange={handleRowsPerPageChange} // onStateChange={handleStateChange} // density={density} // loading={loading} // filterModel={filterModel} // onFilterModelChange={handleFilterChange} // columnVisibilityModel={columnVisibilityModel} // onColumnVisibilityModelChange={handleColumnVisibilityChange} // onColumnOrderChange={handleColumnOrderChange} // onSelectionModelChange={selectionChanged} // onSortModelChange={handleSortChange} // sortingOrder={[ "asc", "desc" ]} // sortModel={columnSortModel} /> ); } export default RecordGridWidget;