QQQ-30: fixes from yesterday's review

This commit is contained in:
Tim Chamberlain
2022-07-13 12:39:59 -05:00
parent 61986b435b
commit cca8a9f718
12 changed files with 972 additions and 823 deletions

1369
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,7 @@
"@fullcalendar/interaction": "5.10.0", "@fullcalendar/interaction": "5.10.0",
"@fullcalendar/react": "5.10.0", "@fullcalendar/react": "5.10.0",
"@fullcalendar/timegrid": "5.10.0", "@fullcalendar/timegrid": "5.10.0",
"@kingsrook/qqq-frontend-core": "1.0.2", "@kingsrook/qqq-frontend-core": "1.0.3",
"@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",
@ -45,13 +45,14 @@
"react-table": "7.7.0", "react-table": "7.7.0",
"stylis": "4.0.13", "stylis": "4.0.13",
"stylis-plugin-rtl": "2.1.1", "stylis-plugin-rtl": "2.1.1",
"ts-md5": "1.2.11",
"uuid": "8.3.2", "uuid": "8.3.2",
"web-vitals": "2.1.4", "web-vitals": "2.1.4",
"yup": "0.32.11" "yup": "0.32.11"
}, },
"scripts": { "scripts": {
"build": "react-scripts build", "build": "react-scripts build",
"clean": "rm -rf node_modules package-lock-bak.json", "clean": "rm -rf node_modules package-lock.json",
"eject": "react-scripts eject", "eject": "react-scripts eject",
"geff-ham": "rm -rf node_modules/ && rm -rf package-lock-bak.json && npm install --legacy-peer-deps && npm start", "geff-ham": "rm -rf node_modules/ && rm -rf package-lock-bak.json && npm install --legacy-peer-deps && npm start",
"install-legacy-peer-deps": "npm install --legacy-peer-deps", "install-legacy-peer-deps": "npm install --legacy-peer-deps",

View File

@ -1,17 +1,3 @@
/**
=========================================================
* Material Dashboard 2 PRO React TS - v1.0.0
=========================================================
* Product Page: https://www.creative-tim.com/product/material-dashboard-2-pro-react-ts
* Copyright 2022 Creative Tim (https://www.creative-tim.com)
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.
*/
import React, { import React, {
useState, useState,
@ -48,6 +34,7 @@ import themeDark from "assets/theme-dark";
import { useMaterialUIController, setMiniSidenav, setOpenConfigurator } from "context"; import { useMaterialUIController, setMiniSidenav, setOpenConfigurator } from "context";
// Images // Images
import nfLogo from "assets/images/nutrifresh_one_icon_white.png";
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";
@ -230,11 +217,11 @@ export default function App()
const getRoutes = (allRoutes: any[]): any => allRoutes.map( const getRoutes = (allRoutes: any[]): any => allRoutes.map(
(route: { (route: {
collapse: any; collapse: any;
route: string; route: string;
component: ReactElement<any, string | JSXElementConstructor<any>>; component: ReactElement<any, string | JSXElementConstructor<any>>;
key: Key; key: Key;
}) => }) =>
{ {
if (route.collapse) if (route.collapse)
{ {
@ -287,8 +274,8 @@ export default function App()
<> <>
<Sidenav <Sidenav
color={sidenavColor} color={sidenavColor}
brand={(transparentSidenav && !darkMode) || whiteSidenav ? brandDark : brandWhite} brand={nfLogo}
brandName="Material Dashboard PRO" brandName="Nutrifresh One"
routes={routes} routes={routes}
onMouseEnter={handleOnMouseEnter} onMouseEnter={handleOnMouseEnter}
onMouseLeave={handleOnMouseLeave} onMouseLeave={handleOnMouseLeave}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -90,7 +90,7 @@ function Breadcrumbs({ icon, title, route, light }: Props): JSX.Element {
color={light ? "white" : "dark"} color={light ? "white" : "dark"}
noWrap noWrap
> >
{title.replace("-", " ")} {title.replace("-", " ").replace("_", " ")}
</MDTypography> </MDTypography>
</MDBox> </MDBox>
); );

View File

@ -13,7 +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 { useEffect, useState, ReactNode } from "react"; import { useEffect, useState, ReactNode, useReducer } from 'react';
// react-router-dom components // react-router-dom components
import { useLocation, NavLink } from "react-router-dom"; import { useLocation, NavLink } from "react-router-dom";
@ -77,6 +77,7 @@ function Sidenav({ color, brand, brandName, routes, ...rest }: Props): JSX.Eleme
const items = pathname.split("/").slice(1); const items = pathname.split("/").slice(1);
const itemParentName = items[1]; const itemParentName = items[1];
const itemName = items[items.length - 1]; const itemName = items[items.length - 1];
const [, forceUpdate] = useReducer((x) => x + 1, 0);
let textColor: let textColor:
| "primary" | "primary"
@ -112,10 +113,13 @@ function Sidenav({ color, brand, brandName, routes, ...rest }: Props): JSX.Eleme
setWhiteSidenav(dispatch, window.innerWidth < 1200 ? false : whiteSidenav); setWhiteSidenav(dispatch, window.innerWidth < 1200 ? false : whiteSidenav);
} }
/** /**
The event listener that's calling the handleMiniSidenav function when resizing the window. The event listener that's calling the handleMiniSidenav function when resizing the window.
*/ */
window.addEventListener("resize", handleMiniSidenav); window.addEventListener("resize", handleMiniSidenav);
window.onload = () => {
forceUpdate();
};
// Call the handleMiniSidenav function to set the state with the initial value. // Call the handleMiniSidenav function to set the state with the initial value.
handleMiniSidenav(); handleMiniSidenav();

View File

@ -1,5 +1,5 @@
// react components // react components
import { useParams } from "react-router-dom"; import { useParams, useSearchParams } from "react-router-dom";
import React, { useReducer, useState } from "react"; import React, { useReducer, useState } from "react";
// misc components // misc components
@ -18,13 +18,13 @@ import Grid from "@mui/material/Grid";
import { Alert } from "@mui/material"; import { Alert } from "@mui/material";
// Material Dashboard 2 PRO React TS components // Material Dashboard 2 PRO React TS components
import MDAlert from "components/MDAlert";
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";
// Declaring props types for EntityForm // Declaring props types for EntityForm
interface Props { interface Props
{
id?: string; id?: string;
} }
@ -36,6 +36,7 @@ function EntityForm({ id }: Props): JSX.Element
const [validations, setValidations] = useState({}); const [validations, setValidations] = useState({});
const [initialValues, setInitialValues] = useState({} as { [key: string]: string }); const [initialValues, setInitialValues] = useState({} as { [key: string]: string });
const [formFields, setFormFields] = useState({}); const [formFields, setFormFields] = useState({});
const [alertContent, setAlertContent] = useState(""); const [alertContent, setAlertContent] = useState("");
const [asyncLoadInited, setAsyncLoadInited] = useState(false); const [asyncLoadInited, setAsyncLoadInited] = useState(false);
@ -46,7 +47,10 @@ function EntityForm({ id }: Props): JSX.Element
function getDynamicStepContent(formData: any): JSX.Element function getDynamicStepContent(formData: any): JSX.Element
{ {
const { const {
formFields, values, errors, touched, formFields,
values,
errors,
touched,
} = formData; } = formData;
if (!Object.keys(formFields).length) if (!Object.keys(formFields).length)
@ -85,10 +89,14 @@ function EntityForm({ id }: Props): JSX.Element
setFormValues(formValues); setFormValues(formValues);
} }
const { dynamicFormFields, formValidations } = DynamicFormUtils.getFormData(fieldArray); const {
dynamicFormFields,
formValidations,
} = DynamicFormUtils.getFormData(fieldArray);
setInitialValues(initialValues); setInitialValues(initialValues);
setFormFields(dynamicFormFields); setFormFields(dynamicFormFields);
setValidations(Yup.object().shape(formValidations)); setValidations(Yup.object()
.shape(formValidations));
forceUpdate(); forceUpdate();
})(); })();
@ -107,7 +115,7 @@ function EntityForm({ id }: Props): JSX.Element
{ {
window.location.href = `/${tableName}/${record.values.get( window.location.href = `/${tableName}/${record.values.get(
tableMetaData.primaryKeyField, tableMetaData.primaryKeyField,
)}`; )}?updateSuccess=true`;
}) })
.catch((error) => .catch((error) =>
{ {
@ -122,7 +130,7 @@ function EntityForm({ id }: Props): JSX.Element
{ {
window.location.href = `/${tableName}/${record.values.get( window.location.href = `/${tableName}/${record.values.get(
tableMetaData.primaryKeyField, tableMetaData.primaryKeyField,
)}`; )}?createSuccess=true`;
}) })
.catch((error) => .catch((error) =>
{ {
@ -158,7 +166,10 @@ function EntityForm({ id }: Props): JSX.Element
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
{({ {({
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%">

View File

@ -73,13 +73,7 @@ function Footer({ company, links }: Props): JSX.Element
&copy; &copy;
{" "} {" "}
{new Date().getFullYear()} {new Date().getFullYear()}
, made with ,
<MDBox fontSize={size.md} color="text" mb={-0.5} mx={0.25}>
<Icon color="inherit" fontSize="inherit">
favorite
</Icon>
</MDBox>
by
<Link href={href} target="_blank"> <Link href={href} target="_blank">
<MDTypography variant="button" fontWeight="medium"> <MDTypography variant="button" fontWeight="medium">
&nbsp; &nbsp;
@ -87,7 +81,6 @@ function Footer({ company, links }: Props): JSX.Element
&nbsp; &nbsp;
</MDTypography> </MDTypography>
</Link> </Link>
for a better web.
</MDBox> </MDBox>
<MDBox <MDBox
component="ul" component="ul"
@ -114,8 +107,8 @@ function Footer({ company, links }: Props): JSX.Element
// Declaring default props for Footer // Declaring default props for Footer
Footer.defaultProps = { Footer.defaultProps = {
company: { href: "https://www.kingsrook.com/", name: "Kingsrook" }, company: { href: "https://www.nutrifreshservices.com/", name: "Nutrifresh Services" },
links: [{ href: "https://www.kingsrook.com/", name: "Kingsrook" }], links: [],
}; };
export default Footer; export default Footer;

View File

@ -2,29 +2,25 @@
========================================================= =========================================================
* Material Dashboard 2 PRO React TS - v1.0.0 * Material Dashboard 2 PRO React TS - v1.0.0
========================================================= =========================================================
* Product Page: https://www.creative-tim.com/product/material-dashboard-2-pro-react-ts * Product Page: https://www.creative-tim.com/product/material-dashboard-2-pro-react-ts
* Copyright 2022 Creative Tim (https://www.creative-tim.com) * Copyright 2022 Creative Tim (https://www.creative-tim.com)
Coded by www.creative-tim.com Coded by www.creative-tim.com
========================================================= =========================================================
* The above copyright notice and this permission notice shall be included in all copies or
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. * substantial portions of the Software.
*/ */
/* eslint-disable react/no-unstable-nested-components */ /* eslint-disable react/no-unstable-nested-components */
import React, { useEffect, useReducer, useState } from "react"; import React, { useEffect, useReducer, useState } from "react";
import { useParams } from "react-router-dom"; import { useParams, useSearchParams } from "react-router-dom";
// @mui material components // @mui material components
import Card from "@mui/material/Card"; import Card from "@mui/material/Card";
import Icon from "@mui/material/Icon"; import Icon from "@mui/material/Icon";
import Menu from "@mui/material/Menu"; 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 Link from "@mui/material/Link"; import Link from "@mui/material/Link";
import { makeStyles, Alert } from "@mui/material"; import { Alert } from "@mui/material";
import { import {
DataGrid, DataGrid,
GridCallbackDetails, GridCallbackDetails,
@ -37,7 +33,11 @@ import {
GridSelectionModel, GridSelectionModel,
GridSortItem, GridSortItem,
GridSortModel, GridSortModel,
GridToolbar, GridToolbarColumnsButton, GridToolbarContainer, GridToolbarDensitySelector, GridToolbarExport, GridToolbarFilterButton, GridToolbarColumnsButton,
GridToolbarContainer,
GridToolbarDensitySelector,
GridToolbarExport,
GridToolbarFilterButton,
} from "@mui/x-data-grid"; } from "@mui/x-data-grid";
// Material Dashboard 2 PRO React TS components // Material Dashboard 2 PRO React TS components
@ -55,16 +55,18 @@ import { QFilterCriteria } from "@kingsrook/qqq-frontend-core/lib/model/query/QF
import { QCriteriaOperator } from "@kingsrook/qqq-frontend-core/lib/model/query/QCriteriaOperator"; import { QCriteriaOperator } from "@kingsrook/qqq-frontend-core/lib/model/query/QCriteriaOperator";
import { QFieldType } from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType"; import { QFieldType } from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType";
import QClient from "qqq/utils/QClient"; import QClient from "qqq/utils/QClient";
import MDAlert from "components/MDAlert";
import Footer from "../../components/Footer"; import Footer from "../../components/Footer";
import QProcessUtils from "../../utils/QProcessUtils"; import QProcessUtils from "../../utils/QProcessUtils";
import "./styles.css"; import "./styles.css";
const COLUMN_VISIBILITY_LOCAL_STORAGE_KEY = "qqq.columnVisibility"; const COLUMN_VISIBILITY_LOCAL_STORAGE_KEY_ROOT = "qqq.columnVisibility";
const COLUMN_SORT_LOCAL_STORAGE_KEY = "qqq.columnSort"; const COLUMN_SORT_LOCAL_STORAGE_KEY_ROOT = "qqq.columnSort";
// Declaring props types for DefaultCell // Declaring props types for DefaultCell
interface Props { interface Props
{
table?: QTableMetaData; table?: QTableMetaData;
} }
@ -73,6 +75,7 @@ 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 [buttonText, setButtonText] = useState("");
const [tableState, setTableState] = useState(""); const [tableState, setTableState] = useState("");
const [filtersMenu, setFiltersMenu] = useState(null); const [filtersMenu, setFiltersMenu] = useState(null);
const [actionsMenu, setActionsMenu] = useState(null); const [actionsMenu, setActionsMenu] = useState(null);
@ -89,7 +92,9 @@ function EntityList({ table }: Props): JSX.Element
const [sortModel, setSortModel] = useState([] as GridSortItem[]); const [sortModel, setSortModel] = useState([] as GridSortItem[]);
const [filterModel, setFilterModel] = useState(null as GridFilterModel); const [filterModel, setFilterModel] = useState(null as GridFilterModel);
const [alertContent, setAlertContent] = useState(""); const [alertContent, setAlertContent] = useState("");
const [tableLabel, setTableLabel] = useState("");
const [searchParams, setSearchParams] = useSearchParams();
const [, forceUpdate] = useReducer((x) => x + 1, 0); const [, forceUpdate] = useReducer((x) => x + 1, 0);
const openActionsMenu = (event: any) => setActionsMenu(event.currentTarget); const openActionsMenu = (event: any) => setActionsMenu(event.currentTarget);
@ -97,38 +102,41 @@ function EntityList({ table }: Props): JSX.Element
const openFiltersMenu = (event: any) => setFiltersMenu(event.currentTarget); const openFiltersMenu = (event: any) => setFiltersMenu(event.currentTarget);
const closeFiltersMenu = () => setFiltersMenu(null); const closeFiltersMenu = () => setFiltersMenu(null);
let columnVisibilityLocalStorageKey = "";
let columnSortLocalStorageKey = "";
const translateCriteriaOperator = (operator: string) => const translateCriteriaOperator = (operator: string) =>
{ {
switch (operator) switch (operator)
{ {
case "contains": case "contains":
return QCriteriaOperator.CONTAINS; return QCriteriaOperator.CONTAINS;
case "starts with": case "startsWith":
return QCriteriaOperator.STARTS_WITH;
case "ends with":
return QCriteriaOperator.STARTS_WITH; return QCriteriaOperator.STARTS_WITH;
case "endsWith":
return QCriteriaOperator.ENDS_WITH;
case "is": case "is":
case "equals": case "equals":
case "=": case "=":
return QCriteriaOperator.EQUALS; return QCriteriaOperator.EQUALS;
case "is not": case "isNot":
case "!=": case "!=":
return QCriteriaOperator.NOT_EQUALS; return QCriteriaOperator.NOT_EQUALS;
case "is after": case "after":
case ">": case ">":
return QCriteriaOperator.GREATER_THAN; return QCriteriaOperator.GREATER_THAN;
case "is on or after": case "onOrAfter":
case ">=": case ">=":
return QCriteriaOperator.GREATER_THAN_OR_EQUALS; return QCriteriaOperator.GREATER_THAN_OR_EQUALS;
case "is before": case "before":
case "<": case "<":
return QCriteriaOperator.LESS_THAN; return QCriteriaOperator.LESS_THAN;
case "is on or before": case "onOrBefore":
case "<=": case "<=":
return QCriteriaOperator.LESS_THAN_OR_EQUALS; return QCriteriaOperator.LESS_THAN_OR_EQUALS;
case "is empty": case "isEmpty":
return QCriteriaOperator.IS_BLANK; return QCriteriaOperator.IS_BLANK;
case "is not empty": case "isNotEmpty":
return QCriteriaOperator.IS_NOT_BLANK; return QCriteriaOperator.IS_NOT_BLANK;
// case "is any of": // case "is any of":
// TODO: handle this case // TODO: handle this case
@ -140,19 +148,24 @@ function EntityList({ table }: Props): JSX.Element
const buildQFilter = () => const buildQFilter = () =>
{ {
const qFilter = new QQueryFilter(); const qFilter = new QQueryFilter();
sortModel.forEach((gridSortItem) => if (sortModel)
{ {
qFilter.addOrderBy(new QFilterOrderBy(gridSortItem.field, gridSortItem.sort === "asc")); sortModel.forEach((gridSortItem) =>
}); {
qFilter.addOrderBy(new QFilterOrderBy(gridSortItem.field, gridSortItem.sort === "asc"));
});
}
if (filterModel) if (filterModel)
{ {
filterModel.items.forEach((item) => filterModel.items.forEach((item) =>
{ {
qFilter.addCriteria( const operator = translateCriteriaOperator(item.operatorValue);
new QFilterCriteria(item.columnField, translateCriteriaOperator(item.operatorValue), [ let criteria = new QFilterCriteria(item.columnField, operator, [item.value]);
item.value, if (operator === QCriteriaOperator.IS_BLANK || operator === QCriteriaOperator.IS_NOT_BLANK)
]), {
); criteria = new QFilterCriteria(item.columnField, translateCriteriaOperator(item.operatorValue), null);
}
qFilter.addCriteria(criteria);
}); });
} }
@ -168,10 +181,17 @@ function EntityList({ table }: Props): JSX.Element
const tableMetaData = await QClient.loadTableMetaData(tableName); const tableMetaData = await QClient.loadTableMetaData(tableName);
const count = await QClient.count(tableName, qFilter); const count = await QClient.count(tableName, qFilter);
setTotalRecords(count); setTotalRecords(count);
setButtonText(`new ${tableMetaData.label}`);
columnSortLocalStorageKey = `${COLUMN_SORT_LOCAL_STORAGE_KEY_ROOT}.${tableMetaData.name}`;
columnVisibilityLocalStorageKey = `${COLUMN_VISIBILITY_LOCAL_STORAGE_KEY_ROOT}.${tableMetaData.name}`;
setTableLabel(tableMetaData.label);
if (sortModel.length === 0) if (sortModel.length === 0)
{ {
sortModel.push({ field: tableMetaData.primaryKeyField, sort: "desc" }); sortModel.push({
field: tableMetaData.primaryKeyField,
sort: "desc",
});
setSortModel(sortModel); setSortModel(sortModel);
} }
@ -182,18 +202,19 @@ function EntityList({ table }: Props): JSX.Element
qFilter, qFilter,
rowsPerPage, rowsPerPage,
pageNumber * rowsPerPage, pageNumber * rowsPerPage,
).catch((error) => )
{ .catch((error) =>
if (error.message)
{ {
setAlertContent(error.message); if (error.message)
} {
else setAlertContent(error.message);
{ }
setAlertContent(error.response.data.error); else
} {
throw error; setAlertContent(error.response.data.error);
}); }
throw error;
});
const rows = [] as any[]; const rows = [] as any[];
results.forEach((record) => results.forEach((record) =>
@ -244,11 +265,11 @@ function EntityList({ table }: Props): JSX.Element
} }
}); });
const columnVisibilityModel = localStorage.getItem(COLUMN_VISIBILITY_LOCAL_STORAGE_KEY); const columnVisibilityModel = localStorage.getItem(columnVisibilityLocalStorageKey);
if (columnVisibilityModel) if (columnVisibilityModel)
{ {
setColumnVisibilityModel( setColumnVisibilityModel(
JSON.parse(localStorage.getItem(COLUMN_VISIBILITY_LOCAL_STORAGE_KEY)), JSON.parse(localStorage.getItem(columnVisibilityLocalStorageKey)),
); );
} }
setColumns(columns); setColumns(columns);
@ -301,7 +322,7 @@ function EntityList({ table }: Props): JSX.Element
{ {
setColumnVisibilityModel(columnVisibilityModel); setColumnVisibilityModel(columnVisibilityModel);
localStorage.setItem( localStorage.setItem(
COLUMN_VISIBILITY_LOCAL_STORAGE_KEY, columnVisibilityLocalStorageKey,
JSON.stringify(columnVisibilityModel), JSON.stringify(columnVisibilityModel),
); );
}; };
@ -309,7 +330,7 @@ function EntityList({ table }: Props): JSX.Element
const handleSortChange = (gridSort: GridSortModel) => const handleSortChange = (gridSort: GridSortModel) =>
{ {
setSortModel(gridSort); setSortModel(gridSort);
localStorage.setItem(COLUMN_SORT_LOCAL_STORAGE_KEY, JSON.stringify(gridSort)); localStorage.setItem(columnSortLocalStorageKey, JSON.stringify(gridSort));
}; };
if (tableName !== tableState) if (tableName !== tableState)
@ -317,6 +338,10 @@ function EntityList({ table }: Props): JSX.Element
(async () => (async () =>
{ {
setTableState(tableName); setTableState(tableName);
setSortModel([] as GridSortItem[]);
setColumnVisibilityModel([] as GridColumnVisibilityModel[]);
setFilterModel(null);
setFiltersMenu(null);
const metaData = await QClient.loadMetaData(); const metaData = await QClient.loadMetaData();
setTableProcesses(QProcessUtils.getProcessesForTable(metaData, tableName)); setTableProcesses(QProcessUtils.getProcessesForTable(metaData, tableName));
@ -341,7 +366,11 @@ function EntityList({ table }: Props): JSX.Element
The The
<strong>{` ${selectedIds.length.toLocaleString()} `}</strong> <strong>{` ${selectedIds.length.toLocaleString()} `}</strong>
records on this page are selected. records on this page are selected.
<button type="button" onClick={() => setSelectFullFilterState("filter")} className="MuiButton-root MuiButton-text MuiButton-textPrimary MuiButton-sizeSmall MuiButton-textSizeSmall MuiButtonBase-root css-knwngq-MuiButtonBase-root-MuiButton-root"> <button
type="button"
onClick={() => setSelectFullFilterState("filter")}
className="MuiButton-root MuiButton-text MuiButton-textPrimary MuiButton-sizeSmall MuiButton-textSizeSmall MuiButtonBase-root css-knwngq-MuiButtonBase-root-MuiButton-root"
>
Select all Select all
{` ${totalRecords.toLocaleString()} `} {` ${totalRecords.toLocaleString()} `}
records matching this query records matching this query
@ -355,7 +384,11 @@ function EntityList({ table }: Props): JSX.Element
All All
<strong>{` ${totalRecords.toLocaleString()} `}</strong> <strong>{` ${totalRecords.toLocaleString()} `}</strong>
records matching this query are selected. records matching this query are selected.
<button type="button" onClick={() => setSelectFullFilterState("checked")} className="MuiButton-root MuiButton-text MuiButton-textPrimary MuiButton-sizeSmall MuiButton-textSizeSmall MuiButtonBase-root css-knwngq-MuiButtonBase-root-MuiButton-root"> <button
type="button"
onClick={() => setSelectFullFilterState("checked")}
className="MuiButton-root MuiButton-text MuiButton-textPrimary MuiButton-sizeSmall MuiButton-textSizeSmall MuiButtonBase-root css-knwngq-MuiButtonBase-root-MuiButton-root"
>
Select the Select the
{` ${selectedIds.length.toLocaleString()} `} {` ${selectedIds.length.toLocaleString()} `}
records on this page records on this page
@ -386,8 +419,14 @@ function EntityList({ table }: Props): JSX.Element
const renderActionsMenu = ( const renderActionsMenu = (
<Menu <Menu
anchorEl={actionsMenu} anchorEl={actionsMenu}
anchorOrigin={{ vertical: "bottom", horizontal: "left" }} anchorOrigin={{
transformOrigin={{ vertical: "top", horizontal: "left" }} vertical: "bottom",
horizontal: "left",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
open={Boolean(actionsMenu)} open={Boolean(actionsMenu)}
onClose={closeActionsMenu} onClose={closeActionsMenu}
keepMounted keepMounted
@ -402,6 +441,7 @@ function EntityList({ table }: Props): JSX.Element
useEffect(() => useEffect(() =>
{ {
console.log("UPDATING");
updateTable(); updateTable();
}, [pageNumber, rowsPerPage, tableState, sortModel, filterModel]); }, [pageNumber, rowsPerPage, tableState, sortModel, filterModel]);
@ -416,14 +456,25 @@ function EntityList({ table }: Props): JSX.Element
) : ( ) : (
"" ""
)} )}
{
(tableLabel && searchParams.get("deleteSuccess")) ? (
<MDAlert color="success" dismissible>
{`${tableLabel} successfully deleted`}
</MDAlert>
) : ("")
}
<MDBox display="flex" justifyContent="space-between" alignItems="flex-start" mb={2}> <MDBox display="flex" justifyContent="space-between" alignItems="flex-start" mb={2}>
<Link href={`/${tableName}/create`}> {buttonText ? (
<MDButton variant="gradient" color="info"> <Link href={`/${tableName}/create`}>
new <MDButton variant="gradient" color="info">
{" "} {
{tableName} buttonText
</MDButton> }
</Link> </MDButton>
</Link>
) : (
""
)}
<MDBox display="flex"> <MDBox display="flex">
{tableProcesses.length > 0 && ( {tableProcesses.length > 0 && (

View File

@ -2,19 +2,16 @@
========================================================= =========================================================
* Material Dashboard 2 PRO React TS - v1.0.0 * Material Dashboard 2 PRO React TS - v1.0.0
========================================================= =========================================================
* Product Page: https://www.creative-tim.com/product/material-dashboard-2-pro-react-ts * Product Page: https://www.creative-tim.com/product/material-dashboard-2-pro-react-ts
* Copyright 2022 Creative Tim (https://www.creative-tim.com) * Copyright 2022 Creative Tim (https://www.creative-tim.com)
Coded by www.creative-tim.com Coded by www.creative-tim.com
========================================================= =========================================================
* The above copyright notice and this permission notice shall be included in all copies or
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. * substantial portions of the Software.
*/ */
// react components // react components
import { useParams } from "react-router-dom"; import { useParams, useSearchParams } from "react-router-dom";
import React, { useReducer, useState } from "react"; import React, { useReducer, useState } from "react";
// @material-ui core components // @material-ui core components
@ -38,13 +35,15 @@ import MDTypography from "components/MDTypography";
import Menu from "@mui/material/Menu"; import Menu from "@mui/material/Menu";
import MenuItem from "@mui/material/MenuItem"; import MenuItem from "@mui/material/MenuItem";
import Icon from "@mui/material/Icon"; import Icon from "@mui/material/Icon";
import MDAlert from "components/MDAlert";
import MDButton from "../../../../../components/MDButton"; import MDButton from "../../../../../components/MDButton";
import QProcessUtils from "../../../../utils/QProcessUtils"; import QProcessUtils from "../../../../utils/QProcessUtils";
const qController = new QController(""); const qController = new QController("");
// Declaring props types for ViewForm // Declaring props types for ViewForm
interface Props { interface Props
{
id: string; id: string;
} }
@ -58,6 +57,7 @@ function ViewContents({ id }: Props): JSX.Element
const [tableMetaData, setTableMetaData] = useState(null); const [tableMetaData, setTableMetaData] = useState(null);
const [tableProcesses, setTableProcesses] = useState([] as QProcessMetaData[]); const [tableProcesses, setTableProcesses] = useState([] as QProcessMetaData[]);
const [actionsMenu, setActionsMenu] = useState(null); const [actionsMenu, setActionsMenu] = useState(null);
const [searchParams, setSearchParams] = useSearchParams();
const [, forceUpdate] = useReducer((x) => x + 1, 0); const [, forceUpdate] = useReducer((x) => x + 1, 0);
const openActionsMenu = (event: any) => setActionsMenu(event.currentTarget); const openActionsMenu = (event: any) => setActionsMenu(event.currentTarget);
@ -130,10 +130,11 @@ function ViewContents({ id }: Props): JSX.Element
event.preventDefault(); event.preventDefault();
(async () => (async () =>
{ {
await qController.delete(tableName, id).then(() => await qController.delete(tableName, id)
{ .then(() =>
window.location.href = `/${tableName}`; {
}); window.location.href = `/${tableName}?deleteSuccess=true`;
});
})(); })();
}; };
@ -142,8 +143,14 @@ function ViewContents({ id }: Props): JSX.Element
const renderActionsMenu = ( const renderActionsMenu = (
<Menu <Menu
anchorEl={actionsMenu} anchorEl={actionsMenu}
anchorOrigin={{ vertical: "bottom", horizontal: "left" }} anchorOrigin={{
transformOrigin={{ vertical: "top", horizontal: "left" }} vertical: "bottom",
horizontal: "left",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
open={Boolean(actionsMenu)} open={Boolean(actionsMenu)}
onClose={closeActionsMenu} onClose={closeActionsMenu}
keepMounted keepMounted
@ -157,76 +164,90 @@ function ViewContents({ id }: Props): JSX.Element
); );
return ( return (
<Card id="basic-info" sx={{ overflow: "visible" }}>
<MDBox p={3}> <MDBox pb={3}>
<MDBox display="flex" justifyContent="space-between"> {
<MDTypography variant="h5"> (searchParams.get("createSuccess") || searchParams.get("updateSuccess")) ? (
Viewing <MDAlert color="success" dismissible>
{" "}
{tableMetaData?.label} {tableMetaData?.label}
{" "} {" "}
( successfully
{id} {" "}
) {searchParams.get("createSuccess") ? "created" : "updated"}
</MDTypography>
{tableProcesses.length > 0 && ( </MDAlert>
<MDButton ) : ("")
variant={actionsMenu ? "contained" : "outlined"} }
color="dark" <Card id="basic-info" sx={{ overflow: "visible" }}>
onClick={openActionsMenu} <MDBox p={3}>
> <MDBox display="flex" justifyContent="space-between">
actions&nbsp; <MDTypography variant="h5">
<Icon>keyboard_arrow_down</Icon> Viewing
</MDButton>
)}
{renderActionsMenu}
</MDBox>
</MDBox>
<MDBox p={3}>{nameValues}</MDBox>
<MDBox component="form" pb={3} px={3}>
<Grid key="tres" container spacing={3}>
<MDBox ml="auto" mr={3}>
<MDButton
variant="gradient"
color="primary"
size="small"
onClick={handleClickConfirmOpen}
>
delete
{" "} {" "}
{tableMetaData?.label} {tableMetaData?.label}
</MDButton> {" "}
<Dialog (
open={open} {id}
onClose={handleClickConfirmClose} )
aria-labelledby="alert-dialog-title" </MDTypography>
aria-describedby="alert-dialog-description" {tableProcesses.length > 0 && (
> <MDButton
<DialogTitle id="alert-dialog-title">Confirm Deletion</DialogTitle> variant={actionsMenu ? "contained" : "outlined"}
<DialogContent> color="dark"
<DialogContentText id="alert-dialog-description"> onClick={openActionsMenu}
Are you sure you want to delete this record? >
</DialogContentText> actions&nbsp;
</DialogContent> <Icon>keyboard_arrow_down</Icon>
<DialogActions> </MDButton>
<Button onClick={handleClickConfirmClose}>No</Button> )}
<Button onClick={handleDelete} autoFocus> {renderActionsMenu}
Yes
</Button>
</DialogActions>
</Dialog>
</MDBox> </MDBox>
<MDBox> </MDBox>
<MDButton variant="gradient" color="dark" size="small"> <MDBox p={3}>{nameValues}</MDBox>
<Link href={editPath}> <MDBox component="form" pb={3} px={3}>
edit <Grid key="tres" container spacing={3}>
<MDBox ml="auto" mr={3}>
<MDButton
variant="gradient"
color="primary"
size="small"
onClick={handleClickConfirmOpen}
>
delete
{" "}
{tableMetaData?.label} {tableMetaData?.label}
</Link> </MDButton>
</MDButton> <Dialog
</MDBox> open={open}
</Grid> onClose={handleClickConfirmClose}
</MDBox> aria-labelledby="alert-dialog-title"
</Card> aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">Confirm Deletion</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Are you sure you want to delete this record?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClickConfirmClose}>No</Button>
<Button onClick={handleDelete} autoFocus>
Yes
</Button>
</DialogActions>
</Dialog>
</MDBox>
<MDBox>
<MDButton variant="gradient" color="dark" size="small">
<Link href={editPath}>
{`edit ${tableMetaData?.label}`}
</Link>
</MDButton>
</MDBox>
</Grid>
</MDBox>
</Card>
</MDBox>
); );
} }