Files
qqq-frontend-material-dashb…/src/qqq/pages/app-home/index.tsx
2022-08-29 21:48:38 -05:00

324 lines
14 KiB
TypeScript

/*
* 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 {QAppMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QAppMetaData";
import {QAppNodeType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QAppNodeType";
import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance";
import {QProcessMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QProcessMetaData";
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
import {Icon} from "@mui/material";
import Card from "@mui/material/Card";
import Grid from "@mui/material/Grid";
import React, {useEffect, useState} from "react";
import {Link, useLocation} from "react-router-dom";
import MDBadgeDot from "components/MDBadgeDot";
import MDBox from "components/MDBox";
import MDTypography from "components/MDTypography";
import DefaultInfoCard from "examples/Cards/InfoCards/DefaultInfoCard";
import MiniStatisticsCard from "examples/Cards/StatisticsCards/MiniStatisticsCard";
import DefaultLineChart from "examples/Charts/LineCharts/DefaultLineChart";
import BaseLayout from "qqq/components/BaseLayout";
import ProcessLinkCard from "qqq/components/ProcessLinkCard";
import BarChart from "qqq/components/Widgets/BarChart";
import QuickSightChart from "qqq/components/Widgets/QuickSightChart";
import QClient from "qqq/utils/QClient";
const qController = QClient.getInstance();
interface Props
{
app?: QAppMetaData;
}
function AppHome({app}: Props): JSX.Element
{
const [qInstance, setQInstance] = useState(null as QInstance);
const [tables, setTables] = useState([] as QTableMetaData[]);
const [processes, setProcesses] = useState([] as QProcessMetaData[]);
const [childApps, setChildApps] = useState([] as QAppMetaData[]);
const [tableCounts, setTableCounts] = useState(new Map<string, { isLoading: boolean, value: number }>());
const [updatedTableCounts, setUpdatedTableCounts] = useState(new Date());
const [widgets, setWidgets] = useState([] as any[]);
const location = useLocation();
useEffect(() =>
{
(async () =>
{
const newQInstance = await qController.loadMetaData();
setQInstance(newQInstance);
})();
}, []);
useEffect(() =>
{
if (!qInstance)
{
return;
}
const newTables: QTableMetaData[] = [];
const newProcesses: QProcessMetaData[] = [];
const newChildApps: QAppMetaData[] = [];
app.children.forEach((child) =>
{
switch (child.type)
{
// todo - filter out hidden
case QAppNodeType.TABLE:
newTables.push(qInstance.tables.get(child.name));
break;
case QAppNodeType.PROCESS:
newProcesses.push(qInstance.processes.get(child.name));
break;
case QAppNodeType.APP:
newChildApps.push(qInstance.apps.get(child.name));
break;
default:
console.log(`Unexpected child type: ${child.type}`);
}
});
setTables(newTables);
setProcesses(newProcesses);
setChildApps(newChildApps);
const tableCounts = new Map<string, { isLoading: boolean, value: number }>();
newTables.forEach((table) =>
{
tableCounts.set(table.name, {isLoading: true, value: null});
setTimeout(async () =>
{
const count = await qController.count(table.name);
tableCounts.set(table.name, {isLoading: false, value: count});
setTableCounts(tableCounts);
setUpdatedTableCounts(new Date());
}, 1);
});
setTableCounts(tableCounts);
console.log(app.widgets);
if (app.widgets)
{
const widgets: any[] = [];
for (let i = 0; i < app.widgets.length; i++)
{
widgets[i] = {};
setTimeout(async () =>
{
const widget = await qController.widget(app.widgets[i]);
widgets[i] = widget;
setUpdatedTableCounts(new Date());
}, 1);
}
setWidgets(widgets);
}
}, [qInstance, location]);
/*
const charts = [
{
type: "barChart",
title: "Parcel Invoice Lines per Month",
barChartData: {
labels: ["Feb 22", "Mar 22", "Apr 22", "May 22", "Jun 22", "Jul 22", "Aug 22"],
datasets: {label: "Parcel Invoice Lines", data: [50000, 22000, 11111, 22333, 40404, 9876, 2355]},
},
},
{
type: "lineChart",
title: "Total Charges by Carrier per Month",
lineChartData: {
labels: ["Feb 22", "Mar 22", "Apr 22", "May 22", "Jun 22", "Jul 22", "Aug 22"],
datasets: [
{label: "UPS", color: "info", data: [50000, 22000, 11111, 22333, 40404, 9876, 2355]},
{label: "FedEx", color: "dark", data: [5000, 22000, 31111, 32333, 20404, 19876, 24355]},
{label: "LSO", color: "error", data: [500, 2200, 1111, 2333, 404, 17876, 2355]},
],
},
},
];
*/
console.log(`Widgets: ${widgets} and tables: ${tables}`);
const haveWidgets = widgets && widgets.length;
const widgetCount = widgets ? widgets.length : 0;
// eslint-disable-next-line no-nested-ternary
const tileSizeLg = (widgetCount === 0 ? 3 : widgetCount === 1 ? 4 : 6);
let gridIndex = 0;
return (
<BaseLayout>
<MDBox mt={4}>
<Grid container spacing={3}>
{
widgetCount > 0 ? (
<Grid item xs={12} lg={widgetCount === 1 ? 3 : 6}>
<Grid container spacing={3}>
{
widgets.map((chart) => (
<Grid key={`${gridIndex++}`} item xs={12} lg={widgetCount === 1 ? 12 : 6}>
<MDBox mb={3}>
{
chart.type === "quickSightChart" && (
<QuickSightChart url={chart.url} label={chart.label} />
)
}
{
chart.type === "barChart" && (
<BarChart
color="info"
title={chart.title}
date={`As of ${new Date().toDateString()}`}
chart={chart.barChartData}
/>
)
}
{
chart.type === "lineChart" && (
<DefaultLineChart
title={chart.title}
description={(
<MDBox display="flex" justifyContent="space-between">
<MDBox display="flex" ml={-1}>
{
chart.lineChartData.datasets.map((dataSet: any) => (
<MDBadgeDot key={dataSet.label} color={dataSet.color} size="sm" badgeContent={dataSet.label} />
))
}
</MDBox>
<MDBox mt={-4} mr={-1} position="absolute" right="1.5rem" />
</MDBox>
)}
chart={chart.lineChartData as { labels: string[]; datasets: { label: string; color: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "light" | "dark"; data: number[]; }[]; }}
/>
)
}
</MDBox>
</Grid>
))
}
</Grid>
</Grid>
) : null
}
{
tables.length > 0 || processes.length > 0 || childApps.length > 0 ? (
// eslint-disable-next-line no-nested-ternary
<Grid item xs={12} lg={widgetCount === 0 ? 12 : widgetCount === 1 ? 9 : 6}>
{
tables.length ? (
<MDBox mb={3}>
<Card id="basic-info" sx={{overflow: "visible"}}>
<MDBox p={3}>
<MDTypography variant="h5">Tables</MDTypography>
</MDBox>
<Grid container spacing={3} padding={3} paddingTop={0}>
{tables.map((table) => (
<Grid key={table.name} item xs={12} md={12} lg={tileSizeLg}>
<Link to={table.name}>
<MDBox mb={3}>
<MiniStatisticsCard
title={{fontWeight: "bold", text: table.label}}
count={!tableCounts.has(table.name) || tableCounts.get(table.name).isLoading ? "..." : tableCounts.get(table.name).value.toLocaleString()}
percentage={{color: "info", text: (!tableCounts.has(table.name) || tableCounts.get(table.name).isLoading ? "" : "total records")}}
icon={{color: "info", component: <Icon>{table.iconName || app.iconName}</Icon>}}
direction="right"
/>
</MDBox>
</Link>
</Grid>
))}
</Grid>
</Card>
</MDBox>
) : null
}
{
processes.length ? (
<MDBox mb={3}>
<Card id="basic-info" sx={{overflow: "visible"}}>
<MDBox p={3}>
<MDTypography variant="h5">Processes</MDTypography>
</MDBox>
<Grid container spacing={3} padding={3} paddingTop={3}>
{processes.map((process) => (
<Grid key={process.name} item xs={12} md={12} lg={tileSizeLg}>
<Link to={process.name}>
<ProcessLinkCard
icon={process.iconName || app.iconName}
title={process.label}
/>
</Link>
</Grid>
))}
</Grid>
</Card>
</MDBox>
) : null
}
{
childApps.length ? (
<MDBox mb={3}>
<Card id="basic-info" sx={{overflow: "visible"}}>
<MDBox p={3}>
<MDTypography variant="h5">Apps</MDTypography>
</MDBox>
<Grid container spacing={3} padding={3} paddingTop={3}>
{childApps.map((childApp) => (
<Grid key={childApp.name} item xs={12} md={12} lg={tileSizeLg}>
<Link to={childApp.name}>
<DefaultInfoCard
icon={childApp.iconName || app.iconName}
title={childApp.label}
/>
</Link>
</Grid>
))}
</Grid>
</Card>
</MDBox>
) : null
}
</Grid>
) : null
}
</Grid>
</MDBox>
</BaseLayout>
);
}
AppHome.defaultProps = {
app: null,
};
export default AppHome;