mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-19 05:40:44 +00:00
Add add-child capability to recordGridWidget; making new standard Widget that others can(should) use
This commit is contained in:
@ -259,7 +259,7 @@ function CarrierPerformance(): JSX.Element
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12} sm={4}>
|
||||
<SimpleStatisticsCard
|
||||
title={qInstance?.widgets.get("TotalShipmentsStatisticsCard").label}
|
||||
title={qInstance?.widgets?.get("TotalShipmentsStatisticsCard").label}
|
||||
data={totalShipmentsData}
|
||||
increaseIsGood={true}
|
||||
dropdown={{
|
||||
@ -271,7 +271,7 @@ function CarrierPerformance(): JSX.Element
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={4}>
|
||||
<SimpleStatisticsCard
|
||||
title={qInstance?.widgets.get("SuccessfulDeliveriesStatisticsCard").label}
|
||||
title={qInstance?.widgets?.get("SuccessfulDeliveriesStatisticsCard").label}
|
||||
data={successfulDeliveriesData}
|
||||
increaseIsGood={true}
|
||||
dropdown={{
|
||||
@ -284,7 +284,7 @@ function CarrierPerformance(): JSX.Element
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={4}>
|
||||
<SimpleStatisticsCard
|
||||
title={qInstance?.widgets.get("ServiceFailuresStatisticsCard").label}
|
||||
title={qInstance?.widgets?.get("ServiceFailuresStatisticsCard").label}
|
||||
data={serviceFailuresData}
|
||||
increaseIsGood={false}
|
||||
dropdown={{
|
||||
@ -300,7 +300,7 @@ function CarrierPerformance(): JSX.Element
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12} sm={6} lg={8}>
|
||||
<DefaultLineChart
|
||||
title={qInstance?.widgets.get("CarrierVolumeLineChart").label}
|
||||
title={qInstance?.widgets?.get("CarrierVolumeLineChart").label}
|
||||
data={carrierVolumeData}
|
||||
/>
|
||||
</Grid>
|
||||
|
@ -265,7 +265,7 @@ function Overview(): JSX.Element
|
||||
<MDBox mb={3}>
|
||||
<BarChart
|
||||
color="info"
|
||||
title={qInstance?.widgets.get("TotalShipmentsByDayBarChart").label}
|
||||
title={qInstance?.widgets?.get("TotalShipmentsByDayBarChart").label}
|
||||
description={shipmentsByDayDescription}
|
||||
date="Updated 3 minutes ago"
|
||||
data={shipmentsByDayData}
|
||||
@ -275,7 +275,7 @@ function Overview(): JSX.Element
|
||||
<Grid item xs={12} md={6} lg={4}>
|
||||
<MDBox mb={3}>
|
||||
<PieChartCard
|
||||
title={qInstance?.widgets.get("YTDShipmentsByCarrierPieChart").label}
|
||||
title={qInstance?.widgets?.get("YTDShipmentsByCarrierPieChart").label}
|
||||
description={shipmentsByCarrierDescription}
|
||||
data={shipmentsByCarrierData}
|
||||
/>
|
||||
@ -285,7 +285,7 @@ function Overview(): JSX.Element
|
||||
<MDBox mb={3}>
|
||||
<SmallLineChart
|
||||
color="dark"
|
||||
title={qInstance?.widgets.get("TotalShipmentsByMonthLineChart").label}
|
||||
title={qInstance?.widgets?.get("TotalShipmentsByMonthLineChart").label}
|
||||
description={shipmentsByMonthDescription}
|
||||
date=""
|
||||
chart={shipmentsByMonthData}
|
||||
|
@ -21,32 +21,28 @@
|
||||
|
||||
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
|
||||
import Box from "@mui/material/Box";
|
||||
import Card from "@mui/material/Card";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import {DataGridPro, GridValidRowModel} from "@mui/x-data-grid-pro";
|
||||
import {DataGridPro} from "@mui/x-data-grid-pro";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import MDTypography from "qqq/components/Temporary/MDTypography";
|
||||
import DataGridUtils from "qqq/utils/DataGridUtils";
|
||||
import Widget, {AddNewRecordButton, HeaderLink} from "./Widget";
|
||||
|
||||
interface Props
|
||||
{
|
||||
title: string
|
||||
title: string;
|
||||
data: any;
|
||||
reloadWidgetCallback?: (widgetIndex: number, params: string) => void;
|
||||
}
|
||||
|
||||
RecordGridWidget.defaultProps = {
|
||||
};
|
||||
RecordGridWidget.defaultProps = {};
|
||||
|
||||
function RecordGridWidget({title, data}: Props): JSX.Element
|
||||
function RecordGridWidget({title, data, reloadWidgetCallback}: Props): JSX.Element
|
||||
{
|
||||
const [rows, setRows] = useState([]);
|
||||
const [columns, setColumns] = useState([])
|
||||
const [columns, setColumns] = useState([]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(data && data.childTableMetaData && data.queryOutput)
|
||||
if (data && data.childTableMetaData && data.queryOutput)
|
||||
{
|
||||
const records: QRecord[] = [];
|
||||
const queryOutputRecords = data.queryOutput.records;
|
||||
@ -60,28 +56,26 @@ function RecordGridWidget({title, data}: Props): JSX.Element
|
||||
|
||||
const tableMetaData = new QTableMetaData(data.childTableMetaData);
|
||||
const {rows, columnsToRender} = DataGridUtils.makeRows(records, tableMetaData);
|
||||
const columns = DataGridUtils.setupGridColumns(tableMetaData, columnsToRender, data.tablePath);
|
||||
|
||||
const childTablePath = data.tablePath + (data.tablePath.endsWith("/") ? "" : "/")
|
||||
const columns = DataGridUtils.setupGridColumns(tableMetaData, columnsToRender, childTablePath);
|
||||
|
||||
setRows(rows);
|
||||
setColumns(columns);
|
||||
}
|
||||
}, [data])
|
||||
}, [data]);
|
||||
|
||||
return (
|
||||
<Card sx={{width: "100%"}}>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="h5" fontWeight="medium" p={3}>
|
||||
{title}
|
||||
</Typography>
|
||||
{
|
||||
data.viewAllLink &&
|
||||
<Typography variant="body2" p={3}>
|
||||
<Link to={data.viewAllLink}>
|
||||
View All
|
||||
</Link>
|
||||
</Typography>
|
||||
}
|
||||
</Box>
|
||||
<Widget
|
||||
label={title}
|
||||
labelAdditionalComponentsLeft={[
|
||||
new HeaderLink("View All", data.viewAllLink)
|
||||
]}
|
||||
labelAdditionalComponentsRight={[
|
||||
new AddNewRecordButton(data.childTableMetaData, data.defaultValuesForNewChildRecords)
|
||||
]}
|
||||
reloadWidgetCallback={reloadWidgetCallback}
|
||||
>
|
||||
<DataGridPro
|
||||
autoHeight
|
||||
rows={rows}
|
||||
@ -115,8 +109,8 @@ function RecordGridWidget({title, data}: Props): JSX.Element
|
||||
// sortingOrder={[ "asc", "desc" ]}
|
||||
// sortModel={columnSortModel}
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
export default RecordGridWidget;
|
||||
|
194
src/qqq/pages/dashboards/Widgets/Widget.tsx
Normal file
194
src/qqq/pages/dashboards/Widgets/Widget.tsx
Normal file
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* 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 {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||
import Box from "@mui/material/Box";
|
||||
import Button from "@mui/material/Button";
|
||||
import Card from "@mui/material/Card";
|
||||
import Modal from "@mui/material/Modal";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import React, {useState} from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import EntityForm from "qqq/components/EntityForm";
|
||||
|
||||
interface Props
|
||||
{
|
||||
label: string;
|
||||
labelAdditionalComponentsLeft: [LabelComponent];
|
||||
labelAdditionalComponentsRight: [LabelComponent];
|
||||
children: JSX.Element;
|
||||
reloadWidgetCallback?: (widgetIndex: number, params: string) => void;
|
||||
}
|
||||
|
||||
Widget.defaultProps = {
|
||||
label: null,
|
||||
labelAdditionalComponentsLeft: [],
|
||||
labelAdditionalComponentsRight: [],
|
||||
};
|
||||
|
||||
|
||||
|
||||
class LabelComponent
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class HeaderLink extends LabelComponent
|
||||
{
|
||||
label: string;
|
||||
to: string
|
||||
|
||||
constructor(label: string, to: string)
|
||||
{
|
||||
super();
|
||||
this.label = label;
|
||||
this.to = to;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class AddNewRecordButton extends LabelComponent
|
||||
{
|
||||
table: QTableMetaData;
|
||||
label:string;
|
||||
defaultValues: any;
|
||||
disabledFields: any;
|
||||
|
||||
constructor(table: QTableMetaData, defaultValues: any, label: string = "Add new", disabledFields: any = defaultValues)
|
||||
{
|
||||
super();
|
||||
this.table = table;
|
||||
this.label = label;
|
||||
this.defaultValues = defaultValues;
|
||||
this.disabledFields = disabledFields;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function Widget(props: React.PropsWithChildren<Props>): JSX.Element
|
||||
{
|
||||
const [showEditForm, setShowEditForm] = useState(null as any);
|
||||
|
||||
function openEditForm(table: QTableMetaData, id: any = null, defaultValues: any, disabledFields: any)
|
||||
{
|
||||
const showEditForm: any = {};
|
||||
showEditForm.table = table;
|
||||
showEditForm.id = id;
|
||||
showEditForm.defaultValues = defaultValues;
|
||||
showEditForm.disabledFields = disabledFields;
|
||||
setShowEditForm(showEditForm);
|
||||
}
|
||||
|
||||
const closeEditForm = (event: object, reason: string) =>
|
||||
{
|
||||
if (reason === "backdropClick")
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (reason === "recordUpdated" || reason === "recordCreated")
|
||||
{
|
||||
if(props.reloadWidgetCallback)
|
||||
{
|
||||
props.reloadWidgetCallback(0, "ok");
|
||||
}
|
||||
else
|
||||
{
|
||||
window.location.reload()
|
||||
}
|
||||
}
|
||||
|
||||
setShowEditForm(null);
|
||||
};
|
||||
|
||||
function renderComponent(component: LabelComponent)
|
||||
{
|
||||
if(component instanceof HeaderLink)
|
||||
{
|
||||
const link = component as HeaderLink
|
||||
return (
|
||||
<Typography variant="body2" p={2} display="inline">
|
||||
{link.to ? <Link to={link.to}>{link.label}</Link> : null}
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
||||
if (component instanceof AddNewRecordButton)
|
||||
{
|
||||
const addNewRecordButton = component as AddNewRecordButton
|
||||
return (
|
||||
<Typography variant="body2" p={2} pr={1} display="inline">
|
||||
<Button onClick={() => openEditForm(addNewRecordButton.table, null, addNewRecordButton.defaultValues, addNewRecordButton.disabledFields)}>{addNewRecordButton.label}</Button>
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card sx={{width: "100%"}}>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Box py={2}>
|
||||
<Typography variant="h6" fontWeight="medium" p={3} display="inline">
|
||||
{props.label}
|
||||
</Typography>
|
||||
{
|
||||
props.labelAdditionalComponentsLeft.map((component, i) =>
|
||||
{
|
||||
return (<span key={i}>{renderComponent(component)}</span>);
|
||||
})
|
||||
}
|
||||
</Box>
|
||||
<Box pr={1}>
|
||||
{
|
||||
props.labelAdditionalComponentsRight.map((component, i) =>
|
||||
{
|
||||
return (<span key={i}>{renderComponent(component)}</span>);
|
||||
})
|
||||
}
|
||||
</Box>
|
||||
</Box>
|
||||
{props.children}
|
||||
</Card>
|
||||
{
|
||||
showEditForm &&
|
||||
<Modal open={showEditForm as boolean} onClose={(event, reason) => closeEditForm(event, reason)}>
|
||||
<div className="modalEditForm">
|
||||
<EntityForm
|
||||
isModal={true}
|
||||
closeModalHandler={closeEditForm}
|
||||
table={showEditForm.table}
|
||||
id={showEditForm.id}
|
||||
defaultValues={showEditForm.defaultValues}
|
||||
disabledFields={showEditForm.disabledFields} />
|
||||
</div>
|
||||
</Modal>
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Widget;
|
@ -273,9 +273,9 @@ function EntityView({table, launchProcess}: Props): JSX.Element
|
||||
{
|
||||
sectionFieldElements.set(section.name,
|
||||
<Grid id={section.name} key={section.name} item lg={12} xs={12} sx={{display: "flex", alignItems: "stretch", scrollMarginTop: "100px"}}>
|
||||
<MDBox mb={3} width="100%">
|
||||
<MDBox width="100%">
|
||||
<Card id={section.name} sx={{overflow: "visible", scrollMarginTop: "100px"}}>
|
||||
<MDTypography variant="h5" p={3} pb={1}>
|
||||
<MDTypography variant="h6" p={3} pb={1}>
|
||||
{section.label}
|
||||
</MDTypography>
|
||||
<MDBox p={3} pt={0} flexDirection="column">
|
||||
@ -466,9 +466,9 @@ function EntityView({table, launchProcess}: Props): JSX.Element
|
||||
{nonT1TableSections.length > 0 ? nonT1TableSections.map(({
|
||||
iconName, label, name, fieldNames, tier,
|
||||
}: any) => (
|
||||
<>
|
||||
<React.Fragment key={name}>
|
||||
{sectionFieldElements.get(name)}
|
||||
</>
|
||||
</React.Fragment>
|
||||
)) : null}
|
||||
</Grid>
|
||||
<MDBox component="form" p={3}>
|
||||
|
@ -275,7 +275,7 @@ function ProcessRun({process, defaultProcessValues, isModal, isWidget, recordIds
|
||||
<Grid m={3} mt={9} container>
|
||||
<Grid item xs={0} lg={3} />
|
||||
<Grid item xs={12} lg={6}>
|
||||
<Card>
|
||||
<Card elevation={5}>
|
||||
<MDBox p={3}>
|
||||
<MDTypography variant="h5" component="div">
|
||||
Working
|
||||
|
Reference in New Issue
Block a user