mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-17 21:00:45 +00:00
Update to get back processes in metaData; add actions menu to entity-list; fix routing directly to entity lists
This commit is contained in:
60
src/App.tsx
60
src/App.tsx
@ -13,14 +13,7 @@ Coded by www.creative-tim.com
|
|||||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {
|
import React, { useState, useEffect, JSXElementConstructor, Key, ReactElement } from "react";
|
||||||
useState,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
JSXElementConstructor,
|
|
||||||
Key,
|
|
||||||
ReactElement,
|
|
||||||
} from "react";
|
|
||||||
|
|
||||||
// react-router components
|
// react-router components
|
||||||
import { Routes, Route, Navigate, useLocation } from "react-router-dom";
|
import { Routes, Route, Navigate, useLocation } from "react-router-dom";
|
||||||
@ -39,15 +32,12 @@ import Configurator from "examples/Configurator";
|
|||||||
|
|
||||||
// Material Dashboard 2 PRO React TS themes
|
// Material Dashboard 2 PRO React TS themes
|
||||||
import theme from "assets/theme";
|
import theme from "assets/theme";
|
||||||
import themeRTL from "assets/theme/theme-rtl";
|
|
||||||
|
|
||||||
// Material Dashboard 2 PRO React TS Dark Mode themes
|
// Material Dashboard 2 PRO React TS Dark Mode themes
|
||||||
import themeDark from "assets/theme-dark";
|
import themeDark from "assets/theme-dark";
|
||||||
import themeDarkRTL from "assets/theme-dark/theme-rtl";
|
|
||||||
|
|
||||||
// RTL plugins
|
// RTL plugins
|
||||||
import rtlPlugin from "stylis-plugin-rtl";
|
import rtlPlugin from "stylis-plugin-rtl";
|
||||||
import { CacheProvider } from "@emotion/react";
|
|
||||||
import createCache from "@emotion/cache";
|
import createCache from "@emotion/cache";
|
||||||
|
|
||||||
// Material Dashboard 2 PRO React TS routes
|
// Material Dashboard 2 PRO React TS routes
|
||||||
@ -60,8 +50,10 @@ import { useMaterialUIController, setMiniSidenav, setOpenConfigurator } from "co
|
|||||||
import brandWhite from "assets/images/logo-ct.png";
|
import brandWhite from "assets/images/logo-ct.png";
|
||||||
import brandDark from "assets/images/logo-ct-dark.png";
|
import brandDark from "assets/images/logo-ct-dark.png";
|
||||||
import EntityCreate from "./qqq/pages/entity-create";
|
import EntityCreate from "./qqq/pages/entity-create";
|
||||||
|
import EntityList from "./qqq/pages/entity-list";
|
||||||
import EntityView from "./qqq/pages/entity-view";
|
import EntityView from "./qqq/pages/entity-view";
|
||||||
import EntityEdit from "./qqq/pages/entity-edit";
|
import EntityEdit from "./qqq/pages/entity-edit";
|
||||||
|
import ProcessRun from "./qqq/pages/process-run";
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const [controller, dispatch] = useMaterialUIController();
|
const [controller, dispatch] = useMaterialUIController();
|
||||||
@ -76,20 +68,8 @@ export default function App() {
|
|||||||
darkMode,
|
darkMode,
|
||||||
} = controller;
|
} = controller;
|
||||||
const [onMouseEnter, setOnMouseEnter] = useState(false);
|
const [onMouseEnter, setOnMouseEnter] = useState(false);
|
||||||
const [rtlCache, setRtlCache] = useState(null);
|
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
|
|
||||||
// Cache for the rtl
|
|
||||||
useMemo(() => {
|
|
||||||
const pluginRtl: any = rtlPlugin;
|
|
||||||
const cacheRtl = createCache({
|
|
||||||
key: "rtl",
|
|
||||||
stylisPlugins: [pluginRtl],
|
|
||||||
});
|
|
||||||
|
|
||||||
setRtlCache(cacheRtl);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Open sidenav when mouse enter on mini sidenav
|
// Open sidenav when mouse enter on mini sidenav
|
||||||
const handleOnMouseEnter = () => {
|
const handleOnMouseEnter = () => {
|
||||||
if (miniSidenav && !onMouseEnter) {
|
if (miniSidenav && !onMouseEnter) {
|
||||||
@ -164,39 +144,13 @@ export default function App() {
|
|||||||
</MDBox>
|
</MDBox>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const entityListElement = <EntityList />;
|
||||||
const entityCreateElement = <EntityCreate />;
|
const entityCreateElement = <EntityCreate />;
|
||||||
const entityViewElement = <EntityView />;
|
const entityViewElement = <EntityView />;
|
||||||
const entityEditElement = <EntityEdit />;
|
const entityEditElement = <EntityEdit />;
|
||||||
|
const processRunElement = <ProcessRun />;
|
||||||
|
|
||||||
return direction === "rtl" ? (
|
return (
|
||||||
<CacheProvider value={rtlCache}>
|
|
||||||
<ThemeProvider theme={darkMode ? themeDarkRTL : themeRTL}>
|
|
||||||
<CssBaseline />
|
|
||||||
{layout === "dashboard" && (
|
|
||||||
<>
|
|
||||||
<Sidenav
|
|
||||||
color={sidenavColor}
|
|
||||||
brand={(transparentSidenav && !darkMode) || whiteSidenav ? brandDark : brandWhite}
|
|
||||||
brandName="Material Dashboard PRO"
|
|
||||||
routes={routes}
|
|
||||||
onMouseEnter={handleOnMouseEnter}
|
|
||||||
onMouseLeave={handleOnMouseLeave}
|
|
||||||
/>
|
|
||||||
<Configurator />
|
|
||||||
{configsButton}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{layout === "vr" && <Configurator />}
|
|
||||||
<Routes>
|
|
||||||
<Route path="*" element={<Navigate to="/dashboards/analytics" />} />
|
|
||||||
<Route path="/:tableName/create" element={entityCreateElement} key="entity-create" />;
|
|
||||||
<Route path="/:tableName/view/:id" element={entityViewElement} key="entity-view" />;
|
|
||||||
<Route path="/:tableName/edit/:id" element={entityEditElement} key="entity-edit" />;
|
|
||||||
{getRoutes(routes)}
|
|
||||||
</Routes>
|
|
||||||
</ThemeProvider>
|
|
||||||
</CacheProvider>
|
|
||||||
) : (
|
|
||||||
<ThemeProvider theme={darkMode ? themeDark : theme}>
|
<ThemeProvider theme={darkMode ? themeDark : theme}>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
{layout === "dashboard" && (
|
{layout === "dashboard" && (
|
||||||
@ -216,9 +170,11 @@ export default function App() {
|
|||||||
{layout === "vr" && <Configurator />}
|
{layout === "vr" && <Configurator />}
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="*" element={<Navigate to="/dashboards/analytics" />} />
|
<Route path="*" element={<Navigate to="/dashboards/analytics" />} />
|
||||||
|
<Route path="/:tableName/list" element={entityListElement} key="entity-list" />;
|
||||||
<Route path="/:tableName/create" element={entityCreateElement} key="entity-create" />;
|
<Route path="/:tableName/create" element={entityCreateElement} key="entity-create" />;
|
||||||
<Route path="/:tableName/view/:id" element={entityViewElement} key="entity-view" />;
|
<Route path="/:tableName/view/:id" element={entityViewElement} key="entity-view" />;
|
||||||
<Route path="/:tableName/edit/:id" element={entityEditElement} key="entity-edit" />;
|
<Route path="/:tableName/edit/:id" element={entityEditElement} key="entity-edit" />;
|
||||||
|
<Route path="/processes/:processName" element={processRunElement} key="process-run" />;
|
||||||
{getRoutes(routes)}
|
{getRoutes(routes)}
|
||||||
</Routes>
|
</Routes>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
|
@ -34,18 +34,20 @@ import DashboardNavbar from "examples/Navbars/DashboardNavbar";
|
|||||||
import DataTable from "examples/Tables/DataTable";
|
import DataTable from "examples/Tables/DataTable";
|
||||||
|
|
||||||
// Data
|
// Data
|
||||||
|
import { QProcessMetaData } from "@kingsrook/qqq-frontend-core/lib/model/metaData/QProcessMetaData";
|
||||||
import { QController } from "@kingsrook/qqq-frontend-core/lib/controllers/QController";
|
import { QController } from "@kingsrook/qqq-frontend-core/lib/controllers/QController";
|
||||||
import Link from "@mui/material/Link";
|
import Link from "@mui/material/Link";
|
||||||
import { QTableMetaData } from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
import { QTableMetaData } from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
import IdCell from "./components/IdCell";
|
import IdCell from "./components/IdCell";
|
||||||
import Footer from "../../components/Footer";
|
import Footer from "../../components/Footer";
|
||||||
|
import EntityForm from "../../components/EntityForm";
|
||||||
|
|
||||||
const qController = new QController("");
|
const qController = new QController("");
|
||||||
console.log(qController);
|
|
||||||
|
|
||||||
// Declaring props types for DefaultCell
|
// Declaring props types for DefaultCell
|
||||||
interface Props {
|
interface Props {
|
||||||
table: QTableMetaData;
|
table?: QTableMetaData;
|
||||||
}
|
}
|
||||||
|
|
||||||
let dataTableData = {
|
let dataTableData = {
|
||||||
@ -54,62 +56,97 @@ let dataTableData = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function EntityList({ table }: Props): JSX.Element {
|
function EntityList({ table }: Props): JSX.Element {
|
||||||
const [menu, setMenu] = useState(null);
|
const tableNameParam = useParams().tableName;
|
||||||
|
const tableName = table === null ? tableNameParam : table.name;
|
||||||
|
const [filtersMenu, setFiltersMenu] = useState(null);
|
||||||
|
const [actionsMenu, setActionsMenu] = useState(null);
|
||||||
const [tableState, setTableState] = useState("");
|
const [tableState, setTableState] = useState("");
|
||||||
|
const [tableProcesses, setTableProcesses] = useState([] as QProcessMetaData[]);
|
||||||
console.log(tableState);
|
console.log(tableState);
|
||||||
|
|
||||||
const newEntity = (event: any) => setMenu(event.currentTarget);
|
const openActionsMenu = (event: any) => setActionsMenu(event.currentTarget);
|
||||||
const openMenu = (event: any) => setMenu(event.currentTarget);
|
const closeActionsMenu = () => setActionsMenu(null);
|
||||||
const closeMenu = () => setMenu(null);
|
const openFiltersMenu = (event: any) => setFiltersMenu(event.currentTarget);
|
||||||
|
const closeFiltersMenu = () => setFiltersMenu(null);
|
||||||
|
|
||||||
const createPath = `/${table.name}/create`;
|
const createPath = `/${tableName}/create`;
|
||||||
|
|
||||||
(async () => {
|
if (tableState === "") {
|
||||||
const tableMetaData = await qController.loadTableMetaData(table.name);
|
(async () => {
|
||||||
const results = await qController.query(table.name, 250);
|
const tableMetaData = await qController.loadTableMetaData(tableName);
|
||||||
dataTableData = {
|
const metaData = await qController.loadMetaData();
|
||||||
columns: [],
|
const results = await qController.query(tableName, 250);
|
||||||
rows: [],
|
dataTableData = {
|
||||||
};
|
columns: [],
|
||||||
|
rows: [],
|
||||||
|
};
|
||||||
|
|
||||||
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);
|
||||||
if (key === tableMetaData.primaryKeyField) {
|
if (key === tableMetaData.primaryKeyField) {
|
||||||
dataTableData.columns.splice(0, 0, {
|
dataTableData.columns.splice(0, 0, {
|
||||||
Header: field.label,
|
Header: field.label,
|
||||||
accessor: key,
|
accessor: key,
|
||||||
Cell: ({ value }: any) => <IdCell id={value} />,
|
Cell: ({ value }: any) => <IdCell id={value} />,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
dataTableData.columns.push({
|
dataTableData.columns.push({
|
||||||
Header: field.label,
|
Header: field.label,
|
||||||
accessor: key,
|
accessor: key,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
results.forEach((record) => {
|
results.forEach((record) => {
|
||||||
dataTableData.rows.push(Object.fromEntries(record.values.entries()));
|
dataTableData.rows.push(Object.fromEntries(record.values.entries()));
|
||||||
});
|
});
|
||||||
|
|
||||||
setTableState(table.name);
|
const matchingProcesses: QProcessMetaData[] = [];
|
||||||
})();
|
const processKeys = [...metaData.processes.keys()];
|
||||||
|
processKeys.forEach((key) => {
|
||||||
|
const process = metaData.processes.get(key);
|
||||||
|
if (process.tableName === tableName) {
|
||||||
|
matchingProcesses.push(process);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setTableProcesses(matchingProcesses);
|
||||||
|
|
||||||
const renderMenu = (
|
setTableState(tableName);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderActionsMenu = (
|
||||||
<Menu
|
<Menu
|
||||||
anchorEl={menu}
|
anchorEl={actionsMenu}
|
||||||
anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
|
anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
|
||||||
transformOrigin={{ vertical: "top", horizontal: "left" }}
|
transformOrigin={{ vertical: "top", horizontal: "left" }}
|
||||||
open={Boolean(menu)}
|
open={Boolean(actionsMenu)}
|
||||||
onClose={closeMenu}
|
onClose={closeActionsMenu}
|
||||||
keepMounted
|
keepMounted
|
||||||
>
|
>
|
||||||
<MenuItem onClick={closeMenu}>Status: Paid</MenuItem>
|
{tableProcesses.map((process) => (
|
||||||
<MenuItem onClick={closeMenu}>Status: Refunded</MenuItem>
|
<MenuItem key={process.name}>
|
||||||
<MenuItem onClick={closeMenu}>Status: Canceled</MenuItem>
|
<Link href={`/processes/${process.name}`}>{process.label}</Link>
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderFiltersMenu = (
|
||||||
|
<Menu
|
||||||
|
anchorEl={filtersMenu}
|
||||||
|
anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
|
||||||
|
transformOrigin={{ vertical: "top", horizontal: "left" }}
|
||||||
|
open={Boolean(filtersMenu)}
|
||||||
|
onClose={closeFiltersMenu}
|
||||||
|
keepMounted
|
||||||
|
>
|
||||||
|
<MenuItem onClick={closeFiltersMenu}>Status: Paid</MenuItem>
|
||||||
|
<MenuItem onClick={closeFiltersMenu}>Status: Refunded</MenuItem>
|
||||||
|
<MenuItem onClick={closeFiltersMenu}>Status: Canceled</MenuItem>
|
||||||
<Divider sx={{ margin: "0.5rem 0" }} />
|
<Divider sx={{ margin: "0.5rem 0" }} />
|
||||||
<MenuItem onClick={closeMenu}>
|
<MenuItem onClick={closeFiltersMenu}>
|
||||||
<MDTypography variant="button" color="error" fontWeight="regular">
|
<MDTypography variant="button" color="error" fontWeight="regular">
|
||||||
Remove Filter
|
Remove Filter
|
||||||
</MDTypography>
|
</MDTypography>
|
||||||
@ -122,15 +159,32 @@ function EntityList({ table }: Props): JSX.Element {
|
|||||||
<DashboardNavbar />
|
<DashboardNavbar />
|
||||||
<MDBox my={3}>
|
<MDBox my={3}>
|
||||||
<MDBox display="flex" justifyContent="space-between" alignItems="flex-start" mb={2}>
|
<MDBox display="flex" justifyContent="space-between" alignItems="flex-start" mb={2}>
|
||||||
<MDButton variant="gradient" color="info" onClick={newEntity}>
|
<MDButton variant="gradient" color="info">
|
||||||
<Link href={createPath}>new {table.label}</Link>
|
<Link href={createPath}>new {tableName}</Link>
|
||||||
</MDButton>
|
</MDButton>
|
||||||
<MDBox display="flex">
|
<MDBox display="flex">
|
||||||
<MDButton variant={menu ? "contained" : "outlined"} color="dark" onClick={openMenu}>
|
{tableProcesses.length > 0 && (
|
||||||
filters
|
<MDButton
|
||||||
<Icon>keyboard_arrow_down</Icon>
|
variant={actionsMenu ? "contained" : "outlined"}
|
||||||
</MDButton>
|
color="dark"
|
||||||
{renderMenu}
|
onClick={openActionsMenu}
|
||||||
|
>
|
||||||
|
actions
|
||||||
|
<Icon>keyboard_arrow_down</Icon>
|
||||||
|
</MDButton>
|
||||||
|
)}
|
||||||
|
{renderActionsMenu}
|
||||||
|
<MDBox ml={1}>
|
||||||
|
<MDButton
|
||||||
|
variant={filtersMenu ? "contained" : "outlined"}
|
||||||
|
color="dark"
|
||||||
|
onClick={openFiltersMenu}
|
||||||
|
>
|
||||||
|
filters
|
||||||
|
<Icon>keyboard_arrow_down</Icon>
|
||||||
|
</MDButton>
|
||||||
|
{renderFiltersMenu}
|
||||||
|
</MDBox>
|
||||||
<MDBox ml={1}>
|
<MDBox ml={1}>
|
||||||
<MDButton variant="outlined" color="dark">
|
<MDButton variant="outlined" color="dark">
|
||||||
<Icon>description</Icon>
|
<Icon>description</Icon>
|
||||||
@ -148,4 +202,9 @@ function EntityList({ table }: Props): JSX.Element {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Declaring default props for DefaultCell
|
||||||
|
EntityList.defaultProps = {
|
||||||
|
table: null,
|
||||||
|
};
|
||||||
|
|
||||||
export default EntityList;
|
export default EntityList;
|
||||||
|
@ -391,13 +391,12 @@ console.log(qController);
|
|||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const metaData = await qController.loadMetaData();
|
const metaData = await qController.loadMetaData();
|
||||||
console.log(`metaData: ${metaData}`);
|
|
||||||
|
|
||||||
// get the keys sorted
|
// get the keys sorted
|
||||||
const keys = [...metaData.keys()].sort();
|
const keys = [...metaData.tables.keys()].sort();
|
||||||
const tableList = [] as any[];
|
const tableList = [] as any[];
|
||||||
keys.forEach((key) => {
|
keys.forEach((key) => {
|
||||||
const table = metaData.get(key);
|
const table = metaData.tables.get(key);
|
||||||
tableList.push({
|
tableList.push({
|
||||||
name: table.label,
|
name: table.label,
|
||||||
key: table.name,
|
key: table.name,
|
||||||
|
@ -554,8 +554,8 @@ console.log(qController);
|
|||||||
console.log(`metaData: ${metaData}`);
|
console.log(`metaData: ${metaData}`);
|
||||||
|
|
||||||
const tableList = [] as any[];
|
const tableList = [] as any[];
|
||||||
metaData.forEach((value, key) => {
|
metaData.tables.forEach((value, key) => {
|
||||||
const table = metaData.get(key);
|
const table = metaData.tables.get(key);
|
||||||
console.log(`TABLE: ${table}`);
|
console.log(`TABLE: ${table}`);
|
||||||
tableList.push({
|
tableList.push({
|
||||||
name: table.label,
|
name: table.label,
|
||||||
|
Reference in New Issue
Block a user