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)}>