From 47fca5243761796fc72f96a98e2d2e2426723be6 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Fri, 20 Oct 2023 10:22:54 -0500 Subject: [PATCH] CE-604 Add topRightInsideCardIcon as a right-component and chartSubheaderData in StackedBarChart and PieChart; Add support for tabs; --- .../MaterialDashboardIconRoleNames.java | 31 ++++ .../components/widgets/DashboardWidgets.tsx | 132 ++++++++++++------ 2 files changed, 124 insertions(+), 39 deletions(-) create mode 100644 src/main/java/com/kingsrook/qqq/frontend/materialdashboard/model/metadata/MaterialDashboardIconRoleNames.java diff --git a/src/main/java/com/kingsrook/qqq/frontend/materialdashboard/model/metadata/MaterialDashboardIconRoleNames.java b/src/main/java/com/kingsrook/qqq/frontend/materialdashboard/model/metadata/MaterialDashboardIconRoleNames.java new file mode 100644 index 0000000..6d1377d --- /dev/null +++ b/src/main/java/com/kingsrook/qqq/frontend/materialdashboard/model/metadata/MaterialDashboardIconRoleNames.java @@ -0,0 +1,31 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2023. 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 . + */ + +package com.kingsrook.qqq.frontend.materialdashboard.model.metadata; + + +/******************************************************************************* + ** + *******************************************************************************/ +public interface MaterialDashboardIconRoleNames +{ + String TOP_RIGHT_INSIDE_CARD = "topRightInsideCard"; +} diff --git a/src/qqq/components/widgets/DashboardWidgets.tsx b/src/qqq/components/widgets/DashboardWidgets.tsx index 057f02c..edd577c 100644 --- a/src/qqq/components/widgets/DashboardWidgets.tsx +++ b/src/qqq/components/widgets/DashboardWidgets.tsx @@ -22,11 +22,14 @@ import {QWidgetMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/Q import {Skeleton} from "@mui/material"; import Box from "@mui/material/Box"; import Grid from "@mui/material/Grid"; +import Tab from "@mui/material/Tab"; +import Tabs from "@mui/material/Tabs"; import parse from "html-react-parser"; import React, {useContext, useEffect, useReducer, useState} from "react"; import {useLocation} from "react-router-dom"; import QContext from "QContext"; import MDTypography from "qqq/components/legacy/MDTypography"; +import TabPanel from "qqq/components/misc/TabPanel"; import BarChart from "qqq/components/widgets/charts/barchart/BarChart"; import HorizontalBarChart from "qqq/components/widgets/charts/barchart/HorizontalBarChart"; import DefaultLineChart from "qqq/components/widgets/charts/linechart/DefaultLineChart"; @@ -44,7 +47,7 @@ import USMapWidget from "qqq/components/widgets/misc/USMapWidget"; import ParentWidget from "qqq/components/widgets/ParentWidget"; import MultiStatisticsCard from "qqq/components/widgets/statistics/MultiStatisticsCard"; import StatisticsCard from "qqq/components/widgets/statistics/StatisticsCard"; -import Widget, {WIDGET_DROPDOWN_SELECTION_LOCAL_STORAGE_KEY_ROOT} from "qqq/components/widgets/Widget"; +import Widget, {HeaderIcon, WIDGET_DROPDOWN_SELECTION_LOCAL_STORAGE_KEY_ROOT, LabelComponent} from "qqq/components/widgets/Widget"; import ProcessRun from "qqq/pages/processes/ProcessRun"; import Client from "qqq/utils/qqq/Client"; import TableWidget from "./tables/TableWidget"; @@ -58,9 +61,10 @@ interface Props tableName?: string; entityPrimaryKey?: string; omitWrappingGridContainer: boolean; - areChildren?: boolean - childUrlParams?: string - parentWidgetMetaData?: QWidgetMetaData + areChildren?: boolean; + childUrlParams?: string; + parentWidgetMetaData?: QWidgetMetaData; + wrapWidgetsInTabPanels: boolean; } DashboardWidgets.defaultProps = { @@ -70,10 +74,11 @@ DashboardWidgets.defaultProps = { omitWrappingGridContainer: false, areChildren: false, childUrlParams: "", - parentWidgetMetaData: null + parentWidgetMetaData: null, + wrapWidgetsInTabPanels: false, }; -function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omitWrappingGridContainer, areChildren, childUrlParams, parentWidgetMetaData}: Props): JSX.Element +function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omitWrappingGridContainer, areChildren, childUrlParams, parentWidgetMetaData, wrapWidgetsInTabPanels}: Props): JSX.Element { const location = useLocation(); const [widgetData, setWidgetData] = useState([] as any[]); @@ -84,6 +89,13 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit const [haveLoadedParams, setHaveLoadedParams] = useState(false); const {accentColor} = useContext(QContext); + const [selectedTab, setSelectedTab] = useState(0); + + const changeTab = (newValue: number) => + { + setSelectedTab(newValue); + }; + useEffect(() => { setWidgetData([]); @@ -102,15 +114,15 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit widgetData[i] = await qController.widget(widgetMetaData.name, urlParams); setWidgetData(widgetData); setWidgetCounter(widgetCounter + 1); - if(widgetData[i]) + if (widgetData[i]) { widgetData[i]["errorLoading"] = false; } } - catch(e) + catch (e) { console.error(e); - if(widgetData[i]) + if (widgetData[i]) { widgetData[i]["errorLoading"] = true; } @@ -123,7 +135,7 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit const reloadWidget = async (index: number, data: string) => { - (async() => + (async () => { const urlParams = getQueryParams(widgetMetaDataList[index], data); setCurrentUrlParams(urlParams); @@ -140,7 +152,7 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit widgetData[index]["errorLoading"] = false; } } - catch(e) + catch (e) { console.error(e); if (widgetData[index]) @@ -151,7 +163,7 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit forceUpdate(); })(); - } + }; function getQueryParams(widgetMetaData: QWidgetMetaData, extraParams: string): string { @@ -178,36 +190,36 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit } } - if(entityPrimaryKey) + if (entityPrimaryKey) { paramMap.set("id", entityPrimaryKey); } - if(tableName) + if (tableName) { paramMap.set("tableName", tableName); } - if(extraParams) + if (extraParams) { let pairs = extraParams.split("&"); for (let i = 0; i < pairs.length; i++) { let nameValue = pairs[i].split("="); - if(nameValue.length == 2) + if (nameValue.length == 2) { paramMap.set(nameValue[0], nameValue[1]); } } } - if(childUrlParams) + if (childUrlParams) { let pairs = childUrlParams.split("&"); for (let i = 0; i < pairs.length; i++) { let nameValue = pairs[i].split("="); - if(nameValue.length == 2) + if (nameValue.length == 2) { paramMap.set(nameValue[0], nameValue[1]); } @@ -227,6 +239,16 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit const renderWidget = (widgetMetaData: QWidgetMetaData, i: number): JSX.Element => { + const labelAdditionalComponentsRight: LabelComponent[] = []; + if (widgetMetaData && widgetMetaData.icons) + { + const topRightInsideCardIcon = widgetMetaData.icons.get("topRightInsideCard"); + if (topRightInsideCardIcon) + { + labelAdditionalComponentsRight.push(new HeaderIcon(topRightInsideCardIcon.name, topRightInsideCardIcon.color)); + } + } + return ( { @@ -270,8 +292,9 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit widgetData={widgetData[i]} reloadWidgetCallback={(data) => reloadWidget(i, data)} isChild={areChildren} + labelAdditionalComponentsRight={labelAdditionalComponentsRight} > - + ) } @@ -381,10 +404,12 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit widgetData={widgetData[i]} reloadWidgetCallback={(data) => reloadWidget(i, data)} isChild={areChildren} + labelAdditionalComponentsRight={labelAdditionalComponentsRight} >
@@ -436,11 +461,11 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit { widgetMetaData.type === "fieldValueList" && ( widgetData && widgetData[i] && - reloadWidget(i, data)} - /> + reloadWidget(i, data)} + /> ) } { @@ -461,32 +486,61 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit }
); - } + }; const body: JSX.Element = ( <> { - widgetMetaDataList.map((widgetMetaData, i) => ( - omitWrappingGridContainer - ? widgetMetaData && renderWidget(widgetMetaData, i) - : - widgetMetaData && - {renderWidget(widgetMetaData, i)} - - )) + widgetMetaDataList.map((widgetMetaData, i) => + { + let renderedWidget = widgetMetaData ? renderWidget(widgetMetaData, i) : (<>); + + if (!omitWrappingGridContainer) + { + renderedWidget = ( + {renderedWidget} + ); + } + + if (wrapWidgetsInTabPanels) + { + renderedWidget = ( + {renderedWidget} + ); + } + + return ({renderedWidget}) + }) } ); + const tabs = widgetMetaDataList && wrapWidgetsInTabPanels ? + changeTab(newValue)} + variant="standard" + > + {widgetMetaDataList.map((widgetMetaData, i) => ( + + ))} + + : <> + return ( widgetCount > 0 ? ( - omitWrappingGridContainer ? body : - ( - - {body} - - ) + <> + {tabs} + { + omitWrappingGridContainer ? body : ( + + {body} + + ) + } + ) : null ); }