mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-18 05:10:45 +00:00
QQQ-21: updated list table to paginate properly
This commit is contained in:
@ -16,6 +16,7 @@
|
|||||||
"@mui/icons-material": "5.4.1",
|
"@mui/icons-material": "5.4.1",
|
||||||
"@mui/material": "5.4.1",
|
"@mui/material": "5.4.1",
|
||||||
"@mui/styled-engine": "5.4.1",
|
"@mui/styled-engine": "5.4.1",
|
||||||
|
"@mui/x-data-grid": "5.13.0",
|
||||||
"@react-jvectormap/core": "1.0.1",
|
"@react-jvectormap/core": "1.0.1",
|
||||||
"@react-jvectormap/world": "1.0.0",
|
"@react-jvectormap/world": "1.0.0",
|
||||||
"@testing-library/jest-dom": "5.16.2",
|
"@testing-library/jest-dom": "5.16.2",
|
||||||
|
@ -100,7 +100,7 @@ function EntityForm({ id }: Props): JSX.Element {
|
|||||||
})();
|
})();
|
||||||
};
|
};
|
||||||
|
|
||||||
const pageTitle =
|
const formTitle =
|
||||||
id != null ? `Edit ${tableMetaData?.label} (${id})` : `Create New ${tableMetaData?.label}`;
|
id != null ? `Edit ${tableMetaData?.label} (${id})` : `Create New ${tableMetaData?.label}`;
|
||||||
const formId =
|
const formId =
|
||||||
id != null ? `edit-${tableMetaData?.label}-form` : `create-${tableMetaData?.label}-form`;
|
id != null ? `edit-${tableMetaData?.label}-form` : `create-${tableMetaData?.label}-form`;
|
||||||
@ -108,7 +108,7 @@ function EntityForm({ id }: Props): JSX.Element {
|
|||||||
return (
|
return (
|
||||||
<Card id="edit-form-container" sx={{ overflow: "visible" }}>
|
<Card id="edit-form-container" sx={{ overflow: "visible" }}>
|
||||||
<MDBox p={3}>
|
<MDBox p={3}>
|
||||||
<MDTypography variant="h5">{pageTitle}</MDTypography>
|
<MDTypography variant="h5">{formTitle}</MDTypography>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
<MDBox pb={3} px={3}>
|
<MDBox pb={3} px={3}>
|
||||||
<Grid key="fields-grid" container spacing={3}>
|
<Grid key="fields-grid" container spacing={3}>
|
||||||
@ -120,24 +120,22 @@ function EntityForm({ id }: Props): JSX.Element {
|
|||||||
{({ values, errors, touched, isSubmitting }) => (
|
{({ values, errors, touched, isSubmitting }) => (
|
||||||
<Form id={formId} autoComplete="off">
|
<Form id={formId} autoComplete="off">
|
||||||
<MDBox p={3} width="100%">
|
<MDBox p={3} width="100%">
|
||||||
<MDBox>
|
{/***************************************************************************
|
||||||
{/***************************************************************************
|
** step content - e.g., the appropriate form or other screen for the step **
|
||||||
** step content - e.g., the appropriate form or other screen for the step **
|
***************************************************************************/}
|
||||||
***************************************************************************/}
|
{getDynamicStepContent({
|
||||||
{getDynamicStepContent({
|
values,
|
||||||
values,
|
touched,
|
||||||
touched,
|
formFields,
|
||||||
formFields,
|
errors,
|
||||||
errors,
|
})}
|
||||||
})}
|
<Grid key="buttonGrid" container spacing={3}>
|
||||||
<Grid key="buttonGrid" container spacing={3}>
|
<MDBox mt={5} ml="auto">
|
||||||
<MDBox mt={5} ml="auto">
|
<MDButton type="submit" variant="gradient" color="dark" size="small">
|
||||||
<MDButton type="submit" variant="gradient" color="dark" size="small">
|
save {tableMetaData?.label}
|
||||||
save {tableMetaData?.label}
|
</MDButton>
|
||||||
</MDButton>
|
</MDBox>
|
||||||
</MDBox>
|
</Grid>
|
||||||
</Grid>
|
|
||||||
</MDBox>
|
|
||||||
</MDBox>
|
</MDBox>
|
||||||
</Form>
|
</Form>
|
||||||
)}
|
)}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
*/
|
*/
|
||||||
/* eslint-disable react/no-unstable-nested-components */
|
/* eslint-disable react/no-unstable-nested-components */
|
||||||
|
|
||||||
import React, { useState } from "react";
|
import React, { useEffect, useReducer, useState } from "react";
|
||||||
|
|
||||||
// @mui material components
|
// @mui material components
|
||||||
import Card from "@mui/material/Card";
|
import Card from "@mui/material/Card";
|
||||||
@ -23,82 +23,108 @@ import Menu from "@mui/material/Menu";
|
|||||||
import MenuItem from "@mui/material/MenuItem";
|
import MenuItem from "@mui/material/MenuItem";
|
||||||
import Divider from "@mui/material/Divider";
|
import Divider from "@mui/material/Divider";
|
||||||
import Link from "@mui/material/Link";
|
import Link from "@mui/material/Link";
|
||||||
|
import { makeStyles } from "@mui/material";
|
||||||
// Material Dashboard 2 PRO React TS examples components
|
import { DataGrid, GridColDef, GridRowParams, GridRowsProp } from "@mui/x-data-grid";
|
||||||
import DashboardLayout from "examples/LayoutContainers/DashboardLayout";
|
|
||||||
import DashboardNavbar from "examples/Navbars/DashboardNavbar";
|
|
||||||
import DataTable from "examples/Tables/DataTable";
|
|
||||||
|
|
||||||
// QQQ
|
|
||||||
import { QProcessMetaData } from "@kingsrook/qqq-frontend-core/lib/model/metaData/QProcessMetaData";
|
|
||||||
import { QController } from "@kingsrook/qqq-frontend-core/lib/controllers/QController";
|
|
||||||
import { QTableMetaData } from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
|
||||||
import { useParams } from "react-router-dom";
|
|
||||||
import DynamicFormUtils from "qqq/components/QDynamicForm/utils/DynamicFormUtils";
|
|
||||||
|
|
||||||
// Material Dashboard 2 PRO React TS components
|
// Material Dashboard 2 PRO React TS components
|
||||||
|
import DashboardLayout from "examples/LayoutContainers/DashboardLayout";
|
||||||
|
import DashboardNavbar from "examples/Navbars/DashboardNavbar";
|
||||||
import MDBox from "components/MDBox";
|
import MDBox from "components/MDBox";
|
||||||
import MDTypography from "components/MDTypography";
|
import MDTypography from "components/MDTypography";
|
||||||
import MDButton from "components/MDButton";
|
import MDButton from "components/MDButton";
|
||||||
import Footer from "../../components/Footer";
|
|
||||||
import IdCell from "../../components/EntityForm/components/IdCell";
|
|
||||||
|
|
||||||
const qController = new QController("");
|
// QQQ
|
||||||
|
import { QProcessMetaData } from "@kingsrook/qqq-frontend-core/lib/model/metaData/QProcessMetaData";
|
||||||
|
import { QTableMetaData } from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import QClient from "qqq/utils/QClient";
|
||||||
|
import Footer from "../../components/Footer";
|
||||||
|
|
||||||
// Declaring props types for DefaultCell
|
// Declaring props types for DefaultCell
|
||||||
interface Props {
|
interface Props {
|
||||||
table?: QTableMetaData;
|
table?: QTableMetaData;
|
||||||
}
|
}
|
||||||
|
|
||||||
let dataTableData = {
|
|
||||||
columns: [] as any[],
|
|
||||||
rows: [] as any[],
|
|
||||||
};
|
|
||||||
|
|
||||||
function EntityList({ table }: Props): JSX.Element {
|
function EntityList({ table }: Props): JSX.Element {
|
||||||
const tableNameParam = useParams().tableName;
|
const tableNameParam = useParams().tableName;
|
||||||
const tableName = table === null ? tableNameParam : table.name;
|
const tableName = table === null ? tableNameParam : table.name;
|
||||||
|
|
||||||
|
const [tableState, setTableState] = useState("");
|
||||||
const [filtersMenu, setFiltersMenu] = useState(null);
|
const [filtersMenu, setFiltersMenu] = useState(null);
|
||||||
const [actionsMenu, setActionsMenu] = useState(null);
|
const [actionsMenu, setActionsMenu] = useState(null);
|
||||||
const [tableState, setTableState] = useState("");
|
|
||||||
const [tableProcesses, setTableProcesses] = useState([] as QProcessMetaData[]);
|
const [tableProcesses, setTableProcesses] = useState([] as QProcessMetaData[]);
|
||||||
console.log(tableState);
|
const [pageNumber, setPageNumber] = useState(1);
|
||||||
|
const [totalRecords, setTotalRecords] = useState(0);
|
||||||
|
const [rowsPerPage, setRowsPerPage] = useState(10);
|
||||||
|
const [columns, setColumns] = useState([] as GridColDef[]);
|
||||||
|
const [rows, setRows] = useState([] as GridRowsProp[]);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
||||||
|
|
||||||
const openActionsMenu = (event: any) => setActionsMenu(event.currentTarget);
|
const openActionsMenu = (event: any) => setActionsMenu(event.currentTarget);
|
||||||
const closeActionsMenu = () => setActionsMenu(null);
|
const closeActionsMenu = () => setActionsMenu(null);
|
||||||
const openFiltersMenu = (event: any) => setFiltersMenu(event.currentTarget);
|
const openFiltersMenu = (event: any) => setFiltersMenu(event.currentTarget);
|
||||||
const closeFiltersMenu = () => setFiltersMenu(null);
|
const closeFiltersMenu = () => setFiltersMenu(null);
|
||||||
|
|
||||||
if (tableState === "") {
|
console.log("DERPSSS");
|
||||||
|
console.log(`hoc${tableState}`);
|
||||||
|
|
||||||
|
const updateTable = () => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const tableMetaData = await qController.loadTableMetaData(tableName);
|
const tableMetaData = await QClient.loadTableMetaData(tableName);
|
||||||
const metaData = await qController.loadMetaData();
|
const count = await QClient.count(tableName);
|
||||||
const results = await qController.query(tableName, 250);
|
setTotalRecords(count);
|
||||||
dataTableData = {
|
|
||||||
columns: [],
|
const columns = [] as GridColDef[];
|
||||||
rows: [],
|
const results = await QClient.query(tableName, rowsPerPage, pageNumber * rowsPerPage);
|
||||||
};
|
|
||||||
|
const rows = [] as any[];
|
||||||
|
results.forEach((record) => {
|
||||||
|
rows.push(Object.fromEntries(record.values.entries()));
|
||||||
|
});
|
||||||
|
|
||||||
const sortedKeys = [...tableMetaData.fields.keys()].sort();
|
const sortedKeys = [...tableMetaData.fields.keys()].sort();
|
||||||
sortedKeys.forEach((key) => {
|
sortedKeys.forEach((key) => {
|
||||||
const field = tableMetaData.fields.get(key);
|
const field = tableMetaData.fields.get(key);
|
||||||
|
|
||||||
|
const column = {
|
||||||
|
field: field.name,
|
||||||
|
headerName: field.label,
|
||||||
|
width: 200,
|
||||||
|
};
|
||||||
|
|
||||||
if (key === tableMetaData.primaryKeyField) {
|
if (key === tableMetaData.primaryKeyField) {
|
||||||
dataTableData.columns.splice(0, 0, {
|
columns.splice(0, 0, column);
|
||||||
Header: field.label,
|
|
||||||
accessor: key,
|
|
||||||
Cell: ({ value }: any) => <IdCell id={value} />,
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
dataTableData.columns.push({
|
columns.push(column);
|
||||||
Header: field.label,
|
|
||||||
accessor: key,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
results.forEach((record) => {
|
setColumns(columns);
|
||||||
dataTableData.rows.push(Object.fromEntries(record.values.entries()));
|
setRows(rows);
|
||||||
});
|
setLoading(false);
|
||||||
|
forceUpdate();
|
||||||
|
})();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePageChange = (page: number) => {
|
||||||
|
setPageNumber(page);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRowsPerPageChange = (size: number) => {
|
||||||
|
setRowsPerPage(size);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRowClick = (params: GridRowParams) => {
|
||||||
|
document.location.href = `/${tableName}/${params.id}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (tableName !== tableState) {
|
||||||
|
(async () => {
|
||||||
|
console.log("DDDDDDDDDDDDOOOOOOOOOOO ITTTTTTTTTTTT");
|
||||||
|
setTableState(tableName);
|
||||||
|
const metaData = await QClient.loadMetaData();
|
||||||
|
|
||||||
const matchingProcesses: QProcessMetaData[] = [];
|
const matchingProcesses: QProcessMetaData[] = [];
|
||||||
const processKeys = [...metaData.processes.keys()];
|
const processKeys = [...metaData.processes.keys()];
|
||||||
@ -110,7 +136,8 @@ function EntityList({ table }: Props): JSX.Element {
|
|||||||
});
|
});
|
||||||
setTableProcesses(matchingProcesses);
|
setTableProcesses(matchingProcesses);
|
||||||
|
|
||||||
setTableState(tableName);
|
// reset rows to trigger rerender
|
||||||
|
setRows([]);
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,6 +179,10 @@ function EntityList({ table }: Props): JSX.Element {
|
|||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateTable();
|
||||||
|
}, [pageNumber, rowsPerPage, tableState]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardLayout>
|
<DashboardLayout>
|
||||||
<DashboardNavbar />
|
<DashboardNavbar />
|
||||||
@ -192,7 +223,26 @@ function EntityList({ table }: Props): JSX.Element {
|
|||||||
</MDBox>
|
</MDBox>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
<Card>
|
<Card>
|
||||||
<DataTable table={dataTableData} entriesPerPage={false} canSearch />
|
<MDBox height="100%">
|
||||||
|
<DataGrid
|
||||||
|
page={pageNumber}
|
||||||
|
checkboxSelection
|
||||||
|
disableSelectionOnClick
|
||||||
|
autoHeight
|
||||||
|
rows={rows}
|
||||||
|
columns={columns}
|
||||||
|
rowBuffer={10}
|
||||||
|
rowCount={totalRecords}
|
||||||
|
pageSize={rowsPerPage}
|
||||||
|
rowsPerPageOptions={[10, 25, 50]}
|
||||||
|
onPageSizeChange={handleRowsPerPageChange}
|
||||||
|
onPageChange={handlePageChange}
|
||||||
|
onRowClick={handleRowClick}
|
||||||
|
paginationMode="server"
|
||||||
|
density="compact"
|
||||||
|
loading={loading}
|
||||||
|
/>
|
||||||
|
</MDBox>
|
||||||
</Card>
|
</Card>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
<Footer />
|
<Footer />
|
||||||
@ -200,7 +250,6 @@ function EntityList({ table }: Props): JSX.Element {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Declaring default props for DefaultCell
|
|
||||||
EntityList.defaultProps = {
|
EntityList.defaultProps = {
|
||||||
table: null,
|
table: null,
|
||||||
};
|
};
|
||||||
|
57
src/qqq/utils/QClient.ts
Normal file
57
src/qqq/utils/QClient.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { QFieldMetaData } from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData";
|
||||||
|
import { QController } from "@kingsrook/qqq-frontend-core/lib/controllers/QController";
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** client wrapper of qqq backend
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
class QClient {
|
||||||
|
private static qController: QController;
|
||||||
|
|
||||||
|
private static getInstance() {
|
||||||
|
if (this.qController == null) {
|
||||||
|
this.qController = new QController("");
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.qController;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static loadTableMetaData(tableName: string) {
|
||||||
|
return this.getInstance().loadTableMetaData(tableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static loadMetaData() {
|
||||||
|
return this.getInstance().loadMetaData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static query(tableName: string, limit: number, skip: number) {
|
||||||
|
return this.getInstance().query(tableName, limit, skip);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static count(tableName: string) {
|
||||||
|
return this.getInstance().count(tableName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default QClient;
|
Reference in New Issue
Block a user