mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-17 12:50:43 +00:00
SPRINT-17: updated some widgets to look less broken when data is 'not available now', checkpoint commit on 'real dashboards'
This commit is contained in:
@ -13,7 +13,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.38",
|
"@kingsrook/qqq-frontend-core": "1.0.39",
|
||||||
"@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",
|
||||||
|
BIN
public/flags/AU.png
Normal file
BIN
public/flags/AU.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
BIN
public/flags/BR.png
Normal file
BIN
public/flags/BR.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
public/flags/DE.png
Normal file
BIN
public/flags/DE.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 927 B |
BIN
public/flags/GB.png
Normal file
BIN
public/flags/GB.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
BIN
public/flags/US.png
Normal file
BIN
public/flags/US.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
24
src/App.tsx
24
src/App.tsx
@ -83,7 +83,6 @@ function getStaticRoutes()
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{type: "divider", key: "divider-1"},
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -411,14 +410,35 @@ export default function App()
|
|||||||
|
|
||||||
const sideNavAppList = [] as any[];
|
const sideNavAppList = [] as any[];
|
||||||
const appRoutesList = [] as any[];
|
const appRoutesList = [] as any[];
|
||||||
|
const mainDashboardsApp = {} as any;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// iterate throught the list to find the 'main dashboard so we can put it first' //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
for (let i = 0; i < metaData.appTree.length; i++)
|
for (let i = 0; i < metaData.appTree.length; i++)
|
||||||
{
|
{
|
||||||
const app = metaData.appTree[i];
|
const app = metaData.appTree[i];
|
||||||
|
if(app.name === "mainDashboards")
|
||||||
|
{
|
||||||
|
addAppToSideNavList(app, sideNavAppList, "", 0);
|
||||||
|
addAppToAppRoutesList(metaData, app, appRoutesList, "", 0);
|
||||||
|
sideNavAppList.push({type: "divider", key: "divider-1"});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = 0; i < metaData.appTree.length; i++)
|
||||||
|
{
|
||||||
|
const app = metaData.appTree[i];
|
||||||
|
if(app.name === "mainDashboards")
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
addAppToSideNavList(app, sideNavAppList, "", 0);
|
addAppToSideNavList(app, sideNavAppList, "", 0);
|
||||||
addAppToAppRoutesList(metaData, app, appRoutesList, "", 0);
|
addAppToAppRoutesList(metaData, app, appRoutesList, "", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const newSideNavRoutes = getStaticRoutes();
|
const newSideNavRoutes = []; // getStaticRoutes();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
newSideNavRoutes.unshift(profileRoutes);
|
newSideNavRoutes.unshift(profileRoutes);
|
||||||
for (let i = 0; i < sideNavAppList.length; i++)
|
for (let i = 0; i < sideNavAppList.length; i++)
|
||||||
|
@ -37,11 +37,15 @@ import FieldValueListWidget from "qqq/pages/dashboards/Widgets/FieldValueListWid
|
|||||||
import HorizontalBarChart from "qqq/pages/dashboards/Widgets/HorizontalBarChart";
|
import HorizontalBarChart from "qqq/pages/dashboards/Widgets/HorizontalBarChart";
|
||||||
import MultiStatisticsCard from "qqq/pages/dashboards/Widgets/MultiStatisticsCard";
|
import MultiStatisticsCard from "qqq/pages/dashboards/Widgets/MultiStatisticsCard";
|
||||||
import ParentWidget from "qqq/pages/dashboards/Widgets/ParentWidget";
|
import ParentWidget from "qqq/pages/dashboards/Widgets/ParentWidget";
|
||||||
|
import PieChartCard from "qqq/pages/dashboards/Widgets/PieChartCard";
|
||||||
import QuickSightChart from "qqq/pages/dashboards/Widgets/QuickSightChart";
|
import QuickSightChart from "qqq/pages/dashboards/Widgets/QuickSightChart";
|
||||||
import RecordGridWidget from "qqq/pages/dashboards/Widgets/RecordGridWidget";
|
import RecordGridWidget from "qqq/pages/dashboards/Widgets/RecordGridWidget";
|
||||||
import SimpleStatisticsCard from "qqq/pages/dashboards/Widgets/SimpleStatisticsCard";
|
import SimpleStatisticsCard from "qqq/pages/dashboards/Widgets/SimpleStatisticsCard";
|
||||||
|
import SmallLineChart from "qqq/pages/dashboards/Widgets/SmallLineChart";
|
||||||
|
import StatisticsCard from "qqq/pages/dashboards/Widgets/StatisticsCard";
|
||||||
import StepperCard from "qqq/pages/dashboards/Widgets/StepperCard";
|
import StepperCard from "qqq/pages/dashboards/Widgets/StepperCard";
|
||||||
import TableCard from "qqq/pages/dashboards/Widgets/TableCard";
|
import TableCard from "qqq/pages/dashboards/Widgets/TableCard";
|
||||||
|
import USMapWidget from "qqq/pages/dashboards/Widgets/USMapWidget";
|
||||||
import Widget from "qqq/pages/dashboards/Widgets/Widget";
|
import Widget from "qqq/pages/dashboards/Widgets/Widget";
|
||||||
import ProcessRun from "qqq/pages/process-run";
|
import ProcessRun from "qqq/pages/process-run";
|
||||||
import QClient from "qqq/utils/QClient";
|
import QClient from "qqq/utils/QClient";
|
||||||
@ -98,9 +102,9 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit
|
|||||||
widgetData[i] = {};
|
widgetData[i] = {};
|
||||||
(async () =>
|
(async () =>
|
||||||
{
|
{
|
||||||
console.log(`widgets: ${getQueryParams(null)}`)
|
|
||||||
widgetData[i] = await qController.widget(widgetMetaDataList[i].name, getQueryParams(null));
|
widgetData[i] = await qController.widget(widgetMetaDataList[i].name, getQueryParams(null));
|
||||||
setWidgetCounter(widgetCounter + 1);
|
setWidgetCounter(widgetCounter + 1);
|
||||||
|
console.log(`widget data: ${JSON.stringify(widgetData[i])}`)
|
||||||
forceUpdate();
|
forceUpdate();
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
@ -147,7 +151,7 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit
|
|||||||
}
|
}
|
||||||
|
|
||||||
return params;
|
return params;
|
||||||
};
|
}
|
||||||
|
|
||||||
const widgetCount = widgetMetaDataList ? widgetMetaDataList.length : 0;
|
const widgetCount = widgetMetaDataList ? widgetMetaDataList.length : 0;
|
||||||
|
|
||||||
@ -158,6 +162,7 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit
|
|||||||
{
|
{
|
||||||
widgetMetaData.type === "parentWidget" && (
|
widgetMetaData.type === "parentWidget" && (
|
||||||
<ParentWidget
|
<ParentWidget
|
||||||
|
icon={widgetMetaData.icon}
|
||||||
entityPrimaryKey={entityPrimaryKey}
|
entityPrimaryKey={entityPrimaryKey}
|
||||||
tableName={tableName}
|
tableName={tableName}
|
||||||
widgetIndex={i}
|
widgetIndex={i}
|
||||||
@ -168,20 +173,38 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
widgetMetaData.type === "table" && (
|
widgetMetaData.type === "usaMap" && (
|
||||||
<TableCard
|
<USMapWidget
|
||||||
color="info"
|
|
||||||
title={widgetMetaData.label}
|
|
||||||
linkText={widgetData[i]?.linkText}
|
|
||||||
linkURL={widgetData[i]?.linkURL}
|
|
||||||
noRowsFoundHTML={widgetData[i]?.noRowsFoundHTML}
|
|
||||||
data={widgetData[i]}
|
|
||||||
dropdownOptions={widgetData[i]?.dropdownOptions}
|
|
||||||
reloadWidgetCallback={reloadWidget}
|
|
||||||
widgetIndex={i}
|
widgetIndex={i}
|
||||||
|
label={widgetMetaData.label}
|
||||||
|
data={widgetData[i]}
|
||||||
|
reloadWidgetCallback={reloadWidget}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
widgetMetaData.type === "table" && (
|
||||||
|
<Widget
|
||||||
|
label={widgetData[i]?.label}
|
||||||
|
isCard={widgetMetaData.isCard}
|
||||||
|
widgetData={widgetData[i]}
|
||||||
|
reloadWidgetCallback={(data) => reloadWidget(i, data)}
|
||||||
|
isChild={areChildren}
|
||||||
|
>
|
||||||
|
<TableCard
|
||||||
|
color="info"
|
||||||
|
title={widgetMetaData.label}
|
||||||
|
linkText={widgetData[i]?.linkText}
|
||||||
|
linkURL={widgetData[i]?.linkURL}
|
||||||
|
noRowsFoundHTML={widgetData[i]?.noRowsFoundHTML}
|
||||||
|
data={widgetData[i]}
|
||||||
|
dropdownOptions={widgetData[i]?.dropdownOptions}
|
||||||
|
reloadWidgetCallback={(data) => reloadWidget(i, data)}
|
||||||
|
widgetIndex={i}
|
||||||
|
/>
|
||||||
|
</Widget>
|
||||||
|
)
|
||||||
|
}
|
||||||
{
|
{
|
||||||
widgetMetaData.type === "process" && widgetData[i]?.processMetaData && (
|
widgetMetaData.type === "process" && widgetData[i]?.processMetaData && (
|
||||||
<Widget
|
<Widget
|
||||||
@ -235,10 +258,21 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
widgetMetaData.type === "statistics" && (
|
widgetMetaData.type === "smallLineChart" && (
|
||||||
|
<SmallLineChart
|
||||||
|
color="dark"
|
||||||
|
title={widgetMetaData.label}
|
||||||
|
description={widgetData[i]?.description}
|
||||||
|
date=""
|
||||||
|
chart={widgetData[i]?.chartData}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
widgetMetaData.type === "simpleStatistics" && (
|
||||||
widgetData && widgetData[i] && (
|
widgetData && widgetData[i] && (
|
||||||
<SimpleStatisticsCard
|
<SimpleStatisticsCard
|
||||||
title={widgetMetaData.label}
|
title={widgetData[i]?.title}
|
||||||
data={widgetData[i]}
|
data={widgetData[i]}
|
||||||
increaseIsGood={widgetData[i].increaseIsGood}
|
increaseIsGood={widgetData[i].increaseIsGood}
|
||||||
isCurrency={widgetData[i].isCurrency}
|
isCurrency={widgetData[i].isCurrency}
|
||||||
@ -246,6 +280,17 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
widgetMetaData.type === "statistics" && (
|
||||||
|
widgetData && widgetData[i] && (
|
||||||
|
<StatisticsCard
|
||||||
|
icon={widgetMetaData.icon}
|
||||||
|
data={widgetData[i]}
|
||||||
|
increaseIsGood={true}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
{
|
{
|
||||||
widgetMetaData.type === "quickSightChart" && (
|
widgetMetaData.type === "quickSightChart" && (
|
||||||
<QuickSightChart url={widgetData[i]?.url} label={widgetMetaData.label} />
|
<QuickSightChart url={widgetData[i]?.url} label={widgetMetaData.label} />
|
||||||
@ -255,8 +300,18 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit
|
|||||||
widgetMetaData.type === "barChart" && (
|
widgetMetaData.type === "barChart" && (
|
||||||
<BarChart
|
<BarChart
|
||||||
color="info"
|
color="info"
|
||||||
title={widgetMetaData.label}
|
title={widgetData[i]?.title}
|
||||||
date={`As of ${new Date().toDateString()}`}
|
date={`As of ${new Date().toDateString()}`}
|
||||||
|
description={widgetData[i]?.description}
|
||||||
|
data={widgetData[i]?.chartData}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
widgetMetaData.type === "pieChart" && (
|
||||||
|
<PieChartCard
|
||||||
|
title={widgetData[i]?.label}
|
||||||
|
description={widgetData[i]?.description}
|
||||||
data={widgetData[i]?.chartData}
|
data={widgetData[i]?.chartData}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@ -270,14 +325,12 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
widgetMetaData.type === "horizontalBarChart" && (
|
widgetMetaData.type === "horizontalBarChart" && (
|
||||||
widgetData && widgetData[i] && widgetData[i].chartData && (
|
<HorizontalBarChart
|
||||||
<HorizontalBarChart
|
height={widgetData[i]?.height}
|
||||||
height={widgetData[i].height}
|
title={widgetMetaData.label}
|
||||||
title={widgetMetaData.label}
|
data={widgetData[i]?.chartData}
|
||||||
data={widgetData[i]?.chartData}
|
isCurrency={widgetData[i]?.isCurrency}
|
||||||
isCurrency={widgetData[i].isCurrency}
|
/>
|
||||||
/>
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
@ -38,12 +38,7 @@ import MDInput from "qqq/components/Temporary/MDInput";
|
|||||||
import MDPagination from "qqq/components/Temporary/MDPagination";
|
import MDPagination from "qqq/components/Temporary/MDPagination";
|
||||||
import MDTypography from "qqq/components/Temporary/MDTypography";
|
import MDTypography from "qqq/components/Temporary/MDTypography";
|
||||||
import ImageCell from "qqq/pages/dashboards/Tables/ImageCell";
|
import ImageCell from "qqq/pages/dashboards/Tables/ImageCell";
|
||||||
|
import {TableDataInput} from "qqq/pages/dashboards/Widgets/TableCard";
|
||||||
export interface TableDataInput
|
|
||||||
{
|
|
||||||
columns: { [key: string]: any }[];
|
|
||||||
rows: { [key: string]: any }[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props
|
interface Props
|
||||||
{
|
{
|
||||||
|
@ -29,7 +29,6 @@ import QContext from "QContext";
|
|||||||
import DashboardLayout from "qqq/components/DashboardLayout";
|
import DashboardLayout from "qqq/components/DashboardLayout";
|
||||||
import Footer from "qqq/components/Footer";
|
import Footer from "qqq/components/Footer";
|
||||||
import Navbar from "qqq/components/Navbar";
|
import Navbar from "qqq/components/Navbar";
|
||||||
import {TableDataInput} from "qqq/components/Temporary/DataTable";
|
|
||||||
import MDBox from "qqq/components/Temporary/MDBox";
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
import ShipmentsByWarehouseTable from "qqq/pages/dashboards/Tables/ShipmentsByWarehouseTable";
|
import ShipmentsByWarehouseTable from "qqq/pages/dashboards/Tables/ShipmentsByWarehouseTable";
|
||||||
import {GenericChartData} from "qqq/pages/dashboards/Widgets/Data/GenericChartData";
|
import {GenericChartData} from "qqq/pages/dashboards/Widgets/Data/GenericChartData";
|
||||||
@ -40,7 +39,7 @@ import {PieChartData} from "qqq/pages/dashboards/Widgets/PieChart";
|
|||||||
import PieChartCard from "qqq/pages/dashboards/Widgets/PieChartCard";
|
import PieChartCard from "qqq/pages/dashboards/Widgets/PieChartCard";
|
||||||
import SimpleStatisticsCard from "qqq/pages/dashboards/Widgets/SimpleStatisticsCard";
|
import SimpleStatisticsCard from "qqq/pages/dashboards/Widgets/SimpleStatisticsCard";
|
||||||
import {StatisticsCardData} from "qqq/pages/dashboards/Widgets/StatisticsCard";
|
import {StatisticsCardData} from "qqq/pages/dashboards/Widgets/StatisticsCard";
|
||||||
import TableCard from "qqq/pages/dashboards/Widgets/TableCard";
|
import TableCard, {TableDataInput} from "qqq/pages/dashboards/Widgets/TableCard";
|
||||||
import QClient from "qqq/utils/QClient";
|
import QClient from "qqq/utils/QClient";
|
||||||
|
|
||||||
const qController = QClient.getInstance();
|
const qController = QClient.getInstance();
|
||||||
|
@ -47,7 +47,6 @@ function Overview(): JSX.Element
|
|||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
// shipments by day widget data //
|
// shipments by day widget data //
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
const [shipmentsByDayTitle, setShipmentsByDayTitle] = useState("");
|
|
||||||
const [shipmentsByDayDescription, setShipmentsByDayDescription] = useState("");
|
const [shipmentsByDayDescription, setShipmentsByDayDescription] = useState("");
|
||||||
const [shipmentsByDayData, setShipmentsByDayData] = useState({} as GenericChartDataSingleDataset);
|
const [shipmentsByDayData, setShipmentsByDayData] = useState({} as GenericChartDataSingleDataset);
|
||||||
|
|
||||||
@ -135,7 +134,6 @@ function Overview(): JSX.Element
|
|||||||
}
|
}
|
||||||
|
|
||||||
const description = "Over the last week there have been <strong>" + daysOverAverage.toLocaleString() + (daysOverAverage == 1 ? " day" : " days") + "</strong> with total shipments greater than the daily average of <strong>" + average.toLocaleString() + " shipments</strong>.";
|
const description = "Over the last week there have been <strong>" + daysOverAverage.toLocaleString() + (daysOverAverage == 1 ? " day" : " days") + "</strong> with total shipments greater than the daily average of <strong>" + average.toLocaleString() + " shipments</strong>.";
|
||||||
setShipmentsByDayTitle(widgetData.title);
|
|
||||||
setShipmentsByDayData(widgetData.chartData);
|
setShipmentsByDayData(widgetData.chartData);
|
||||||
setShipmentsByDayDescription(description);
|
setShipmentsByDayDescription(description);
|
||||||
})();
|
})();
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
import Card from "@mui/material/Card";
|
import Card from "@mui/material/Card";
|
||||||
import Divider from "@mui/material/Divider";
|
import Divider from "@mui/material/Divider";
|
||||||
import Icon from "@mui/material/Icon";
|
import Icon from "@mui/material/Icon";
|
||||||
@ -142,44 +143,47 @@ function BarChart({color, title, description, date, data}: Props): JSX.Element
|
|||||||
const {chartData} = getChartData(data?.labels, data?.dataset);
|
const {chartData} = getChartData(data?.labels, data?.dataset);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card sx={{height: "100%"}}>
|
|
||||||
<MDBox padding="1rem">
|
<Box mt={3} sx={{flexGrow: 1, display: "flex"}}>
|
||||||
{useMemo(
|
<Card sx={{flexGrow: 1, display: "flex", height: "100%"}}>
|
||||||
() => (
|
<MDBox padding="1rem">
|
||||||
<MDBox
|
{useMemo(
|
||||||
variant="gradient"
|
() => (
|
||||||
bgColor={color}
|
<MDBox
|
||||||
borderRadius="lg"
|
variant="gradient"
|
||||||
coloredShadow={color}
|
bgColor={color}
|
||||||
py={2}
|
borderRadius="lg"
|
||||||
pr={0.5}
|
coloredShadow={color}
|
||||||
mt={-5}
|
py={2}
|
||||||
height="12.5rem"
|
pr={0.5}
|
||||||
>
|
mt={-5}
|
||||||
<Bar data={chartData} options={options} />
|
height="12.5rem"
|
||||||
|
>
|
||||||
|
<Bar data={chartData} options={options} />
|
||||||
|
</MDBox>
|
||||||
|
),
|
||||||
|
[data, color]
|
||||||
|
)}
|
||||||
|
<MDBox pt={3} pb={1} px={1}>
|
||||||
|
<MDTypography variant="h5" textTransform="capitalize">
|
||||||
|
{title}
|
||||||
|
</MDTypography>
|
||||||
|
<MDTypography component="div" variant="button" color="text" fontWeight="light">
|
||||||
|
{parse(description)}
|
||||||
|
</MDTypography>
|
||||||
|
<Divider />
|
||||||
|
<MDBox display="flex" alignItems="center">
|
||||||
|
<MDTypography variant="button" color="text" lineHeight={1} sx={{mt: 0.15, mr: 0.5}}>
|
||||||
|
<Icon>schedule</Icon>
|
||||||
|
</MDTypography>
|
||||||
|
<MDTypography variant="button" color="text" fontWeight="light">
|
||||||
|
{date}
|
||||||
|
</MDTypography>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
),
|
|
||||||
[data, color]
|
|
||||||
)}
|
|
||||||
<MDBox pt={3} pb={1} px={1}>
|
|
||||||
<MDTypography variant="h5" textTransform="capitalize">
|
|
||||||
{title}
|
|
||||||
</MDTypography>
|
|
||||||
<MDTypography component="div" variant="button" color="text" fontWeight="light">
|
|
||||||
{parse(description)}
|
|
||||||
</MDTypography>
|
|
||||||
<Divider />
|
|
||||||
<MDBox display="flex" alignItems="center">
|
|
||||||
<MDTypography variant="button" color="text" lineHeight={1} sx={{mt: 0.15, mr: 0.5}}>
|
|
||||||
<Icon>schedule</Icon>
|
|
||||||
</MDTypography>
|
|
||||||
<MDTypography variant="button" color="text" fontWeight="light">
|
|
||||||
{date}
|
|
||||||
</MDTypography>
|
|
||||||
</MDBox>
|
</MDBox>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
</MDBox>
|
</Card>
|
||||||
</Card>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ const options = {
|
|||||||
callbacks: {
|
callbacks: {
|
||||||
label: function(context:any)
|
label: function(context:any)
|
||||||
{
|
{
|
||||||
return(context.parsed.x);
|
return(" " + Number(context.parsed.y).toLocaleString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -90,7 +90,7 @@ const options = {
|
|||||||
},
|
},
|
||||||
callback: function(value: any, index: any, values: any)
|
callback: function(value: any, index: any, values: any)
|
||||||
{
|
{
|
||||||
return value;
|
return value.toLocaleString();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
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 {ReactNode, useMemo} from "react";
|
import {ReactNode, useMemo} from "react";
|
||||||
@ -48,7 +49,7 @@ const options = {
|
|||||||
return(context.parsed.x);
|
return(context.parsed.x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
y: {
|
y: {
|
||||||
@ -122,27 +123,30 @@ interface Props
|
|||||||
|
|
||||||
function HorizontalBarChart({icon, title, description, height, data, isCurrency}: Props): JSX.Element
|
function HorizontalBarChart({icon, title, description, height, data, isCurrency}: Props): JSX.Element
|
||||||
{
|
{
|
||||||
const chartDatasets = data.datasets
|
|
||||||
? data.datasets.map((dataset) => ({
|
|
||||||
...dataset,
|
|
||||||
weight: 5,
|
|
||||||
borderWidth: 0,
|
|
||||||
borderRadius: 4,
|
|
||||||
backgroundColor: dataset?.color
|
|
||||||
? dataset.color
|
|
||||||
: colors.info.main,
|
|
||||||
fill: false,
|
|
||||||
maxBarThickness: 15,
|
|
||||||
}))
|
|
||||||
: [];
|
|
||||||
|
|
||||||
let fullData = {};
|
let fullData = {};
|
||||||
if (data)
|
if(data && data.datasets)
|
||||||
{
|
{
|
||||||
fullData = {
|
const chartDatasets = data.datasets
|
||||||
labels: data.labels,
|
? data.datasets.map((dataset) => ({
|
||||||
datasets: chartDatasets
|
...dataset,
|
||||||
};
|
weight: 5,
|
||||||
|
borderWidth: 0,
|
||||||
|
borderRadius: 4,
|
||||||
|
backgroundColor: dataset?.color
|
||||||
|
? dataset.color
|
||||||
|
: colors.info.main,
|
||||||
|
fill: false,
|
||||||
|
maxBarThickness: 15,
|
||||||
|
}))
|
||||||
|
: [];
|
||||||
|
|
||||||
|
if (data)
|
||||||
|
{
|
||||||
|
fullData = {
|
||||||
|
labels: data.labels,
|
||||||
|
datasets: chartDatasets
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let customOptions = options;
|
let customOptions = options;
|
||||||
@ -166,10 +170,9 @@ function HorizontalBarChart({icon, title, description, height, data, isCurrency}
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const renderChart = (
|
const renderChart = (
|
||||||
<MDBox py={2} pr={2} pl={icon.component ? 1 : 2} sx={{alignItems: "stretch", flexGrow: 1, display: "flex", marginTop: "0px", paddingTop: "0px"}}>
|
<MDBox py={2} pr={2} pl={icon.component ? 1 : 2} sx={{alignItems: "stretch", flexGrow: 1, display: "flex", marginTop: "0px", paddingTop: "0px"}}>
|
||||||
{title || description ? (
|
{title || description && (
|
||||||
<MDBox display="flex" px={description ? 1 : 0} pt={description ? 1 : 0} sx={{alignItems: "stretch", flexGrow: 1, display: "flex", marginTop: "0px", paddingTop: "0px"}}>
|
<MDBox display="flex" px={description ? 1 : 0} pt={description ? 1 : 0} sx={{alignItems: "stretch", flexGrow: 1, display: "flex", marginTop: "0px", paddingTop: "0px"}}>
|
||||||
{icon.component && (
|
{icon.component && (
|
||||||
<MDBox
|
<MDBox
|
||||||
@ -198,11 +201,17 @@ function HorizontalBarChart({icon, title, description, height, data, isCurrency}
|
|||||||
</MDBox>
|
</MDBox>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
) : null}
|
)}
|
||||||
{useMemo(
|
{useMemo(
|
||||||
() => (
|
() => (
|
||||||
<MDBox height={height} sx={{alignItems: "stretch", flexGrow: 1, display: "flex", marginTop: "0px", paddingTop: "0px"}}>
|
<MDBox height={height} sx={{alignItems: "stretch", flexGrow: 1, display: "flex", marginTop: "0px", paddingTop: "0px"}}>
|
||||||
<Bar data={fullData} options={options} />
|
{
|
||||||
|
data && data?.datasets && data?.datasets.length > 0 ?(
|
||||||
|
<Bar data={fullData} options={options} />
|
||||||
|
):(
|
||||||
|
<Box mt={2} sx={{width: "100%", textAlign: "center"}}><i>No data was provided to this chart</i></Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
</MDBox>
|
</MDBox>
|
||||||
),
|
),
|
||||||
[data, height]
|
[data, height]
|
||||||
|
@ -21,13 +21,10 @@
|
|||||||
|
|
||||||
import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance";
|
import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance";
|
||||||
import {QWidgetMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QWidgetMetaData";
|
import {QWidgetMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QWidgetMetaData";
|
||||||
import {Box, Typography} from "@mui/material";
|
import {Box} from "@mui/material";
|
||||||
import Card from "@mui/material/Card";
|
|
||||||
import Grid from "@mui/material/Grid";
|
|
||||||
import React, {useEffect, useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import DashboardWidgets from "qqq/components/DashboardWidgets";
|
import DashboardWidgets from "qqq/components/DashboardWidgets";
|
||||||
import DropdownMenu from "qqq/pages/dashboards/Widgets/Components/DropdownMenu";
|
import Widget from "qqq/pages/dashboards/Widgets/Widget";
|
||||||
import Widget, {Dropdown, LabelComponent} from "qqq/pages/dashboards/Widgets/Widget";
|
|
||||||
import QClient from "qqq/utils/QClient";
|
import QClient from "qqq/utils/QClient";
|
||||||
|
|
||||||
|
|
||||||
@ -44,6 +41,7 @@ export interface ParentWidgetData
|
|||||||
}[][];
|
}[][];
|
||||||
childWidgetNameList: string[];
|
childWidgetNameList: string[];
|
||||||
dropdownNeedsSelectedText?: string;
|
dropdownNeedsSelectedText?: string;
|
||||||
|
icon?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -54,6 +52,7 @@ interface Props
|
|||||||
{
|
{
|
||||||
widgetIndex: number;
|
widgetIndex: number;
|
||||||
label: string;
|
label: string;
|
||||||
|
icon?: string;
|
||||||
data: ParentWidgetData;
|
data: ParentWidgetData;
|
||||||
reloadWidgetCallback?: (widgetIndex: number, params: string) => void;
|
reloadWidgetCallback?: (widgetIndex: number, params: string) => void;
|
||||||
entityPrimaryKey?: string;
|
entityPrimaryKey?: string;
|
||||||
@ -62,7 +61,7 @@ interface Props
|
|||||||
|
|
||||||
|
|
||||||
const qController = QClient.getInstance();
|
const qController = QClient.getInstance();
|
||||||
function ParentWidget({widgetIndex, label, data, reloadWidgetCallback, entityPrimaryKey, tableName}: Props, ): JSX.Element
|
function ParentWidget({widgetIndex, label, icon, data, reloadWidgetCallback, entityPrimaryKey, tableName}: Props, ): JSX.Element
|
||||||
{
|
{
|
||||||
const [childUrlParams, setChildUrlParams] = useState("");
|
const [childUrlParams, setChildUrlParams] = useState("");
|
||||||
const [qInstance, setQInstance] = useState(null as QInstance);
|
const [qInstance, setQInstance] = useState(null as QInstance);
|
||||||
@ -93,16 +92,19 @@ function ParentWidget({widgetIndex, label, data, reloadWidgetCallback, entityPri
|
|||||||
const parentReloadWidgetCallback = (data: string) =>
|
const parentReloadWidgetCallback = (data: string) =>
|
||||||
{
|
{
|
||||||
setChildUrlParams(data);
|
setChildUrlParams(data);
|
||||||
|
|
||||||
reloadWidgetCallback(widgetIndex, data);
|
reloadWidgetCallback(widgetIndex, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
return (
|
return (
|
||||||
<Widget
|
<Widget
|
||||||
|
icon={icon}
|
||||||
label={label}
|
label={label}
|
||||||
widgetData={data}
|
widgetData={data}
|
||||||
reloadWidgetCallback={parentReloadWidgetCallback}
|
reloadWidgetCallback={parentReloadWidgetCallback}
|
||||||
>
|
>
|
||||||
<Box px={3}>
|
<Box px={3} sx={{width: "100%"}}>
|
||||||
<DashboardWidgets widgetMetaDataList={widgets} entityPrimaryKey={entityPrimaryKey} tableName={tableName} childUrlParams={childUrlParams} areChildren={true}/>
|
<DashboardWidgets widgetMetaDataList={widgets} entityPrimaryKey={entityPrimaryKey} tableName={tableName} childUrlParams={childUrlParams} areChildren={true}/>
|
||||||
</Box>
|
</Box>
|
||||||
</Widget>
|
</Widget>
|
||||||
|
@ -48,8 +48,8 @@ function PieChartCard({title, description, data}: Props): JSX.Element
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card sx={{height: "100%"}}>
|
<Card sx={{height: "100%", width: "100%", display: "flex"}}>
|
||||||
<MDBox display="flex" justifyContent="space-between" alignItems="center" pt={2} px={2}>
|
<MDBox display="flex" pt={2} px={2}>
|
||||||
<MDTypography variant="h5">{title}</MDTypography>
|
<MDTypography variant="h5">{title}</MDTypography>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
<MDBox mt={3}>
|
<MDBox mt={3}>
|
||||||
@ -72,15 +72,19 @@ function PieChartCard({title, description, data}: Props): JSX.Element
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Grid container>
|
{
|
||||||
<Grid item xs={12}>
|
description && (
|
||||||
<MDBox pb={2} px={2} display="flex" flexDirection={{xs: "column", sm: "row"}} mt="auto">
|
<Grid container>
|
||||||
<MDTypography variant="button" color="text" fontWeight="light">
|
<Grid item xs={12}>
|
||||||
{parse(description)}
|
<MDBox pb={2} px={2} display="flex" flexDirection={{xs: "column", sm: "row"}} mt="auto">
|
||||||
</MDTypography>
|
<MDTypography variant="button" color="text" fontWeight="light">
|
||||||
</MDBox>
|
{parse(description)}
|
||||||
</Grid>
|
</MDTypography>
|
||||||
</Grid>
|
</MDBox>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
)
|
||||||
|
}
|
||||||
</MDBox>
|
</MDBox>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
@ -82,7 +82,7 @@ function SimpleStatisticsCard({title, data, increaseIsGood, isCurrency, dropdown
|
|||||||
</MDBox>
|
</MDBox>
|
||||||
<MDBox lineHeight={1}>
|
<MDBox lineHeight={1}>
|
||||||
{
|
{
|
||||||
count ? (
|
count !== undefined ? (
|
||||||
isCurrency ? (
|
isCurrency ? (
|
||||||
<MDTypography variant="h5" fontWeight="bold">
|
<MDTypography variant="h5" fontWeight="bold">
|
||||||
{count.toLocaleString("en-US", {style: "currency", currency: "USD"})}
|
{count.toLocaleString("en-US", {style: "currency", currency: "USD"})}
|
||||||
@ -90,21 +90,29 @@ function SimpleStatisticsCard({title, data, increaseIsGood, isCurrency, dropdown
|
|||||||
) : (
|
) : (
|
||||||
|
|
||||||
<MDTypography variant="h5" fontWeight="bold">
|
<MDTypography variant="h5" fontWeight="bold">
|
||||||
{count.toLocaleString("en-US", {style: "currency", currency: "USD"})}
|
{count.toLocaleString()}
|
||||||
</MDTypography>
|
</MDTypography>
|
||||||
)
|
)
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
<MDTypography variant="button" fontWeight="bold" color={percentColor}>
|
{
|
||||||
{percentageString}
|
count !== undefined ? (
|
||||||
<MDTypography
|
<MDTypography variant="button" fontWeight="bold" color={percentColor}>
|
||||||
variant="button"
|
{percentageString}
|
||||||
fontWeight="regular"
|
<MDTypography
|
||||||
color={"secondary"}
|
variant="button"
|
||||||
>
|
fontWeight="regular"
|
||||||
{percentageLabel}
|
color={"secondary"}
|
||||||
</MDTypography>
|
>
|
||||||
</MDTypography>
|
{percentageLabel}
|
||||||
|
</MDTypography>
|
||||||
|
</MDTypography>
|
||||||
|
):(
|
||||||
|
<MDTypography variant="button" fontWeight="regular">
|
||||||
|
<i>Loading.</i>
|
||||||
|
</MDTypography>
|
||||||
|
)
|
||||||
|
}
|
||||||
</MDBox>
|
</MDBox>
|
||||||
</Grid>
|
</Grid>
|
||||||
{dropdown && (
|
{dropdown && (
|
||||||
|
@ -19,9 +19,8 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
import Card from "@mui/material/Card";
|
import Card from "@mui/material/Card";
|
||||||
import Divider from "@mui/material/Divider";
|
|
||||||
import Icon from "@mui/material/Icon";
|
|
||||||
import parse from "html-react-parser";
|
import parse from "html-react-parser";
|
||||||
import {useMemo} from "react";
|
import {useMemo} from "react";
|
||||||
import {Line} from "react-chartjs-2";
|
import {Line} from "react-chartjs-2";
|
||||||
@ -57,47 +56,40 @@ function SmallLineChart({color, title, description, date, chart}: Props): JSX.El
|
|||||||
{
|
{
|
||||||
const {data, options} = configs(chart?.labels || [], chart?.dataset || {});
|
const {data, options} = configs(chart?.labels || [], chart?.dataset || {});
|
||||||
|
|
||||||
console.log(`DATA: ${JSON.stringify(data)}`);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card sx={{height: "100%"}}>
|
|
||||||
<MDBox padding="1rem">
|
<Box mt={3} sx={{flexGrow: 1, display: "flex"}}>
|
||||||
{useMemo(
|
<Card sx={{height: "100%"}}>
|
||||||
() => (
|
<MDBox padding="1rem">
|
||||||
<MDBox
|
|
||||||
variant="gradient"
|
{useMemo(
|
||||||
bgColor={color}
|
() => (
|
||||||
borderRadius="lg"
|
<MDBox
|
||||||
coloredShadow={color}
|
variant="gradient"
|
||||||
py={2}
|
bgColor={color}
|
||||||
pr={0.5}
|
borderRadius="lg"
|
||||||
mt={-5}
|
coloredShadow={color}
|
||||||
height="12.5rem"
|
py={2}
|
||||||
>
|
pr={0.5}
|
||||||
<Line data={data} options={options} />
|
mt={-5}
|
||||||
</MDBox>
|
height="12.5rem"
|
||||||
),
|
>
|
||||||
[chart, color]
|
<Line data={data} options={options} />
|
||||||
)}
|
</MDBox>
|
||||||
<MDBox pt={3} pb={1} px={1}>
|
),
|
||||||
<MDTypography variant="h5" textTransform="capitalize">
|
[chart, color]
|
||||||
{title}
|
)}
|
||||||
</MDTypography>
|
<MDBox pt={3} pb={1} px={1}>
|
||||||
<MDTypography component="div" variant="button" color="text" fontWeight="light">
|
<MDTypography variant="h5" textTransform="capitalize">
|
||||||
{parse(description)}
|
{title}
|
||||||
</MDTypography>
|
|
||||||
<Divider />
|
|
||||||
<MDBox display="flex" alignItems="center">
|
|
||||||
<MDTypography variant="button" color="text" lineHeight={1} sx={{mt: 0.15, mr: 0.5}}>
|
|
||||||
<Icon>schedule</Icon>
|
|
||||||
</MDTypography>
|
</MDTypography>
|
||||||
<MDTypography variant="button" color="text" fontWeight="light">
|
<MDTypography component="div" variant="button" color="text" fontWeight="light">
|
||||||
{date}
|
{parse(description)}
|
||||||
</MDTypography>
|
</MDTypography>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
</MDBox>
|
</Card>
|
||||||
</Card>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +88,8 @@ function StatisticsCard({data, color, icon, increaseIsGood}: Props): JSX.Element
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
|
||||||
|
<Card sx={{height: "100%", alignItems: "stretch", flexGrow: 1, display: "flex", marginTop: 3, paddingTop: "0px"}}>
|
||||||
<MDBox display="flex" justifyContent="space-between" pt={1} px={2}>
|
<MDBox display="flex" justifyContent="space-between" pt={1} px={2}>
|
||||||
<MDBox
|
<MDBox
|
||||||
variant="gradient"
|
variant="gradient"
|
||||||
@ -118,10 +119,10 @@ function StatisticsCard({data, color, icon, increaseIsGood}: Props): JSX.Element
|
|||||||
}
|
}
|
||||||
</MDBox>
|
</MDBox>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
<Divider />
|
|
||||||
{
|
{
|
||||||
percentageAmount !== undefined && percentageAmount !== 0 ? (
|
percentageAmount !== undefined && percentageAmount !== 0 ? (
|
||||||
<MDBox pb={2} px={2}>
|
<MDBox px={2}>
|
||||||
|
<Divider />
|
||||||
<MDTypography component="p" variant="button" color="text" display="flex">
|
<MDTypography component="p" variant="button" color="text" display="flex">
|
||||||
<MDTypography
|
<MDTypography
|
||||||
component="span"
|
component="span"
|
||||||
|
@ -19,24 +19,31 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Icon, Skeleton} from "@mui/material";
|
import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance";
|
||||||
import Card from "@mui/material/Card";
|
import {Skeleton} from "@mui/material";
|
||||||
import Grid from "@mui/material/Grid";
|
|
||||||
import Menu from "@mui/material/Menu";
|
|
||||||
import MenuItem from "@mui/material/MenuItem";
|
|
||||||
import Table from "@mui/material/Table";
|
import Table from "@mui/material/Table";
|
||||||
import TableBody from "@mui/material/TableBody";
|
import TableBody from "@mui/material/TableBody";
|
||||||
import TableContainer from "@mui/material/TableContainer";
|
import TableContainer from "@mui/material/TableContainer";
|
||||||
import TableRow from "@mui/material/TableRow";
|
import TableRow from "@mui/material/TableRow";
|
||||||
import parse from "html-react-parser";
|
import parse from "html-react-parser";
|
||||||
import React, {useEffect, useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import {NavLink} from "react-router-dom";
|
import DataTable from "qqq/components/Temporary/DataTable";
|
||||||
import DataTable, {TableDataInput} from "qqq/components/Temporary/DataTable";
|
|
||||||
import DataTableBodyCell from "qqq/components/Temporary/DataTable/DataTableBodyCell";
|
import DataTableBodyCell from "qqq/components/Temporary/DataTable/DataTableBodyCell";
|
||||||
import DataTableHeadCell from "qqq/components/Temporary/DataTable/DataTableHeadCell";
|
import DataTableHeadCell from "qqq/components/Temporary/DataTable/DataTableHeadCell";
|
||||||
import DefaultCell from "qqq/components/Temporary/DefaultCell";
|
import DefaultCell from "qqq/components/Temporary/DefaultCell";
|
||||||
import MDBox from "qqq/components/Temporary/MDBox";
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
import MDTypography from "qqq/components/Temporary/MDTypography";
|
import MDTypography from "qqq/components/Temporary/MDTypography";
|
||||||
|
import QClient from "qqq/utils/QClient";
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////
|
||||||
|
// structure of expected table data //
|
||||||
|
//////////////////////////////////////
|
||||||
|
export interface TableDataInput
|
||||||
|
{
|
||||||
|
columns: { [key: string]: any }[];
|
||||||
|
rows: { [key: string]: any }[];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////
|
/////////////////////////
|
||||||
@ -49,130 +56,39 @@ interface Props
|
|||||||
linkURL?: string;
|
linkURL?: string;
|
||||||
noRowsFoundHTML?: string;
|
noRowsFoundHTML?: string;
|
||||||
data: TableDataInput;
|
data: TableDataInput;
|
||||||
dropdownOptions?: {
|
reloadWidgetCallback?: (params: string) => void;
|
||||||
id: string,
|
|
||||||
name: string
|
|
||||||
}[];
|
|
||||||
reloadWidgetCallback?: (widgetIndex: number, params: string) => void;
|
|
||||||
widgetIndex?: number;
|
widgetIndex?: number;
|
||||||
isChild?: boolean;
|
isChild?: boolean;
|
||||||
|
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
function TableCard({title, linkText, linkURL, noRowsFoundHTML, data, dropdownOptions, reloadWidgetCallback, widgetIndex, isChild}: Props): JSX.Element
|
const qController = QClient.getInstance();
|
||||||
|
function TableCard({noRowsFoundHTML, data, reloadWidgetCallback}: Props): JSX.Element
|
||||||
{
|
{
|
||||||
const openArrowIcon = "arrow_drop_down";
|
const [qInstance, setQInstance] = useState(null as QInstance);
|
||||||
const closeArrowIcon = "arrow_drop_up";
|
|
||||||
const [dropdown, setDropdown] = useState<string | null>(null);
|
|
||||||
const [dropdownValue, setDropdownValue] = useState("");
|
|
||||||
const [dropdownLabel, setDropdownLabel] = useState<string>("");
|
|
||||||
const [dropdownIcon, setDropdownIcon] = useState<string>(openArrowIcon);
|
|
||||||
|
|
||||||
// console.log(`data: ${JSON.stringify(data?.rows)}`);
|
|
||||||
// console.log(`bool: ${data && data?.columns && !data?.rows}`);
|
|
||||||
// console.log(`norowsfound: ${noRowsFoundHTML}`);
|
|
||||||
|
|
||||||
const openDropdown = ({currentTarget}: any) =>
|
|
||||||
{
|
|
||||||
setDropdown(currentTarget);
|
|
||||||
setDropdownIcon(closeArrowIcon);
|
|
||||||
};
|
|
||||||
const closeDropdown = ({currentTarget}: any) =>
|
|
||||||
{
|
|
||||||
setDropdown(null);
|
|
||||||
setDropdownValue(currentTarget.innerText || dropdownValue);
|
|
||||||
setDropdownIcon(openArrowIcon);
|
|
||||||
reloadWidgetCallback(widgetIndex, null);
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderMenu = (state: any, open: any, close: any, icon: string) => (
|
|
||||||
dropdownOptions && (
|
|
||||||
<span style={{whiteSpace: "nowrap"}}>
|
|
||||||
<Icon onClick={open} fontSize={"medium"} style={{cursor: "pointer", float: "right"}}>{icon}</Icon>
|
|
||||||
<Menu
|
|
||||||
anchorEl={state}
|
|
||||||
transformOrigin={{vertical: "top", horizontal: "center"}}
|
|
||||||
open={Boolean(state)}
|
|
||||||
onClose={close}
|
|
||||||
keepMounted
|
|
||||||
disableAutoFocusItem
|
|
||||||
>
|
|
||||||
{
|
|
||||||
dropdownOptions.map((optionMap, index: number) =>
|
|
||||||
<MenuItem id={optionMap["id"]} key={index} onClick={close}>{optionMap["name"]}</MenuItem>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</Menu>
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
if (dropdownOptions)
|
(async () =>
|
||||||
{
|
{
|
||||||
setDropdownValue(dropdownOptions[0]["id"]);
|
const newQInstance = await qController.loadMetaData();
|
||||||
setDropdownLabel(dropdownOptions[0]["name"]);
|
setQInstance(newQInstance);
|
||||||
|
})();
|
||||||
}
|
}, []);
|
||||||
}, [dropdownOptions]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card sx={{alignItems: "stretch", flexGrow: 1, display: "flex", marginTop: "0px", paddingTop: "0px"}}>
|
<MDBox py={1}>
|
||||||
<Grid container>
|
{
|
||||||
<Grid item xs={6}>
|
data && data.columns && !noRowsFoundHTML ?
|
||||||
<MDBox pt={3} px={3}>
|
<DataTable
|
||||||
<MDTypography variant={isChild ? "h6" : "h5"} fontWeight="medium">
|
table={data}
|
||||||
{title}
|
entriesPerPage={false}
|
||||||
</MDTypography>
|
showTotalEntries={false}
|
||||||
</MDBox>
|
isSorted={false}
|
||||||
</Grid>
|
noEndBorder
|
||||||
<Grid item xs={6}>
|
/>
|
||||||
{
|
: noRowsFoundHTML ?
|
||||||
linkText && (
|
|
||||||
<MDBox sx={{textAlign: "right", fontSize: "14px"}} pt={3} px={3}>
|
|
||||||
<NavLink to={linkURL}>{linkText}</NavLink>
|
|
||||||
</MDBox>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={5}>
|
|
||||||
{dropdownOptions && (
|
|
||||||
<MDBox p={2} width="100%" textAlign="right" lineHeight={1}>
|
|
||||||
<MDTypography
|
|
||||||
variant="caption"
|
|
||||||
color="secondary"
|
|
||||||
fontWeight="regular"
|
|
||||||
>
|
|
||||||
<strong>Billing Period: </strong>
|
|
||||||
</MDTypography>
|
|
||||||
<MDTypography
|
|
||||||
variant="caption"
|
|
||||||
color="secondary"
|
|
||||||
fontWeight="regular"
|
|
||||||
sx={{cursor: "pointer"}}
|
|
||||||
onClick={openDropdown}
|
|
||||||
>
|
|
||||||
{dropdownLabel}
|
|
||||||
</MDTypography>
|
|
||||||
{renderMenu(dropdown, openDropdown, closeDropdown, dropdownIcon)}
|
|
||||||
</MDBox>
|
|
||||||
)}
|
|
||||||
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
<MDBox py={1}>
|
|
||||||
{
|
|
||||||
data && data.columns && !noRowsFoundHTML ? (
|
|
||||||
<DataTable
|
|
||||||
table={data}
|
|
||||||
entriesPerPage={false}
|
|
||||||
showTotalEntries={false}
|
|
||||||
isSorted={false}
|
|
||||||
noEndBorder
|
|
||||||
/>
|
|
||||||
) : noRowsFoundHTML ? (
|
|
||||||
<MDBox p={3} pt={1} pb={1} sx={{textAlign: "center"}}>
|
<MDBox p={3} pt={1} pb={1} sx={{textAlign: "center"}}>
|
||||||
<MDTypography
|
<MDTypography
|
||||||
variant="subtitle2"
|
variant="subtitle2"
|
||||||
@ -186,7 +102,7 @@ function TableCard({title, linkText, linkURL, noRowsFoundHTML, data, dropdownOpt
|
|||||||
}
|
}
|
||||||
</MDTypography>
|
</MDTypography>
|
||||||
</MDBox>
|
</MDBox>
|
||||||
) : (
|
:
|
||||||
<TableContainer sx={{boxShadow: "none"}}>
|
<TableContainer sx={{boxShadow: "none"}}>
|
||||||
<Table>
|
<Table>
|
||||||
<MDBox component="thead">
|
<MDBox component="thead">
|
||||||
@ -211,10 +127,8 @@ function TableCard({title, linkText, linkURL, noRowsFoundHTML, data, dropdownOpt
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</TableContainer>
|
</TableContainer>
|
||||||
)
|
}
|
||||||
}
|
</MDBox>
|
||||||
</MDBox>
|
|
||||||
</Card>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
146
src/qqq/pages/dashboards/Widgets/USMapWidget.tsx
Normal file
146
src/qqq/pages/dashboards/Widgets/USMapWidget.tsx
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
/*
|
||||||
|
* 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 {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance";
|
||||||
|
import {Box, Grid} from "@mui/material";
|
||||||
|
import {VectorMap} from "@react-jvectormap/core";
|
||||||
|
import {usAea} from "@react-jvectormap/unitedstates";
|
||||||
|
import React, {useEffect, useState} from "react";
|
||||||
|
import QClient from "qqq/utils/QClient";
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
// structure of expected US and A widget data //
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
export interface MapMarkerData
|
||||||
|
{
|
||||||
|
name: string;
|
||||||
|
latitude: number;
|
||||||
|
longitude: number;
|
||||||
|
}
|
||||||
|
export interface USMapWidgetData
|
||||||
|
{
|
||||||
|
height: string;
|
||||||
|
markers?: MapMarkerData[];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////
|
||||||
|
// define properties and defaults //
|
||||||
|
////////////////////////////////////
|
||||||
|
interface Props
|
||||||
|
{
|
||||||
|
widgetIndex: number;
|
||||||
|
label: string;
|
||||||
|
icon?: string;
|
||||||
|
reloadWidgetCallback?: (widgetIndex: number, params: string) => void;
|
||||||
|
data: USMapWidgetData;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const qController = QClient.getInstance();
|
||||||
|
function USMapWidget(props: Props, ): JSX.Element
|
||||||
|
{
|
||||||
|
const [qInstance, setQInstance] = useState(null as QInstance);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
(async () =>
|
||||||
|
{
|
||||||
|
const newQInstance = await qController.loadMetaData();
|
||||||
|
setQInstance(newQInstance);
|
||||||
|
})();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
console.log(JSON.stringify(props))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid container>
|
||||||
|
<Grid item xs={12} sx={{height: props.data?.height}}>
|
||||||
|
{
|
||||||
|
props.data?.height && (
|
||||||
|
<Box mt={3} sx={{height: "100%"}}>
|
||||||
|
<VectorMap
|
||||||
|
map={usAea}
|
||||||
|
zoomOnScroll={false}
|
||||||
|
zoomButtons={false}
|
||||||
|
markersSelectable
|
||||||
|
backgroundColor="transparent"
|
||||||
|
markers={[
|
||||||
|
{
|
||||||
|
name: "edison",
|
||||||
|
latLng: [40.5274, -74.3933],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stockton",
|
||||||
|
latLng: [37.975556, -121.300833],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "patterson",
|
||||||
|
latLng: [37.473056, -121.132778],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
regionStyle={{
|
||||||
|
initial: {
|
||||||
|
fill: "#dee2e7",
|
||||||
|
"fill-opacity": 1,
|
||||||
|
stroke: "none",
|
||||||
|
"stroke-width": 0,
|
||||||
|
"stroke-opacity": 0,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
markerStyle={{
|
||||||
|
initial: {
|
||||||
|
fill: "#e91e63",
|
||||||
|
stroke: "#ffffff",
|
||||||
|
"stroke-width": 5,
|
||||||
|
"stroke-opacity": 0.5,
|
||||||
|
r: 7,
|
||||||
|
},
|
||||||
|
hover: {
|
||||||
|
fill: "E91E63",
|
||||||
|
stroke: "#ffffff",
|
||||||
|
"stroke-width": 5,
|
||||||
|
"stroke-opacity": 0.5,
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
fill: "E91E63",
|
||||||
|
stroke: "#ffffff",
|
||||||
|
"stroke-width": 5,
|
||||||
|
"stroke-opacity": 0.5,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
marginTop: "-1.5rem",
|
||||||
|
}}
|
||||||
|
onRegionTipShow={() => false}
|
||||||
|
onMarkerTipShow={() => false}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default USMapWidget;
|
@ -23,19 +23,18 @@ import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QT
|
|||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
import Card from "@mui/material/Card";
|
import Card from "@mui/material/Card";
|
||||||
import Modal from "@mui/material/Modal";
|
import Icon from "@mui/material/Icon";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import React, {useEffect, useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import {Link, useNavigate} from "react-router-dom";
|
import {Link, useNavigate} from "react-router-dom";
|
||||||
import DashboardWidgets from "qqq/components/DashboardWidgets";
|
import colors from "qqq/components/Temporary/colors";
|
||||||
import EntityForm from "qqq/components/EntityForm";
|
|
||||||
import DropdownMenu, {DropdownOption} from "qqq/pages/dashboards/Widgets/Components/DropdownMenu";
|
import DropdownMenu, {DropdownOption} from "qqq/pages/dashboards/Widgets/Components/DropdownMenu";
|
||||||
|
|
||||||
export interface WidgetData
|
export interface WidgetData
|
||||||
{
|
{
|
||||||
dropdownLabelList: string[];
|
dropdownLabelList?: string[];
|
||||||
dropdownNameList: string[];
|
dropdownNameList?: string[];
|
||||||
dropdownDataList: {
|
dropdownDataList?: {
|
||||||
id: string,
|
id: string,
|
||||||
label: string
|
label: string
|
||||||
}[][];
|
}[][];
|
||||||
@ -45,15 +44,20 @@ export interface WidgetData
|
|||||||
|
|
||||||
interface Props
|
interface Props
|
||||||
{
|
{
|
||||||
|
icon?: string;
|
||||||
label: string;
|
label: string;
|
||||||
labelAdditionalComponentsLeft: LabelComponent[];
|
labelAdditionalComponentsLeft: LabelComponent[];
|
||||||
labelAdditionalComponentsRight: LabelComponent[];
|
labelAdditionalComponentsRight: LabelComponent[];
|
||||||
widgetData?: WidgetData;
|
widgetData?: WidgetData;
|
||||||
children: JSX.Element;
|
children: JSX.Element;
|
||||||
reloadWidgetCallback?: (params: string) => void;
|
reloadWidgetCallback?: (params: string) => void;
|
||||||
|
isChild?: boolean;
|
||||||
|
isCard?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget.defaultProps = {
|
Widget.defaultProps = {
|
||||||
|
isCard: true,
|
||||||
|
isChild: false,
|
||||||
label: null,
|
label: null,
|
||||||
widgetData: {},
|
widgetData: {},
|
||||||
labelAdditionalComponentsLeft: [],
|
labelAdditionalComponentsLeft: [],
|
||||||
@ -246,44 +250,70 @@ function Widget(props: React.PropsWithChildren<Props>): JSX.Element
|
|||||||
}
|
}
|
||||||
}, [counter]);
|
}, [counter]);
|
||||||
|
|
||||||
return (
|
const widgetContent =
|
||||||
<>
|
<Box sx={{width: "100%"}}>
|
||||||
<Card sx={{width: "100%"}}>
|
{
|
||||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
(props.icon || props.label) && (
|
||||||
<Box py={2}>
|
<Box display="flex" justifyContent="space-between" alignItems="center" sx={{width: "100%"}}>
|
||||||
<Typography variant="h5" fontWeight="medium" p={3} display="inline">
|
<Box py={2}>
|
||||||
{props.label}
|
|
||||||
</Typography>
|
|
||||||
{
|
|
||||||
props.labelAdditionalComponentsLeft.map((component, i) =>
|
|
||||||
{
|
{
|
||||||
return (<span key={i}>{renderComponent(component)}</span>);
|
props.icon && (
|
||||||
})
|
<Box
|
||||||
}
|
ml={3}
|
||||||
</Box>
|
mt={-4}
|
||||||
<Box pr={1}>
|
sx={{
|
||||||
{
|
display: "flex",
|
||||||
effectiveLabelAdditionalComponentsRight.map((component, i) =>
|
justifyContent: "center",
|
||||||
{
|
alignItems: "center",
|
||||||
return (<span key={i}>{renderComponent(component)}</span>);
|
width: "64px",
|
||||||
})
|
height: "64px",
|
||||||
}
|
borderRadius: "8px",
|
||||||
</Box>
|
background: colors.info.main,
|
||||||
</Box>
|
color: "#ffffff",
|
||||||
{
|
float: "left"
|
||||||
props.widgetData?.dropdownNeedsSelectedText ? (
|
}}
|
||||||
<Box pb={3} pr={3} sx={{width: "100%", textAlign: "right"}}>
|
>
|
||||||
<Typography variant="body2">
|
<Icon fontSize="medium" color="inherit">
|
||||||
{props.widgetData?.dropdownNeedsSelectedText}
|
{props.icon}
|
||||||
|
</Icon>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<Typography variant={props.isChild ? "h6" : "h5"} fontWeight="medium" p={3} display="inline">
|
||||||
|
{props.label}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
{
|
||||||
|
props.labelAdditionalComponentsLeft.map((component, i) =>
|
||||||
|
{
|
||||||
|
return (<span key={i}>{renderComponent(component)}</span>);
|
||||||
|
})
|
||||||
|
}
|
||||||
</Box>
|
</Box>
|
||||||
) : (
|
<Box pr={1}>
|
||||||
props.children
|
{
|
||||||
)
|
effectiveLabelAdditionalComponentsRight.map((component, i) =>
|
||||||
}
|
{
|
||||||
</Card>
|
return (<span key={i}>{renderComponent(component)}</span>);
|
||||||
</>
|
})
|
||||||
);
|
}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
props.widgetData?.dropdownNeedsSelectedText ? (
|
||||||
|
<Box pb={3} pr={3} sx={{width: "100%", textAlign: "right"}}>
|
||||||
|
<Typography variant="body2">
|
||||||
|
{props.widgetData?.dropdownNeedsSelectedText}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
) : (
|
||||||
|
props.children
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</Box>;
|
||||||
|
|
||||||
|
return props.isCard ? <Card sx={{width: "100%"}}>{widgetContent}</Card> : widgetContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Widget;
|
export default Widget;
|
||||||
|
Reference in New Issue
Block a user