From 8a160109777e8c21467b6112a8f740786d30fbc8 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Wed, 27 Nov 2024 15:25:50 -0600 Subject: [PATCH] CE-1955 - Add better support for bulk-load (by doing layout more like view screen), for when formatPreviewRecordUsingTableLayout processValues is present; including associations! --- .../components/processes/ValidationReview.tsx | 257 ++++++++++++++---- 1 file changed, 205 insertions(+), 52 deletions(-) diff --git a/src/qqq/components/processes/ValidationReview.tsx b/src/qqq/components/processes/ValidationReview.tsx index 80767e4..015c3d5 100644 --- a/src/qqq/components/processes/ValidationReview.tsx +++ b/src/qqq/components/processes/ValidationReview.tsx @@ -24,29 +24,45 @@ import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstan import {QProcessMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QProcessMetaData"; import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData"; import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord"; -import {Box, Button, FormControlLabel, ListItem, Radio, RadioGroup, Typography} from "@mui/material"; +import {Button, FormControlLabel, ListItem, Radio, RadioGroup, Typography} from "@mui/material"; +import Box from "@mui/material/Box"; import Grid from "@mui/material/Grid"; import Icon from "@mui/material/Icon"; import IconButton from "@mui/material/IconButton"; import List from "@mui/material/List"; import ListItemText from "@mui/material/ListItemText"; -import React, {useState} from "react"; import MDTypography from "qqq/components/legacy/MDTypography"; import CustomWidthTooltip from "qqq/components/tooltips/CustomWidthTooltip"; +import RecordGridWidget, {ChildRecordListData} from "qqq/components/widgets/misc/RecordGridWidget"; import {ProcessSummaryLine} from "qqq/models/processes/ProcessSummaryLine"; +import {renderSectionOfFields} from "qqq/pages/records/view/RecordView"; import Client from "qqq/utils/qqq/Client"; +import TableUtils from "qqq/utils/qqq/TableUtils"; import ValueUtils from "qqq/utils/qqq/ValueUtils"; +import React, {useEffect, useState} from "react"; interface Props { - qInstance: QInstance; - process: QProcessMetaData; - table: QTableMetaData; - processValues: any; - step: QFrontendStepMetaData; - previewRecords: QRecord[]; - formValues: any; - doFullValidationRadioChangedHandler: any + qInstance: QInstance, + process: QProcessMetaData, + table: QTableMetaData, + processValues: any, + step: QFrontendStepMetaData, + previewRecords: QRecord[], + formValues: any, + doFullValidationRadioChangedHandler: any, + loadingRecords?: boolean +} + +//////////////////////////////////////////////////////////////////////////// +// e.g., for bulk-load, where we want to show associations under a record // +// the processValue will have these data, to drive this screen. // +//////////////////////////////////////////////////////////////////////////// +interface AssociationPreview +{ + tableName: string; + widgetName: string; + associationName: string; } /******************************************************************************* @@ -55,21 +71,76 @@ interface Props ** results when they are available. *******************************************************************************/ function ValidationReview({ - qInstance, process, table = null, processValues, step, previewRecords = [], formValues, doFullValidationRadioChangedHandler, + qInstance, process, table = null, processValues, step, previewRecords = [], formValues, doFullValidationRadioChangedHandler, loadingRecords }: Props): JSX.Element { const [previewRecordIndex, setPreviewRecordIndex] = useState(0); const [sourceTableMetaData, setSourceTableMetaData] = useState(null as QTableMetaData); + const [previewTableMetaData, setPreviewTableMetaData] = useState(null as QTableMetaData); + const [childTableMetaData, setChildTableMetaData] = useState({} as { [name: string]: QTableMetaData }); - if(processValues.sourceTable && !sourceTableMetaData) + const [associationPreviewsByWidgetName, setAssociationPreviewsByWidgetName] = useState({} as { [widgetName: string]: AssociationPreview }); + + if (processValues.sourceTable && !sourceTableMetaData) { (async () => { - const sourceTableMetaData = await Client.getInstance().loadTableMetaData(processValues.sourceTable) + const sourceTableMetaData = await Client.getInstance().loadTableMetaData(processValues.sourceTable); setSourceTableMetaData(sourceTableMetaData); })(); } + //////////////////////////////////////////////////////////////////////////////////////// + // load meta-data and set up associations-data structure, if so directed from backend // + //////////////////////////////////////////////////////////////////////////////////////// + useEffect(() => + { + if (processValues.formatPreviewRecordUsingTableLayout && !previewTableMetaData) + { + (async () => + { + const previewTableMetaData = await Client.getInstance().loadTableMetaData(processValues.formatPreviewRecordUsingTableLayout); + setPreviewTableMetaData(previewTableMetaData); + })(); + } + + try + { + const previewRecordAssociatedTableNames: string[] = processValues.previewRecordAssociatedTableNames ?? []; + const previewRecordAssociatedWidgetNames: string[] = processValues.previewRecordAssociatedWidgetNames ?? []; + const previewRecordAssociationNames: string[] = processValues.previewRecordAssociationNames ?? []; + + const associationPreviewsByWidgetName: { [widgetName: string]: AssociationPreview } = {}; + for (let i = 0; i < Math.min(previewRecordAssociatedTableNames.length, previewRecordAssociatedWidgetNames.length, previewRecordAssociationNames.length); i++) + { + const associationPreview = {tableName: previewRecordAssociatedTableNames[i], widgetName: previewRecordAssociatedWidgetNames[i], associationName: previewRecordAssociationNames[i]}; + associationPreviewsByWidgetName[associationPreview.widgetName] = associationPreview; + } + setAssociationPreviewsByWidgetName(associationPreviewsByWidgetName); + + if (Object.keys(associationPreviewsByWidgetName)) + { + (async () => + { + for (let key in associationPreviewsByWidgetName) + { + const associationPreview = associationPreviewsByWidgetName[key]; + childTableMetaData[associationPreview.tableName] = await Client.getInstance().loadTableMetaData(associationPreview.tableName); + setChildTableMetaData(Object.assign({}, childTableMetaData)); + } + })(); + } + } + catch (e) + { + console.log(`Error setting up association previews: ${e}`); + } + }, []); + + + /*************************************************************************** + ** + ***************************************************************************/ const updatePreviewRecordIndex = (offset: number) => { let newIndex = previewRecordIndex + offset; @@ -85,6 +156,10 @@ function ValidationReview({ setPreviewRecordIndex(newIndex); }; + + /*************************************************************************** + ** + ***************************************************************************/ const buildDoFullValidationRadioListItem = (value: "true" | "false", labelText: string, tooltipHTML: JSX.Element): JSX.Element => { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -191,6 +266,77 @@ function ValidationReview({ ); + /*************************************************************************** + ** + ***************************************************************************/ + function previewRecordUsingTableLayout(record: QRecord) + { + if (!previewTableMetaData) + { + return (Loading...); + } + + const renderedSections: JSX.Element[] = []; + const tableSections = TableUtils.getSectionsForRecordSidebar(previewTableMetaData); + const previewRecord = previewRecords[previewRecordIndex]; + + for (let i = 0; i < tableSections.length; i++) + { + const section = tableSections[i]; + if (section.isHidden) + { + continue; + } + + if (section.fieldNames) + { + renderedSections.push( +

{section.label}

+ + {renderSectionOfFields(section.name, section.fieldNames, previewTableMetaData, false, previewRecord, undefined, {label: {fontWeight: "500"}})} + +
); + } + else if (section.widgetName) + { + const widget = qInstance.widgets.get(section.widgetName); + if (widget) + { + let data: ChildRecordListData = null; + if (associationPreviewsByWidgetName[section.widgetName]) + { + const associationPreview = associationPreviewsByWidgetName[section.widgetName]; + const associationRecords = previewRecord.associatedRecords.get(associationPreview.associationName) ?? []; + data = { + canAddChildRecord: false, + childTableMetaData: childTableMetaData[associationPreview.tableName], + defaultValuesForNewChildRecords: {}, + disabledFieldsForNewChildRecords: {}, + queryOutput: {records: associationRecords}, + totalRows: associationRecords.length, + tablePath: "", + title: "", + viewAllLink: "", + }; + + renderedSections.push( + { + data && +

{section.label}

+ + + +
+ } +
); + } + } + } + } + + return renderedSections; + } + const recordPreviewWidget = step.recordListFields && ( @@ -200,43 +346,47 @@ function ValidationReview({ { - processValues?.previewMessage && previewRecords && previewRecords.length > 0 ? ( - <> - {processValues?.previewMessage} - - Note that the number of preview records available may be fewer than the total number of records being processed. - - )} - > - info_outlined - - - ) : ( - <> - No record previews are available at this time. - - { - processValues.validationSummary ? ( - <> - It appears as though this process does not contain any valid records. - - ) : ( - <> - If you choose to Perform Validation, and there are any valid records, then you will see a preview here. - - ) - } - - )} - > - info_outlined - - - ) + loadingRecords ? Loading... : <> + { + processValues?.previewMessage && previewRecords && previewRecords.length > 0 ? ( + <> + {processValues?.previewMessage} + + Note that the number of preview records available may be fewer than the total number of records being processed. + + )} + > + info_outlined + + + ) : ( + <> + No record previews are available at this time. + + { + processValues.validationSummary ? ( + <> + It appears as though this process does not contain any valid records. + + ) : ( + <> + If you choose to Perform Validation, and there are any valid records, then you will see a preview here. + + ) + } + + )} + > + info_outlined + + + ) + } + } @@ -244,16 +394,19 @@ function ValidationReview({ { - previewRecords && previewRecords[previewRecordIndex] && step.recordListFields.map((field) => ( + previewRecords && !processValues.formatPreviewRecordUsingTableLayout && previewRecords[previewRecordIndex] && step.recordListFields.map((field) => ( {`${field.label}:`} {" "} -   +   {" "} {ValueUtils.getDisplayValue(field, previewRecords[previewRecordIndex], "view")} )) } + { + previewRecords && processValues.formatPreviewRecordUsingTableLayout && previewRecords[previewRecordIndex] && previewRecordUsingTableLayout(previewRecords[previewRecordIndex]) + } {