diff --git a/package.json b/package.json index cea87a5..d2723e7 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "@auth0/auth0-react": "1.10.2", "@emotion/react": "11.7.1", "@emotion/styled": "11.6.0", - "@kingsrook/qqq-frontend-core": "1.0.96", + "@kingsrook/qqq-frontend-core": "1.0.99", "@mui/icons-material": "5.4.1", "@mui/material": "5.11.1", "@mui/styles": "5.11.1", diff --git a/src/qqq/components/forms/DynamicFormUtils.ts b/src/qqq/components/forms/DynamicFormUtils.ts index 3dc736f..72f7c50 100644 --- a/src/qqq/components/forms/DynamicFormUtils.ts +++ b/src/qqq/components/forms/DynamicFormUtils.ts @@ -175,7 +175,7 @@ class DynamicFormUtils initialDisplayValue: initialDisplayValue, }; } - else + else if(processName) { dynamicFormFields[field.name].possibleValueProps = { @@ -184,6 +184,14 @@ class DynamicFormUtils initialDisplayValue: initialDisplayValue, }; } + else + { + dynamicFormFields[field.name].possibleValueProps = + { + isPossibleValue: true, + initialDisplayValue: initialDisplayValue, + }; + } } } } diff --git a/src/qqq/components/forms/EntityForm.tsx b/src/qqq/components/forms/EntityForm.tsx index cc7cdce..2199ff9 100644 --- a/src/qqq/components/forms/EntityForm.tsx +++ b/src/qqq/components/forms/EntityForm.tsx @@ -43,6 +43,7 @@ import DynamicFormUtils from "qqq/components/forms/DynamicFormUtils"; import MDTypography from "qqq/components/legacy/MDTypography"; import HelpContent from "qqq/components/misc/HelpContent"; import QRecordSidebar from "qqq/components/misc/RecordSidebar"; +import DynamicFormWidget from "qqq/components/widgets/misc/DynamicFormWidget"; import PivotTableSetupWidget from "qqq/components/widgets/misc/PivotTableSetupWidget"; import RecordGridWidget, {ChildRecordListData} from "qqq/components/widgets/misc/RecordGridWidget"; import ReportSetupWidget from "qqq/components/widgets/misc/ReportSetupWidget"; @@ -409,6 +410,19 @@ function EntityForm(props: Props): JSX.Element /> } + if(widgetMetaData.type == "dynamicForm") + { + return + } + return (Unsupported widget type: {widgetMetaData.type}) } @@ -482,7 +496,7 @@ function EntityForm(props: Props): JSX.Element return (true); } - if(widget.type == "reportSetup" || widget.type == "pivotTableSetup") + if(widget.type == "reportSetup" || widget.type == "pivotTableSetup" || widget.type == "dynamicForm") { return (true); } @@ -706,7 +720,8 @@ function EntityForm(props: Props): JSX.Element else { const widgetMetaData = metaData.widgets.get(section.widgetName); - const widgetData = await qController.widget(widgetMetaData.name, props.id ? `${tableMetaData.primaryKeyField}=${props.id}` : ""); + const widgetData = await qController.widget(widgetMetaData.name, makeQueryStringWithIdAndObject(tableMetaData, defaultValues)); + newRenderedWidgetSections[section.widgetName] = getWidgetSection(widgetMetaData, widgetData); newChildListWidgetData[section.widgetName] = widgetData; } @@ -966,6 +981,51 @@ function EntityForm(props: Props): JSX.Element }; + /******************************************************************************* + ** + *******************************************************************************/ + function makeQueryStringWithIdAndObject(tableMetaData: QTableMetaData, object: {[key: string]: any}) + { + const queryParamsArray: string[] = []; + if(props.id) + { + queryParamsArray.push(`${tableMetaData.primaryKeyField}=${encodeURIComponent(props.id)}`) + } + + if(object) + { + for (let key in object) + { + queryParamsArray.push(`${key}=${encodeURIComponent(object[key])}`) + } + } + + return (queryParamsArray.join("&")); + } + + + /******************************************************************************* + ** + *******************************************************************************/ + async function reloadWidget(widgetName: string, additionalQueryParamsForWidget: {[key: string]: any }) + { + const widgetData = await qController.widget(widgetName, makeQueryStringWithIdAndObject(tableMetaData, additionalQueryParamsForWidget)); + const widgetMetaData = metaData.widgets.get(widgetName); + + ///////////////////////////////////////////////////////////////////////////////////////////////////// + // todo - rename this - it holds all widget dta, not just child-lists. also, the type is wrong... // + ///////////////////////////////////////////////////////////////////////////////////////////////////// + const newChildListWidgetData: { [name: string]: ChildRecordListData } = Object.assign({}, childListWidgetData); + newChildListWidgetData[widgetName] = widgetData; + setChildListWidgetData(newChildListWidgetData); + + const newRenderedWidgetSections = Object.assign({}, renderedWidgetSections); + newRenderedWidgetSections[widgetName] = getWidgetSection(widgetMetaData, widgetData); + setRenderedWidgetSections(newRenderedWidgetSections); + forceUpdate(); + } + + /******************************************************************************* ** process a form-field having a changed value (e.g., apply field rules). *******************************************************************************/ @@ -981,6 +1041,10 @@ function EntityForm(props: Props): JSX.Element console.log(`Clearing value from [${fieldRule.targetField}] due to change in [${fieldName}]`); valueChangesToMake[fieldRule.targetField] = null; break; + case FieldRuleAction.RELOAD_WIDGET: + const additionalQueryParamsForWidget: {[key: string]: any} = {}; + additionalQueryParamsForWidget[fieldRule.sourceField] = newValue; + reloadWidget(fieldRule.targetWidget, additionalQueryParamsForWidget); } } } diff --git a/src/qqq/components/widgets/DashboardWidgets.tsx b/src/qqq/components/widgets/DashboardWidgets.tsx index 20bc4fa..46132a6 100644 --- a/src/qqq/components/widgets/DashboardWidgets.tsx +++ b/src/qqq/components/widgets/DashboardWidgets.tsx @@ -38,6 +38,7 @@ import StackedBarChart from "qqq/components/widgets/charts/StackedBarChart"; import CompositeWidget from "qqq/components/widgets/CompositeWidget"; import DataBagViewer from "qqq/components/widgets/misc/DataBagViewer"; import DividerWidget from "qqq/components/widgets/misc/Divider"; +import DynamicFormWidget from "qqq/components/widgets/misc/DynamicFormWidget"; import FieldValueListWidget from "qqq/components/widgets/misc/FieldValueListWidget"; import PivotTableSetupWidget from "qqq/components/widgets/misc/PivotTableSetupWidget"; import QuickSightChart from "qqq/components/widgets/misc/QuickSightChart"; @@ -261,7 +262,7 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, reco { const rs: {[name: string]: any} = {}; - if(record.values) + if(record && record.values) { record.values.forEach((value, key) => rs[key] = value); } @@ -596,6 +597,12 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, reco {}} /> ) } + { + widgetMetaData.type === "dynamicForm" && ( + widgetData && widgetData[i] && + + ) + } ); }; diff --git a/src/qqq/components/widgets/Widget.tsx b/src/qqq/components/widgets/Widget.tsx index 3bfca94..f47a3c4 100644 --- a/src/qqq/components/widgets/Widget.tsx +++ b/src/qqq/components/widgets/Widget.tsx @@ -21,8 +21,7 @@ import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData"; import {QWidgetMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QWidgetMetaData"; -import {InputLabel} from "@mui/material"; -import Box from "@mui/material/Box"; +import {Box, InputLabel} from "@mui/material"; import Button from "@mui/material/Button"; import Card from "@mui/material/Card"; import Icon from "@mui/material/Icon"; diff --git a/src/qqq/components/widgets/misc/DynamicFormWidget.tsx b/src/qqq/components/widgets/misc/DynamicFormWidget.tsx new file mode 100644 index 0000000..e06e31a --- /dev/null +++ b/src/qqq/components/widgets/misc/DynamicFormWidget.tsx @@ -0,0 +1,264 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2024. 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 {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData"; +import {QWidgetMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QWidgetMetaData"; +import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord"; +import Box from "@mui/material/Box"; +import {FormikContextType, useFormikContext} from "formik"; +import QDynamicForm from "qqq/components/forms/DynamicForm"; +import DynamicFormUtils from "qqq/components/forms/DynamicFormUtils"; +import Widget from "qqq/components/widgets/Widget"; +import {renderSectionOfFields} from "qqq/pages/records/view/RecordView"; +import Client from "qqq/utils/qqq/Client"; +import React, {useEffect, useState} from "react"; + + +/******************************************************************************* + ** component props + *******************************************************************************/ +interface DynamicFormWidgetProps +{ + isEditable: boolean; + widgetMetaData: QWidgetMetaData; + widgetData: any; + record: QRecord; + recordValues: { [name: string]: any }; + onSaveCallback?: (values: { [name: string]: any }) => void; +} + + +/******************************************************************************* + ** default values for props + *******************************************************************************/ +DynamicFormWidget.defaultProps = { + onSaveCallback: null +}; + + +/******************************************************************************* + ** Component to display a dynamic form - e.g., on a record edit or view screen, + ** or even within a process. + *******************************************************************************/ +export default function DynamicFormWidget({isEditable, widgetMetaData, widgetData, record, recordValues, onSaveCallback}: DynamicFormWidgetProps): JSX.Element +{ + const [fields, setFields] = useState([] as QFieldMetaData[]); + + const [effectiveIsEditable, setEffectiveIsEditable] = useState(isEditable); + if(widgetMetaData.defaultValues.has("isEditable")) + { + const defaultIsEditableValue = widgetMetaData.defaultValues.get("isEditable") + if(defaultIsEditableValue != effectiveIsEditable) + { + setEffectiveIsEditable(defaultIsEditableValue); + } + } + + const [dynamicFormFields, setDynamicFormFields] = useState(null as any); + const [formValidations, setFormValidations] = useState(null as any); + + const [lastKnowFormValues, setLastKnowFormValues] = useState({} as {[name: string]: any}); + + + ////////////////////////////////////////////////////////////////////////////////////////// + // on initial load, and any time widgetData changes (e.g., if widget gets re-rendered), // + // figure out what our form fields are // + ////////////////////////////////////////////////////////////////////////////////////////// + useEffect(() => + { + setDynamicFormFields({}) + setFormValidations({}) + + if(widgetData && widgetData.fieldList) + { + const newFields: QFieldMetaData[] = []; + for (let i = 0; i < widgetData.fieldList.length; i++) + { + newFields.push(new QFieldMetaData(widgetData.fieldList[i])); + } + setFields(newFields); + + if(newFields.length > 0) + { + const {dynamicFormFields: newDynamicFormFields, formValidations: newFormValidations} = DynamicFormUtils.getFormData(newFields); + const defaultDisplayValues = new Map(); // todo - seems not right? + DynamicFormUtils.addPossibleValueProps(newDynamicFormFields, newFields, recordValues.tableName, null, record ? record.displayValues : defaultDisplayValues); + setDynamicFormFields(newDynamicFormFields) + setFormValidations(newFormValidations) + } + + setLastKnowFormValues({}); + } + else + { + setFields([]) + } + }, [widgetData]); + + + + /******************************************************************************* + ** + *******************************************************************************/ + function checkForFormValueChanges(formikProps: FormikContextType) + { + if(!fields || !fields.length) + { + return; + } + + let anyChanged = false; + for (let i = 0; i < fields.length; i++) + { + const name = fields[i].name; + if(formikProps.values[name] != lastKnowFormValues[name]) + { + anyChanged = true; + lastKnowFormValues[name] = formikProps.values[name]; + } + } + + if(anyChanged) + { + const mergedDynamicFormValuesIntoFieldName = widgetData.mergedDynamicFormValuesIntoFieldName; + if(mergedDynamicFormValuesIntoFieldName && onSaveCallback) + { + const onSaveCallbackParam: {[name: string]: any} = {}; + onSaveCallbackParam[mergedDynamicFormValuesIntoFieldName] = JSON.stringify(lastKnowFormValues); + onSaveCallback(onSaveCallbackParam); + } + } + } + + + /******************************************************************************* + ** + *******************************************************************************/ + function getInitialValue(fieldName: string) + { + for (let i = 0; i < fields?.length; i++) + { + if(fields[i].name == fieldName && fields[i].defaultValue) + { + return (fields[i].defaultValue) + } + } + + return (null); + } + + + /******************************************************************************* + ** + *******************************************************************************/ + function renderEditForm() + { + const formikProps = useFormikContext(); + if(!fields || !fields.length) + { + return ( + + {widgetData && widgetData.noFieldsMessage} + + ); + } + + const formData: any = {}; + formData.values = formikProps.values; + formData.touched = formikProps.touched; + formData.errors = formikProps.errors; + formData.formFields = {}; + + // todo - merge the formValidations object with formik's - maybe in the useEffect where we build it + // setValidations(Yup.object().shape(formValidations)); + // formikProps.validationSchema. + + for (let key of Object.keys(dynamicFormFields)) + { + const dynamicFormField = dynamicFormFields[key]; + formData.formFields[dynamicFormField.name] = dynamicFormField; + + const initialValue = getInitialValue(dynamicFormField.name); + if(initialValue != null) + { + console.log(`@dk trying to set an initial value [${dynamicFormField.name}] to [${initialValue}]`); + // @ts-ignore some any + formikProps.initialValues[dynamicFormField.name] = initialValue; + } + } + + if(formData.values) + { + checkForFormValueChanges(formikProps); + } + + return ( + + + + ); + } + + + /******************************************************************************* + ** + *******************************************************************************/ + function renderViewForm() + { + const fieldNames: string[] = []; + const fieldMap: {[name: string]: QFieldMetaData} = {}; + const fakeRecord = new QRecord({}); + + const mergedDynamicFormValuesIntoFieldName = widgetData.mergedDynamicFormValuesIntoFieldName; + + for (let i = 0; i < fields?.length; i++) + { + const fieldName = fields[i].name; + fieldNames.push(fieldName); + fieldMap[fieldName] = fields[i]; + + if(mergedDynamicFormValuesIntoFieldName && recordValues[mergedDynamicFormValuesIntoFieldName]) + { + fakeRecord.values.set(fieldName, recordValues[mergedDynamicFormValuesIntoFieldName][fieldName]); + } + } + + const section = renderSectionOfFields(`dynamicFormWidget:${widgetMetaData.name}`, fieldNames, null, false, fakeRecord, fieldMap); + + return ( + {section} + ); + } + + + //////////// + // render // + //////////// + return ( + { + + {effectiveIsEditable ? renderEditForm() : renderViewForm()} + + } + ); +} + diff --git a/src/qqq/components/widgets/misc/RecordGridWidget.tsx b/src/qqq/components/widgets/misc/RecordGridWidget.tsx index b025a39..4bf329f 100644 --- a/src/qqq/components/widgets/misc/RecordGridWidget.tsx +++ b/src/qqq/components/widgets/misc/RecordGridWidget.tsx @@ -185,7 +185,7 @@ function RecordGridWidget({widgetMetaData, data, addNewRecordCallback, disableRo if(data && data.viewAllLink) { labelAdditionalElementsLeft.push( - + View All ) @@ -225,8 +225,8 @@ function RecordGridWidget({widgetMetaData, data, addNewRecordCallback, disableRo if(widgetMetaData?.showExportButton) { labelAdditionalElementsLeft.push( - - + + ); } @@ -305,48 +305,50 @@ function RecordGridWidget({widgetMetaData, data, addNewRecordCallback, disableRo labelBoxAdditionalSx={{position: "relative", top: "-0.375rem"}} > - (params.indexRelativeToCurrentPage % 2 === 0 ? "even" : "odd")} - onRowClick={handleRowClick} - getRowId={(row) => row.__rowIndex} - // getRowHeight={() => "auto"} // maybe nice? wraps values in cells... - components={{ - Toolbar: CustomToolbar - }} - // pinnedColumns={pinnedColumns} - // onPinnedColumnsChange={handlePinnedColumnsChange} - // pagination - // paginationMode="server" - // rowsPerPageOptions={[20]} - // sortingMode="server" - // filterMode="server" - // page={pageNumber} - // checkboxSelection - rowCount={data && data.totalRows} - // onPageSizeChange={handleRowsPerPageChange} - // onStateChange={handleStateChange} - // density={density} - // loading={loading} - // filterModel={filterModel} - // onFilterModelChange={handleFilterChange} - // columnVisibilityModel={columnVisibilityModel} - // onColumnVisibilityModelChange={handleColumnVisibilityChange} - // onColumnOrderChange={handleColumnOrderChange} - // onSelectionModelChange={selectionChanged} - // onSortModelChange={handleSortChange} - // sortingOrder={[ "asc", "desc" ]} - // sortModel={columnSortModel} - /> + + (params.indexRelativeToCurrentPage % 2 === 0 ? "even" : "odd")} + onRowClick={handleRowClick} + getRowId={(row) => row.__rowIndex} + // getRowHeight={() => "auto"} // maybe nice? wraps values in cells... + components={{ + Toolbar: CustomToolbar + }} + // pinnedColumns={pinnedColumns} + // onPinnedColumnsChange={handlePinnedColumnsChange} + // pagination + // paginationMode="server" + // rowsPerPageOptions={[20]} + // sortingMode="server" + // filterMode="server" + // page={pageNumber} + // checkboxSelection + rowCount={data && data.totalRows} + // onPageSizeChange={handleRowsPerPageChange} + // onStateChange={handleStateChange} + // density={density} + // loading={loading} + // filterModel={filterModel} + // onFilterModelChange={handleFilterChange} + // columnVisibilityModel={columnVisibilityModel} + // onColumnVisibilityModelChange={handleColumnVisibilityChange} + // onColumnOrderChange={handleColumnOrderChange} + // onSelectionModelChange={selectionChanged} + // onSortModelChange={handleSortChange} + // sortingOrder={[ "asc", "desc" ]} + // sortModel={columnSortModel} + /> + ); diff --git a/src/qqq/pages/processes/ProcessRun.tsx b/src/qqq/pages/processes/ProcessRun.tsx index d9bfdfc..f5d1f29 100644 --- a/src/qqq/pages/processes/ProcessRun.tsx +++ b/src/qqq/pages/processes/ProcessRun.tsx @@ -58,6 +58,7 @@ import QRecordSidebar from "qqq/components/misc/RecordSidebar"; import {GoogleDriveFolderPickerWrapper} from "qqq/components/processes/GoogleDriveFolderPickerWrapper"; import ProcessSummaryResults from "qqq/components/processes/ProcessSummaryResults"; import ValidationReview from "qqq/components/processes/ValidationReview"; +import DashboardWidgets from "qqq/components/widgets/DashboardWidgets"; import BaseLayout from "qqq/layouts/BaseLayout"; import {TABLE_VARIANT_LOCAL_STORAGE_KEY_ROOT} from "qqq/pages/records/query/RecordQuery"; import Client from "qqq/utils/qqq/Client"; @@ -124,6 +125,8 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is const [showErrorDetail, setShowErrorDetail] = useState(false); const [showFullHelpText, setShowFullHelpText] = useState(false); + const [renderedWidgets, setRenderedWidgets] = useState({} as {[step: string]: {[widgetName: string]: any}}); + const {pageHeader, recordAnalytics, setPageHeader} = useContext(QContext); ////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -273,6 +276,42 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is }; }; + + /******************************************************************************* + ** + *******************************************************************************/ + function renderWidget(widgetName: string) + { + if(!renderedWidgets[activeStep.name]) + { + renderedWidgets[activeStep.name] = {}; + setRenderedWidgets(renderedWidgets); + } + + if(renderedWidgets[activeStep.name][widgetName]) + { + return renderedWidgets[activeStep.name][widgetName]; + } + + const widgetMetaData = qInstance.widgets.get(widgetName); + if(!widgetMetaData) + { + return (Unrecognized widget name: {widgetName}); + } + + const queryStringParts: string[] = []; + for (let name in processValues) + { + queryStringParts.push(`${name}=${encodeURIComponent(processValues[name])}`) + } + + const renderedWidget = ( + + ) + renderedWidgets[activeStep.name][widgetName] = renderedWidget; + return renderedWidget; + } + //////////////////////////////////////////////////// // generate the main form body content for a step // //////////////////////////////////////////////////// @@ -653,6 +692,12 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is ) } + { + component.type === QComponentType.WIDGET && ( + component.values?.widgetName && + renderWidget(component.values?.widgetName) + ) + } ); })) diff --git a/src/qqq/pages/records/view/RecordView.tsx b/src/qqq/pages/records/view/RecordView.tsx index e8388a3..c29eeef 100644 --- a/src/qqq/pages/records/view/RecordView.tsx +++ b/src/qqq/pages/records/view/RecordView.tsx @@ -21,6 +21,7 @@ import {QException} from "@kingsrook/qqq-frontend-core/lib/exceptions/QException"; import {Capability} from "@kingsrook/qqq-frontend-core/lib/model/metaData/Capability"; +import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData"; import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance"; import {QProcessMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QProcessMetaData"; import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData"; @@ -82,6 +83,47 @@ RecordView.defaultProps = const TABLE_VARIANT_LOCAL_STORAGE_KEY_ROOT = "qqq.tableVariant"; + +/******************************************************************************* + ** + *******************************************************************************/ +export function renderSectionOfFields(key: string, fieldNames: string[], tableMetaData: QTableMetaData, helpHelpActive: boolean, record: QRecord, fieldMap?: {[name: string]: QFieldMetaData} ) +{ + return + { + fieldNames.map((fieldName: string) => + { + let [field, tableForField] = tableMetaData ? TableUtils.getFieldAndTable(tableMetaData, fieldName) : fieldMap ? [fieldMap[fieldName], null] : [null, null]; + + if (field != null) + { + let label = field.label; + + const helpRoles = ["VIEW_SCREEN", "READ_SCREENS", "ALL_SCREENS"]; + const showHelp = helpHelpActive || hasHelpContent(field.helpContents, helpRoles); + const formattedHelpContent = ; + + const labelElement = {label}:; + + return ( + + <> + { + showHelp && formattedHelpContent ? {labelElement} : labelElement + } +
 
+ + {ValueUtils.getDisplayValue(field, record, "view", fieldName)} + + +
+ ); + } + }) + } +
; +} + function RecordView({table, launchProcess}: Props): JSX.Element { const {id} = useParams(); @@ -519,40 +561,7 @@ function RecordView({table, launchProcess}: Props): JSX.Element // for a section with field names, render the field values. // // for the T1 section, the "wrapper" will come out below - but for other sections, produce a wrapper too. // //////////////////////////////////////////////////////////////////////////////////////////////////////////// - const fields = ( - - { - section.fieldNames.map((fieldName: string) => - { - let [field, tableForField] = TableUtils.getFieldAndTable(tableMetaData, fieldName); - if (field != null) - { - let label = field.label; - - const helpRoles = ["VIEW_SCREEN", "READ_SCREENS", "ALL_SCREENS"]; - const showHelp = helpHelpActive || hasHelpContent(field.helpContents, helpRoles); - const formattedHelpContent = ; - - const labelElement = {label}:; - - return ( - - <> - { - showHelp && formattedHelpContent ? {labelElement} : labelElement - } -
 
- - {ValueUtils.getDisplayValue(field, record, "view", fieldName)} - - -
- ); - } - }) - } -
- ); + const fields = renderSectionOfFields(section.name, section.fieldNames, tableMetaData, helpHelpActive, record); if (section.tier === "T1") { @@ -979,7 +988,7 @@ function RecordView({table, launchProcess}: Props): JSX.Element { showEditChildForm && - closeEditChildForm(event, reason)}> + closeEditChildForm(event, reason)}>