From 630620b65fc53c8b7062ef95ad533848c4736c1f Mon Sep 17 00:00:00 2001 From: Tim Chamberlain Date: Thu, 15 Dec 2022 10:07:27 -0600 Subject: [PATCH] SPRINT-17: updated some widgets to look less broken when data is 'not available now', checkpoint commit on 'real dashboards' --- package.json | 2 +- public/flags/AU.png | Bin 0 -> 1253 bytes public/flags/BR.png | Bin 0 -> 1172 bytes public/flags/DE.png | Bin 0 -> 927 bytes public/flags/GB.png | Bin 0 -> 1376 bytes public/flags/US.png | Bin 0 -> 1157 bytes src/App.tsx | 24 ++- src/qqq/components/DashboardWidgets.tsx | 99 ++++++++--- .../components/Temporary/DataTable/index.tsx | 7 +- .../pages/dashboards/CarrierPerformance.tsx | 3 +- src/qqq/pages/dashboards/Overview.tsx | 2 - src/qqq/pages/dashboards/Widgets/BarChart.tsx | 74 ++++---- .../dashboards/Widgets/DefaultLineChart.tsx | 4 +- .../dashboards/Widgets/HorizontalBarChart.tsx | 57 ++++--- .../pages/dashboards/Widgets/ParentWidget.tsx | 16 +- .../pages/dashboards/Widgets/PieChartCard.tsx | 26 +-- .../Widgets/SimpleStatisticsCard.tsx | 32 ++-- .../dashboards/Widgets/SmallLineChart.tsx | 68 ++++---- .../dashboards/Widgets/StatisticsCard.tsx | 7 +- .../pages/dashboards/Widgets/TableCard.tsx | 160 ++++-------------- .../pages/dashboards/Widgets/USMapWidget.tsx | 146 ++++++++++++++++ src/qqq/pages/dashboards/Widgets/Widget.tsx | 112 +++++++----- 22 files changed, 507 insertions(+), 332 deletions(-) create mode 100644 public/flags/AU.png create mode 100644 public/flags/BR.png create mode 100644 public/flags/DE.png create mode 100644 public/flags/GB.png create mode 100644 public/flags/US.png create mode 100644 src/qqq/pages/dashboards/Widgets/USMapWidget.tsx diff --git a/package.json b/package.json index 86cf182..52c73bf 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "@fullcalendar/interaction": "5.10.0", "@fullcalendar/react": "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/material": "5.4.1", "@mui/styled-engine": "5.4.1", diff --git a/public/flags/AU.png b/public/flags/AU.png new file mode 100644 index 0000000000000000000000000000000000000000..13fd0898d193f03cb49e9a9d55b5e35a8465e0a7 GIT binary patch literal 1253 zcmVPx(pGibPR5%fpR9$S7RTzHG_y4VH%hocW?KYIbVmpRmf`CZWOo%ZO6GQw-&~RhC zP`Tk+x$#18jPVL@5EG1H36Ur~sw~|3{T>ql zn>={1KelVDa;STxcxKNgTO0-^K0U!Q6VURNAaeXlF&0g)?md`6YQRv3kKbvo`)$D) z6po*=JvU)lcHC~*G9>OqOl`?D_1hlz_Or3E_tn9a{>3|^soj~is~1kxjMBNO1^Xkp zU~HFLiEp|$#PZR;_<^*NNX4|?(~X$=(_clD?=8Xi1JvpbJTsg`?uixn{^Xx1H$(LH z#i7IRS1vh|tX13+J6({n@5cS?YyG`2R&GJ;=N}O%6tMaA7ct%w#r8;m3v~wd+I5UB zS~wG$2;0wQtV1m4y!{27hj3gELIgrNJUtC1&Aez>#d32Hx#BzyjOO4n2hGJQp1*Y& z5xU@2>exhUhi~DmxfZ{uPeBl%rpmZ7Q^eHOJS^J>G?@x;v>ZU0&gHPn(=k?>gPy+$ zOI6W*>I^h6YD9rKbs0=ifkYDX14Eb=IwoxcazlVaK$aM6dV=Nn5VnU`$8#9$Pk=WY zP$rPYc}ZYLZV3A}r?HLIVHS$8XA21Lm$3Bqn@DY3kL0~M+%0qbabL!H$AKZacx&&| zc(7E(cc(6aNfLw~qO{yXG8vR{=$Mv}quFWmOU?G$oRgj8#BmxH~ zn+7Hp70i%W$IyhXN@&)bID2CTlQP56ftA=g(2Z)%f)s}M^h(G}?PWtlEFOWRxETexEW&C4tKmX1WEgZP2;^LM5{YyVf_e)|rz;7%uFyF> zRS*%hXwzAYMlnJzvAF_H0zV3=vynI?qClbpj|QXdfDiYM;M+svc<+@FdM-mi1>0b4 z(1*ALOxCttAA@V+_;~*|JeiJzy8->)E=qKFKHL89Y=9u-Fvz=mo*Tl>E$gsx!z$wb zp?4pqP$mrvBbulx6_Q};8U|LaAW`)Wr1%ewl%QaYjLfJn2AQ}Nbos*7d1_3;*^9HV zy%3VzPNf_{o$!G4@(?&JsA1E+G4sIvq~ z*J0ZXUmiOLg&Y!)>LMgPgkST7UjX;+iqM~Px(PDw;TR5%fpQ(b5rRTTd2+_|$e``=BPO_Qd7XdBZX0b%o5kKJWZU=wd# z8X50DVw^3HtxaOm!xOJ5OpmcIEi3nvw^49KRQ9A-TTt<^FXd=a4~~GTLtYXMIbm3H z1GnP(K38$#TY7GzXqa~bg^LVR6QY{0yg7Jamo}6$2tT~6g|8=yo8b-;WN7rpSJt)d!$1s4LV7yF%1$LlvIe~2r8Dek_OkpAdW%W zVKd%FOS-TrHzOB|p2UNNO}ss@g#FDj!kY*2bRhA;$X!fUZ7?FzQ3gQZU<{g;=HIYs z(=5`Wt`TF#>8T;2kDMfjbA2{m&2Qq@nK8Wk<1x6Qg`s{Im(I@Pt#z(`ILDB$7jg1b5rcafWE9~>VBs<~x)$irbeipysY@nj$aG7=(-;&H#&a&YPcO8K z)5z(xOQWgL4U)^(uzVvxVdFN6r50wLemv70!HiSMA<7wUdfRDm z4?J^$ikyCes)%XMObWEKF1Vn{�%HB<(752__Q!66Ntqvxr$T`0LRl%6mhi>5*@+{B6v43tS1T1Ye`k)j_rK}7{fycjb-C-!WFl_cyg=Hm8} zOrnP41Z&a>T2T=7qQIwfhk4}9;i%wE=&|T!GlBD`j%q?Z$}Dk0H;rK?ctO|nm~-CN zBsPRp%jD#5ovmb%Z#%Z>cDnXAjUK5RQ~o=~4^?r!v6X`FhQP!7OR(~mX>y$!NKGLW mTfM+;z(=Fu?{56B&%XfkljZM4#}zsN0000Px&SxH1eR5%fpl*?`-MHGfl-`uuiJI0xTkT56&X%xgPB3Z1N1t?oKS+V3HcnIDk zD;9ZxyhAi>h%yYCMB|wZY2VuJYw`U}EF{=KMW3tc)ak!Yol_Oh{%1QoJ8NBCUG>v6 z9nRG3n)_YED3Ia2$Lk~ z6V(w%Icm4thPVYm&^WSgL}ZFW{ic0uameML1d^8beqMnhs1aHtK-ditbJ1fyVto&R zLwr96^^c(o5o9of1YSZTe#$_L$k}U{Kn`2*A?(AaATjkZv|#g1&3FkC(|20?8njW9 zmIDS~!8VkjjXEGlr9gA>&sJejgsPFWO4ves!Qcz7GdqXm*rU_|y_^CZ0tyy{&V2CrD@tcMEK+CFdEw^sGFzrq zwpuw?R@Uluth=*gcAq;hy#2;p?-%>y#WmR>k6qH!iJgU!u0>_&*gUNfE0c6>H|xw@ zuXFb*%gf)EY5iuksPCp(vBQ$e%E^zx5NE* z+cM#6GZLM)n1*aettKLvc!`0#YOW*Htzqa{}c0`s1ubU8*cyr002ovPDHLkV1j^K B##;aY literal 0 HcmV?d00001 diff --git a/public/flags/GB.png b/public/flags/GB.png new file mode 100644 index 0000000000000000000000000000000000000000..b2f1b3b20cb5f176373c6314f337d68d4b0e42f7 GIT binary patch literal 1376 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 X1~Ulrj~o0d00000NkvXXu0mjf?3x#^ literal 0 HcmV?d00001 diff --git a/src/App.tsx b/src/App.tsx index 296db35..d32ffc7 100644 --- a/src/App.tsx +++ b/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 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++) { 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); addAppToAppRoutesList(metaData, app, appRoutesList, "", 0); } - const newSideNavRoutes = getStaticRoutes(); + const newSideNavRoutes = []; // getStaticRoutes(); // @ts-ignore newSideNavRoutes.unshift(profileRoutes); for (let i = 0; i < sideNavAppList.length; i++) diff --git a/src/qqq/components/DashboardWidgets.tsx b/src/qqq/components/DashboardWidgets.tsx index 9725d42..f78167e 100644 --- a/src/qqq/components/DashboardWidgets.tsx +++ b/src/qqq/components/DashboardWidgets.tsx @@ -37,11 +37,15 @@ import FieldValueListWidget from "qqq/pages/dashboards/Widgets/FieldValueListWid import HorizontalBarChart from "qqq/pages/dashboards/Widgets/HorizontalBarChart"; import MultiStatisticsCard from "qqq/pages/dashboards/Widgets/MultiStatisticsCard"; 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 RecordGridWidget from "qqq/pages/dashboards/Widgets/RecordGridWidget"; 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 TableCard from "qqq/pages/dashboards/Widgets/TableCard"; +import USMapWidget from "qqq/pages/dashboards/Widgets/USMapWidget"; import Widget from "qqq/pages/dashboards/Widgets/Widget"; import ProcessRun from "qqq/pages/process-run"; import QClient from "qqq/utils/QClient"; @@ -98,9 +102,9 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit widgetData[i] = {}; (async () => { - console.log(`widgets: ${getQueryParams(null)}`) widgetData[i] = await qController.widget(widgetMetaDataList[i].name, getQueryParams(null)); setWidgetCounter(widgetCounter + 1); + console.log(`widget data: ${JSON.stringify(widgetData[i])}`) forceUpdate(); })(); } @@ -147,7 +151,7 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit } return params; - }; + } const widgetCount = widgetMetaDataList ? widgetMetaDataList.length : 0; @@ -158,6 +162,7 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit { widgetMetaData.type === "parentWidget" && ( ) } + { + widgetMetaData.type === "table" && ( + reloadWidget(i, data)} + isChild={areChildren} + > + reloadWidget(i, data)} + widgetIndex={i} + /> + + ) + } { widgetMetaData.type === "process" && widgetData[i]?.processMetaData && ( + ) + } + { + widgetMetaData.type === "simpleStatistics" && ( widgetData && widgetData[i] && ( + ) + ) + } { widgetMetaData.type === "quickSightChart" && ( @@ -255,8 +300,18 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit widgetMetaData.type === "barChart" && ( + ) + } + { + widgetMetaData.type === "pieChart" && ( + ) @@ -270,14 +325,12 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit } { widgetMetaData.type === "horizontalBarChart" && ( - widgetData && widgetData[i] && widgetData[i].chartData && ( - - ) + ) } { diff --git a/src/qqq/components/Temporary/DataTable/index.tsx b/src/qqq/components/Temporary/DataTable/index.tsx index 75bfa55..73de877 100644 --- a/src/qqq/components/Temporary/DataTable/index.tsx +++ b/src/qqq/components/Temporary/DataTable/index.tsx @@ -38,12 +38,7 @@ import MDInput from "qqq/components/Temporary/MDInput"; import MDPagination from "qqq/components/Temporary/MDPagination"; import MDTypography from "qqq/components/Temporary/MDTypography"; import ImageCell from "qqq/pages/dashboards/Tables/ImageCell"; - -export interface TableDataInput -{ - columns: { [key: string]: any }[]; - rows: { [key: string]: any }[]; -} +import {TableDataInput} from "qqq/pages/dashboards/Widgets/TableCard"; interface Props { diff --git a/src/qqq/pages/dashboards/CarrierPerformance.tsx b/src/qqq/pages/dashboards/CarrierPerformance.tsx index faba779..88d36a6 100644 --- a/src/qqq/pages/dashboards/CarrierPerformance.tsx +++ b/src/qqq/pages/dashboards/CarrierPerformance.tsx @@ -29,7 +29,6 @@ import QContext from "QContext"; import DashboardLayout from "qqq/components/DashboardLayout"; import Footer from "qqq/components/Footer"; import Navbar from "qqq/components/Navbar"; -import {TableDataInput} from "qqq/components/Temporary/DataTable"; import MDBox from "qqq/components/Temporary/MDBox"; import ShipmentsByWarehouseTable from "qqq/pages/dashboards/Tables/ShipmentsByWarehouseTable"; 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 SimpleStatisticsCard from "qqq/pages/dashboards/Widgets/SimpleStatisticsCard"; 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"; const qController = QClient.getInstance(); diff --git a/src/qqq/pages/dashboards/Overview.tsx b/src/qqq/pages/dashboards/Overview.tsx index c9d2c72..2fbfde3 100644 --- a/src/qqq/pages/dashboards/Overview.tsx +++ b/src/qqq/pages/dashboards/Overview.tsx @@ -47,7 +47,6 @@ function Overview(): JSX.Element ////////////////////////////////// // shipments by day widget data // ////////////////////////////////// - const [shipmentsByDayTitle, setShipmentsByDayTitle] = useState(""); const [shipmentsByDayDescription, setShipmentsByDayDescription] = useState(""); const [shipmentsByDayData, setShipmentsByDayData] = useState({} as GenericChartDataSingleDataset); @@ -135,7 +134,6 @@ function Overview(): JSX.Element } const description = "Over the last week there have been " + daysOverAverage.toLocaleString() + (daysOverAverage == 1 ? " day" : " days") + " with total shipments greater than the daily average of " + average.toLocaleString() + " shipments."; - setShipmentsByDayTitle(widgetData.title); setShipmentsByDayData(widgetData.chartData); setShipmentsByDayDescription(description); })(); diff --git a/src/qqq/pages/dashboards/Widgets/BarChart.tsx b/src/qqq/pages/dashboards/Widgets/BarChart.tsx index cb09524..bae41ba 100644 --- a/src/qqq/pages/dashboards/Widgets/BarChart.tsx +++ b/src/qqq/pages/dashboards/Widgets/BarChart.tsx @@ -19,6 +19,7 @@ * along with this program. If not, see . */ +import Box from "@mui/material/Box"; import Card from "@mui/material/Card"; import Divider from "@mui/material/Divider"; 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); return ( - - - {useMemo( - () => ( - - + + + + + {useMemo( + () => ( + + + + ), + [data, color] + )} + + + {title} + + + {parse(description)} + + + + + schedule + + + {date} + - ), - [data, color] - )} - - - {title} - - - {parse(description)} - - - - - schedule - - - {date} - - - + + ); } diff --git a/src/qqq/pages/dashboards/Widgets/DefaultLineChart.tsx b/src/qqq/pages/dashboards/Widgets/DefaultLineChart.tsx index 9e7f579..8830dcd 100644 --- a/src/qqq/pages/dashboards/Widgets/DefaultLineChart.tsx +++ b/src/qqq/pages/dashboards/Widgets/DefaultLineChart.tsx @@ -57,7 +57,7 @@ const options = { callbacks: { 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) { - return value; + return value.toLocaleString(); } }, }, diff --git a/src/qqq/pages/dashboards/Widgets/HorizontalBarChart.tsx b/src/qqq/pages/dashboards/Widgets/HorizontalBarChart.tsx index d8c619b..72063ea 100644 --- a/src/qqq/pages/dashboards/Widgets/HorizontalBarChart.tsx +++ b/src/qqq/pages/dashboards/Widgets/HorizontalBarChart.tsx @@ -19,6 +19,7 @@ * along with this program. If not, see . */ +import Box from "@mui/material/Box"; import Card from "@mui/material/Card"; import Icon from "@mui/material/Icon"; import {ReactNode, useMemo} from "react"; @@ -48,7 +49,7 @@ const options = { return(context.parsed.x); } } - } + }, }, scales: { y: { @@ -122,27 +123,30 @@ interface Props 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 = {}; - if (data) + if(data && data.datasets) { - fullData = { - labels: data.labels, - datasets: chartDatasets - }; + 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, + })) + : []; + + if (data) + { + fullData = { + labels: data.labels, + datasets: chartDatasets + }; + } } let customOptions = options; @@ -166,10 +170,9 @@ function HorizontalBarChart({icon, title, description, height, data, isCurrency} } } - const renderChart = ( - {title || description ? ( + {title || description && ( {icon.component && ( - ) : null} + )} {useMemo( () => ( - + { + data && data?.datasets && data?.datasets.length > 0 ?( + + ):( + No data was provided to this chart + ) + } ), [data, height] diff --git a/src/qqq/pages/dashboards/Widgets/ParentWidget.tsx b/src/qqq/pages/dashboards/Widgets/ParentWidget.tsx index e1f3a75..0d968b9 100644 --- a/src/qqq/pages/dashboards/Widgets/ParentWidget.tsx +++ b/src/qqq/pages/dashboards/Widgets/ParentWidget.tsx @@ -21,13 +21,10 @@ import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance"; import {QWidgetMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QWidgetMetaData"; -import {Box, Typography} from "@mui/material"; -import Card from "@mui/material/Card"; -import Grid from "@mui/material/Grid"; +import {Box} from "@mui/material"; import React, {useEffect, useState} from "react"; import DashboardWidgets from "qqq/components/DashboardWidgets"; -import DropdownMenu from "qqq/pages/dashboards/Widgets/Components/DropdownMenu"; -import Widget, {Dropdown, LabelComponent} from "qqq/pages/dashboards/Widgets/Widget"; +import Widget from "qqq/pages/dashboards/Widgets/Widget"; import QClient from "qqq/utils/QClient"; @@ -44,6 +41,7 @@ export interface ParentWidgetData }[][]; childWidgetNameList: string[]; dropdownNeedsSelectedText?: string; + icon?: string; } @@ -54,6 +52,7 @@ interface Props { widgetIndex: number; label: string; + icon?: string; data: ParentWidgetData; reloadWidgetCallback?: (widgetIndex: number, params: string) => void; entityPrimaryKey?: string; @@ -62,7 +61,7 @@ interface Props 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 [qInstance, setQInstance] = useState(null as QInstance); @@ -93,16 +92,19 @@ function ParentWidget({widgetIndex, label, data, reloadWidgetCallback, entityPri const parentReloadWidgetCallback = (data: string) => { setChildUrlParams(data); + reloadWidgetCallback(widgetIndex, data); } + // @ts-ignore return ( - + diff --git a/src/qqq/pages/dashboards/Widgets/PieChartCard.tsx b/src/qqq/pages/dashboards/Widgets/PieChartCard.tsx index ad4fbc0..a360724 100644 --- a/src/qqq/pages/dashboards/Widgets/PieChartCard.tsx +++ b/src/qqq/pages/dashboards/Widgets/PieChartCard.tsx @@ -48,8 +48,8 @@ function PieChartCard({title, description, data}: Props): JSX.Element } return ( - - + + {title} @@ -72,15 +72,19 @@ function PieChartCard({title, description, data}: Props): JSX.Element - - - - - {parse(description)} - - - - + { + description && ( + + + + + {parse(description)} + + + + + ) + } ); diff --git a/src/qqq/pages/dashboards/Widgets/SimpleStatisticsCard.tsx b/src/qqq/pages/dashboards/Widgets/SimpleStatisticsCard.tsx index f3a75a2..f4f6a7b 100644 --- a/src/qqq/pages/dashboards/Widgets/SimpleStatisticsCard.tsx +++ b/src/qqq/pages/dashboards/Widgets/SimpleStatisticsCard.tsx @@ -82,7 +82,7 @@ function SimpleStatisticsCard({title, data, increaseIsGood, isCurrency, dropdown { - count ? ( + count !== undefined ? ( isCurrency ? ( {count.toLocaleString("en-US", {style: "currency", currency: "USD"})} @@ -90,21 +90,29 @@ function SimpleStatisticsCard({title, data, increaseIsGood, isCurrency, dropdown ) : ( - {count.toLocaleString("en-US", {style: "currency", currency: "USD"})} + {count.toLocaleString()} ) ) : null } - - {percentageString}  - - {percentageLabel} - - + { + count !== undefined ? ( + + {percentageString}  + + {percentageLabel} + + + ):( + + Loading. + + ) + } {dropdown && ( diff --git a/src/qqq/pages/dashboards/Widgets/SmallLineChart.tsx b/src/qqq/pages/dashboards/Widgets/SmallLineChart.tsx index 34302df..95dd196 100644 --- a/src/qqq/pages/dashboards/Widgets/SmallLineChart.tsx +++ b/src/qqq/pages/dashboards/Widgets/SmallLineChart.tsx @@ -19,9 +19,8 @@ * along with this program. If not, see . */ +import Box from "@mui/material/Box"; 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 {useMemo} from "react"; 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 || {}); - console.log(`DATA: ${JSON.stringify(data)}`); - return ( - - - {useMemo( - () => ( - - - - ), - [chart, color] - )} - - - {title} - - - {parse(description)} - - - - - schedule + + + + + + {useMemo( + () => ( + + + + ), + [chart, color] + )} + + + {title} - - {date} + + {parse(description)} - - + + ); } diff --git a/src/qqq/pages/dashboards/Widgets/StatisticsCard.tsx b/src/qqq/pages/dashboards/Widgets/StatisticsCard.tsx index d20fe5e..2816586 100644 --- a/src/qqq/pages/dashboards/Widgets/StatisticsCard.tsx +++ b/src/qqq/pages/dashboards/Widgets/StatisticsCard.tsx @@ -88,7 +88,8 @@ function StatisticsCard({data, color, icon, increaseIsGood}: Props): JSX.Element } return ( - + + - { percentageAmount !== undefined && percentageAmount !== 0 ? ( - + + . */ -import {Icon, Skeleton} from "@mui/material"; -import Card from "@mui/material/Card"; -import Grid from "@mui/material/Grid"; -import Menu from "@mui/material/Menu"; -import MenuItem from "@mui/material/MenuItem"; +import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance"; +import {Skeleton} from "@mui/material"; import Table from "@mui/material/Table"; import TableBody from "@mui/material/TableBody"; import TableContainer from "@mui/material/TableContainer"; import TableRow from "@mui/material/TableRow"; import parse from "html-react-parser"; import React, {useEffect, useState} from "react"; -import {NavLink} from "react-router-dom"; -import DataTable, {TableDataInput} from "qqq/components/Temporary/DataTable"; +import DataTable from "qqq/components/Temporary/DataTable"; import DataTableBodyCell from "qqq/components/Temporary/DataTable/DataTableBodyCell"; import DataTableHeadCell from "qqq/components/Temporary/DataTable/DataTableHeadCell"; import DefaultCell from "qqq/components/Temporary/DefaultCell"; import MDBox from "qqq/components/Temporary/MDBox"; 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; noRowsFoundHTML?: string; data: TableDataInput; - dropdownOptions?: { - id: string, - name: string - }[]; - reloadWidgetCallback?: (widgetIndex: number, params: string) => void; + reloadWidgetCallback?: (params: string) => void; widgetIndex?: number; isChild?: boolean; [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 closeArrowIcon = "arrow_drop_up"; - const [dropdown, setDropdown] = useState(null); - const [dropdownValue, setDropdownValue] = useState(""); - const [dropdownLabel, setDropdownLabel] = useState(""); - const [dropdownIcon, setDropdownIcon] = useState(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 && ( - - {icon} - - { - dropdownOptions.map((optionMap, index: number) => - {optionMap["name"]} - ) - } - - - ) - ); + const [qInstance, setQInstance] = useState(null as QInstance); useEffect(() => { - if (dropdownOptions) + (async () => { - setDropdownValue(dropdownOptions[0]["id"]); - setDropdownLabel(dropdownOptions[0]["name"]); - - } - }, [dropdownOptions]); + const newQInstance = await qController.loadMetaData(); + setQInstance(newQInstance); + })(); + }, []); return ( - - - - - - {title} - - - - - { - linkText && ( - - {linkText} - - ) - } - - - {dropdownOptions && ( - - - Billing Period: - - - {dropdownLabel} - - {renderMenu(dropdown, openDropdown, closeDropdown, dropdownIcon)} - - )} - - - - - { - data && data.columns && !noRowsFoundHTML ? ( - - ) : noRowsFoundHTML ? ( + + { + data && data.columns && !noRowsFoundHTML ? + + : noRowsFoundHTML ? - ) : ( + : @@ -211,10 +127,8 @@ function TableCard({title, linkText, linkURL, noRowsFoundHTML, data, dropdownOpt
- ) - } -
-
+ } +
); } diff --git a/src/qqq/pages/dashboards/Widgets/USMapWidget.tsx b/src/qqq/pages/dashboards/Widgets/USMapWidget.tsx new file mode 100644 index 0000000..88ef558 --- /dev/null +++ b/src/qqq/pages/dashboards/Widgets/USMapWidget.tsx @@ -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 . + */ + +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 ( + + + { + props.data?.height && ( + + false} + onMarkerTipShow={() => false} + /> + + ) + } + + + + ); +} + +export default USMapWidget; diff --git a/src/qqq/pages/dashboards/Widgets/Widget.tsx b/src/qqq/pages/dashboards/Widgets/Widget.tsx index 7987752..3c1aeb2 100644 --- a/src/qqq/pages/dashboards/Widgets/Widget.tsx +++ b/src/qqq/pages/dashboards/Widgets/Widget.tsx @@ -23,19 +23,18 @@ import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QT 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 Icon from "@mui/material/Icon"; import Typography from "@mui/material/Typography"; import React, {useEffect, useState} from "react"; import {Link, useNavigate} from "react-router-dom"; -import DashboardWidgets from "qqq/components/DashboardWidgets"; -import EntityForm from "qqq/components/EntityForm"; +import colors from "qqq/components/Temporary/colors"; import DropdownMenu, {DropdownOption} from "qqq/pages/dashboards/Widgets/Components/DropdownMenu"; export interface WidgetData { - dropdownLabelList: string[]; - dropdownNameList: string[]; - dropdownDataList: { + dropdownLabelList?: string[]; + dropdownNameList?: string[]; + dropdownDataList?: { id: string, label: string }[][]; @@ -45,15 +44,20 @@ export interface WidgetData interface Props { + icon?: string; label: string; labelAdditionalComponentsLeft: LabelComponent[]; labelAdditionalComponentsRight: LabelComponent[]; widgetData?: WidgetData; children: JSX.Element; reloadWidgetCallback?: (params: string) => void; + isChild?: boolean; + isCard?: boolean; } Widget.defaultProps = { + isCard: true, + isChild: false, label: null, widgetData: {}, labelAdditionalComponentsLeft: [], @@ -246,44 +250,70 @@ function Widget(props: React.PropsWithChildren): JSX.Element } }, [counter]); - return ( - <> - - - - - {props.label} - - { - props.labelAdditionalComponentsLeft.map((component, i) => + const widgetContent = + + { + (props.icon || props.label) && ( + + { - return ({renderComponent(component)}); - }) - } - - - { - effectiveLabelAdditionalComponentsRight.map((component, i) => - { - return ({renderComponent(component)}); - }) - } - - - { - props.widgetData?.dropdownNeedsSelectedText ? ( - - - {props.widgetData?.dropdownNeedsSelectedText} + props.icon && ( + + + {props.icon} + + + ) + } + + {props.label} + { + props.labelAdditionalComponentsLeft.map((component, i) => + { + return ({renderComponent(component)}); + }) + } - ) : ( - props.children - ) - } - - - ); + + { + effectiveLabelAdditionalComponentsRight.map((component, i) => + { + return ({renderComponent(component)}); + }) + } + + + ) + } + { + props.widgetData?.dropdownNeedsSelectedText ? ( + + + {props.widgetData?.dropdownNeedsSelectedText} + + + ) : ( + props.children + ) + } + ; + + return props.isCard ? {widgetContent} : widgetContent; } export default Widget;