From a55f87946a426470c7adf6087ae9aa83613195bc Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Fri, 9 Dec 2022 10:36:59 -0600 Subject: [PATCH] Updates for dropdowns for all widgets, process specifically --- src/qqq/components/DashboardWidgets.tsx | 11 +- src/qqq/components/EntityForm/index.tsx | 2 +- src/qqq/components/QButtons/index.tsx | 2 +- src/qqq/pages/dashboards/Overview.tsx | 3 +- .../pages/dashboards/Widgets/ParentWidget.tsx | 105 ++----------- .../dashboards/Widgets/RecordGridWidget.tsx | 4 +- .../pages/dashboards/Widgets/TableCard.tsx | 2 +- src/qqq/pages/dashboards/Widgets/Widget.tsx | 148 +++++++++++++++++- src/qqq/pages/process-run/index.tsx | 39 +++-- 9 files changed, 198 insertions(+), 118 deletions(-) diff --git a/src/qqq/components/DashboardWidgets.tsx b/src/qqq/components/DashboardWidgets.tsx index d2b67ba..e26995f 100644 --- a/src/qqq/components/DashboardWidgets.tsx +++ b/src/qqq/components/DashboardWidgets.tsx @@ -184,7 +184,14 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit } { widgetMetaData.type === "process" && widgetData[i]?.processMetaData && ( - + reloadWidget(i, data)}> +
+ +
+
) } { @@ -303,9 +310,9 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, omit ) + } { widgetMetaData.type === "fieldValueList" && ( diff --git a/src/qqq/components/EntityForm/index.tsx b/src/qqq/components/EntityForm/index.tsx index 9a1c90f..98175d4 100644 --- a/src/qqq/components/EntityForm/index.tsx +++ b/src/qqq/components/EntityForm/index.tsx @@ -154,7 +154,7 @@ function EntityForm(props: Props): JSX.Element ///////////////////////////////////////////////// // define the sections, e.g., for the left-bar // ///////////////////////////////////////////////// - const tableSections = QTableUtils.getSectionsForRecordSidebar(tableMetaData); + const tableSections = QTableUtils.getSectionsForRecordSidebar(tableMetaData, [...tableMetaData.fields.keys()]); setTableSections(tableSections); const fieldArray = [] as QFieldMetaData[]; diff --git a/src/qqq/components/QButtons/index.tsx b/src/qqq/components/QButtons/index.tsx index b813ea9..c141aad 100644 --- a/src/qqq/components/QButtons/index.tsx +++ b/src/qqq/components/QButtons/index.tsx @@ -101,7 +101,7 @@ interface QActionsMenuButtonProps export function QActionsMenuButton({isOpen, onClickHandler}: QActionsMenuButtonProps): JSX.Element { return ( - + { - warehouseData && warehouseData.map((data) => ( + // @ts-ignore + warehouseData && warehouseData.locationDataList?.map((data) => ( { - if(dropdownData) - { - /////////////////////////////////////////// - // find the index base on selected label // - /////////////////////////////////////////// - const tableName = dropdownLabel.replace("Select ", ""); - let index = -1; - for (let i = 0; i < data.dropdownLabelList.length; i++) - { - if (tableName === data.dropdownLabelList[i]) - { - index = i; - break; - } - } - - if (index < 0) - { - throw(`Could not find table name for label ${tableName}`); - } - - dropdownData[index] = (changedData) ? changedData.id : null; - setDropdownData(dropdownData); - setCounter(counter + 1); - } + setChildUrlParams(data); + reloadWidgetCallback(widgetIndex, data); } - useEffect(() => - { - if(dropdownData) - { - let params = ""; - for (let i = 0; i < dropdownData.length; i++) - { - if (i > 0) - { - params += "&"; - } - params += `${data.dropdownNameList[i]}=`; - if(dropdownData[i]) - { - params += `${dropdownData[i]}`; - - } - } - reloadWidgetCallback(widgetIndex, params); - setChildUrlParams(params) - } - }, [counter]); - - return ( - - - - - - { - label && ( - - {label} - - ) - } - - - - - { - data?.dropdownDataList?.map((dropdownData: any, index: number) => - - ) - } - - - - - { - data?.dropdownNeedsSelectedText ? ( - - - {data.dropdownNeedsSelectedText} - - - ) :( - - ) - } + + + - + ); } diff --git a/src/qqq/pages/dashboards/Widgets/RecordGridWidget.tsx b/src/qqq/pages/dashboards/Widgets/RecordGridWidget.tsx index d05e45a..d3b3b7d 100644 --- a/src/qqq/pages/dashboards/Widgets/RecordGridWidget.tsx +++ b/src/qqq/pages/dashboards/Widgets/RecordGridWidget.tsx @@ -30,12 +30,11 @@ interface Props { title: string; data: any; - reloadWidgetCallback?: (widgetIndex: number, params: string) => void; } RecordGridWidget.defaultProps = {}; -function RecordGridWidget({title, data, reloadWidgetCallback}: Props): JSX.Element +function RecordGridWidget({title, data}: Props): JSX.Element { const [rows, setRows] = useState([]); const [columns, setColumns] = useState([]); @@ -87,7 +86,6 @@ function RecordGridWidget({title, data, reloadWidgetCallback}: Props): JSX.Eleme label={title} labelAdditionalComponentsLeft={labelAdditionalComponentsLeft} labelAdditionalComponentsRight={labelAdditionalComponentsRight} - reloadWidgetCallback={reloadWidgetCallback} > - + {title} diff --git a/src/qqq/pages/dashboards/Widgets/Widget.tsx b/src/qqq/pages/dashboards/Widgets/Widget.tsx index 5634c43..e46e72d 100644 --- a/src/qqq/pages/dashboards/Widgets/Widget.tsx +++ b/src/qqq/pages/dashboards/Widgets/Widget.tsx @@ -25,27 +25,42 @@ import Button from "@mui/material/Button"; import Card from "@mui/material/Card"; import Modal from "@mui/material/Modal"; import Typography from "@mui/material/Typography"; -import React, {useState} from "react"; +import 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 DropdownMenu, {DropdownOption} from "qqq/pages/dashboards/Widgets/Components/DropdownMenu"; + +export interface WidgetData +{ + dropdownLabelList: string[]; + dropdownNameList: string[]; + dropdownDataList: { + id: string, + label: string + }[][]; + dropdownNeedsSelectedText?: string; +} + interface Props { label: string; labelAdditionalComponentsLeft: LabelComponent[]; labelAdditionalComponentsRight: LabelComponent[]; + widgetData?: WidgetData; children: JSX.Element; - reloadWidgetCallback?: (widgetIndex: number, params: string) => void; + reloadWidgetCallback?: (params: string) => void; } Widget.defaultProps = { label: null, + widgetData: {}, labelAdditionalComponentsLeft: [], labelAdditionalComponentsRight: [], }; - export class LabelComponent { @@ -71,7 +86,7 @@ export class HeaderLink extends LabelComponent export class AddNewRecordButton extends LabelComponent { table: QTableMetaData; - label:string; + label: string; defaultValues: any; disabledFields: any; @@ -86,10 +101,28 @@ export class AddNewRecordButton extends LabelComponent } +export class Dropdown extends LabelComponent +{ + label: string; + options: DropdownOption[]; + onChangeCallback: any + + constructor(label: string, options: DropdownOption[], onChangeCallback: any) + { + super(); + this.label = label; + this.options = options; + this.onChangeCallback = onChangeCallback; + } +} + + function Widget(props: React.PropsWithChildren): JSX.Element { const navigate = useNavigate(); + const [dropdownData, setDropdownData] = useState([]); + const [counter, setCounter] = useState(0); function openEditForm(table: QTableMetaData, id: any = null, defaultValues: any, disabledFields: any) { @@ -118,8 +151,101 @@ function Widget(props: React.PropsWithChildren): JSX.Element ); } + if (component instanceof Dropdown) + { + const dropdown = component as Dropdown + return ( + + + + ); + } + + return (
Unsupported component type.
) } + + /////////////////////////////////////////////////////////////////// + // make dropdowns from the widgetData appear as label-components // + /////////////////////////////////////////////////////////////////// + const effectiveLabelAdditionalComponentsRight: LabelComponent[] = []; + if(props.labelAdditionalComponentsRight) + { + props.labelAdditionalComponentsRight.map((component) => effectiveLabelAdditionalComponentsRight.push(component)); + } + if(props.widgetData && props.widgetData.dropdownDataList) + { + props.widgetData.dropdownDataList?.map((dropdownData: any, index: number) => + { + effectiveLabelAdditionalComponentsRight.push(new Dropdown(props.widgetData.dropdownLabelList[index], dropdownData, handleDataChange)) + }); + } + + + function handleDataChange(dropdownLabel: string, changedData: any) + { + if(dropdownData) + { + /////////////////////////////////////////// + // find the index base on selected label // + /////////////////////////////////////////// + const tableName = dropdownLabel.replace("Select ", ""); + let index = -1; + for (let i = 0; i < props.widgetData.dropdownLabelList.length; i++) + { + if (tableName === props.widgetData.dropdownLabelList[i]) + { + index = i; + break; + } + } + + if (index < 0) + { + throw(`Could not find table name for label ${tableName}`); + } + + dropdownData[index] = (changedData) ? changedData.id : null; + setDropdownData(dropdownData); + setCounter(counter + 1); + } + } + + useEffect(() => + { + if(dropdownData) + { + let params = ""; + for (let i = 0; i < dropdownData.length; i++) + { + if (i > 0) + { + params += "&"; + } + params += `${props.widgetData.dropdownNameList[i]}=`; + if(dropdownData[i]) + { + params += `${dropdownData[i]}`; + + } + } + + if(props.reloadWidgetCallback) + { + props.reloadWidgetCallback(params); + } + else + { + console.log(`No reload widget callback in ${props.label}`) + } + } + }, [counter]); + return ( <> @@ -137,14 +263,24 @@ function Widget(props: React.PropsWithChildren): JSX.Element { - props.labelAdditionalComponentsRight.map((component, i) => + effectiveLabelAdditionalComponentsRight.map((component, i) => { return ({renderComponent(component)}); }) } - {props.children} + { + props.widgetData?.dropdownNeedsSelectedText ? ( + + + {props.widgetData?.dropdownNeedsSelectedText} + + + ) : ( + props.children + ) + } ); diff --git a/src/qqq/pages/process-run/index.tsx b/src/qqq/pages/process-run/index.tsx index f85cf79..fd11e83 100644 --- a/src/qqq/pages/process-run/index.tsx +++ b/src/qqq/pages/process-run/index.tsx @@ -74,13 +74,14 @@ interface Props isWidget?: boolean; recordIds?: string | QQueryFilter; closeModalHandler?: (event: object, reason: string) => void; + forceReInit?: number; } const INITIAL_RETRY_MILLIS = 1_500; const RETRY_MAX_MILLIS = 12_000; const BACKOFF_AMOUNT = 1.5; -function ProcessRun({process, defaultProcessValues, isModal, isWidget, recordIds, closeModalHandler}: Props): JSX.Element +function ProcessRun({process, defaultProcessValues, isModal, isWidget, recordIds, closeModalHandler, forceReInit}: Props): JSX.Element { const processNameParam = useParams().processName; const processName = process === null ? processNameParam : process.name; @@ -98,6 +99,7 @@ function ProcessRun({process, defaultProcessValues, isModal, isWidget, recordIds const [newStep, setNewStep] = useState(null); const [steps, setSteps] = useState([] as QFrontendStepMetaData[]); const [needInitialLoad, setNeedInitialLoad] = useState(true); + const [lastForcedReInit, setLastForcedReInit] = useState(null as number); const [processMetaData, setProcessMetaData] = useState(null); const [tableMetaData, setTableMetaData] = useState(null); const [tableSections, setTableSections] = useState(null as QTableSection[]); @@ -322,13 +324,23 @@ function ProcessRun({process, defaultProcessValues, isModal, isWidget, recordIds return ( <> - - {(isModal) ? `${process.label}: ` : ""} - {step?.label} - - {step.components && ( - step.components.map((component: QFrontendComponent, index: number) => ( - // eslint-disable-next-line react/no-array-index-key + { + /////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // hide label on widgets - the Widget component itself provides the label // + // for modals, show the process label, but not for full-screen processes (for them, it is in the breadcrumb) // + /////////////////////////////////////////////////////////////////////////////////////////////////////////////// + !isWidget && + + {(isModal) ? `${process.label}: ` : ""} + {step?.label} + + } + + { + ////////////////////////////////////////////////// + // render all of the components for this screen // + ////////////////////////////////////////////////// + step.components && (step.components.map((component: QFrontendComponent, index: number) => (
{ component.type === QComponentType.HELP_TEXT && ( @@ -925,10 +937,14 @@ function ProcessRun({process, defaultProcessValues, isModal, isWidget, recordIds ////////////////////////////////////////////////////////////////////////////////////////// // do the initial load of data for the process - that is, meta data, plus the init step // + // also - allow the component that contains this component to force a re-init, by // + // changing the value in the forceReInit property // ////////////////////////////////////////////////////////////////////////////////////////// - if (needInitialLoad) + if (needInitialLoad || forceReInit != lastForcedReInit) { setNeedInitialLoad(false); + setLastForcedReInit(forceReInit); + (async () => { const urlSearchParams = new URLSearchParams(location.search); @@ -1108,6 +1124,8 @@ function ProcessRun({process, defaultProcessValues, isModal, isWidget, recordIds } if(isWidget) { + mainCardStyles.background = "none"; + mainCardStyles.boxShadow = "none"; mainCardStyles.minHeight = ""; mainCardStyles.alignItems = "stretch"; mainCardStyles.flexGrow = 1; @@ -1269,7 +1287,8 @@ ProcessRun.defaultProps = { isModal: false, isWidget: false, recordIds: null, - closeModalHandler: null + closeModalHandler: null, + forceReInit: 0 }; export default ProcessRun;