CE-1946: checkpoint initial commit

This commit is contained in:
Tim Chamberlain
2024-11-19 15:07:10 -06:00
parent 2514c463a6
commit 726906061d
6 changed files with 4429 additions and 4676 deletions

8576
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -18,18 +18,20 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
import {QWidgetMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QWidgetMetaData";
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
import {Alert, Skeleton} from "@mui/material";
import Box from "@mui/material/Box";
import Grid from "@mui/material/Grid";
import Modal from "@mui/material/Modal";
import Tab from "@mui/material/Tab";
import Tabs from "@mui/material/Tabs";
import parse from "html-react-parser";
import QContext from "QContext";
import EntityForm from "qqq/components/forms/EntityForm";
import MDTypography from "qqq/components/legacy/MDTypography";
import TabPanel from "qqq/components/misc/TabPanel";
import {BlockData} from "qqq/components/widgets/blocks/BlockModels";
import BarChart from "qqq/components/widgets/charts/barchart/BarChart";
import HorizontalBarChart from "qqq/components/widgets/charts/barchart/HorizontalBarChart";
import DefaultLineChart from "qqq/components/widgets/charts/linechart/DefaultLineChart";
@ -44,7 +46,7 @@ import FieldValueListWidget from "qqq/components/widgets/misc/FieldValueListWidg
import FilterAndColumnsSetupWidget from "qqq/components/widgets/misc/FilterAndColumnsSetupWidget";
import PivotTableSetupWidget from "qqq/components/widgets/misc/PivotTableSetupWidget";
import QuickSightChart from "qqq/components/widgets/misc/QuickSightChart";
import RecordGridWidget from "qqq/components/widgets/misc/RecordGridWidget";
import RecordGridWidget, {ChildRecordListData} from "qqq/components/widgets/misc/RecordGridWidget";
import ScriptViewer from "qqq/components/widgets/misc/ScriptViewer";
import StepperCard from "qqq/components/widgets/misc/StepperCard";
import USMapWidget from "qqq/components/widgets/misc/USMapWidget";
@ -72,9 +74,9 @@ interface Props
childUrlParams?: string;
parentWidgetMetaData?: QWidgetMetaData;
wrapWidgetsInTabPanels: boolean;
actionCallback?: (blockData: BlockData) => boolean;
actionCallback?: (data: any, eventValues?: { [name: string]: any }) => boolean;
initialWidgetDataList: any[];
values?: {[key: string]: any};
values?: { [key: string]: any };
}
DashboardWidgets.defaultProps = {
@ -101,6 +103,12 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, reco
const [haveLoadedParams, setHaveLoadedParams] = useState(false);
const {accentColor} = useContext(QContext);
/////////////////////////
// modal form controls //
/////////////////////////
const [showEditChildForm, setShowEditChildForm] = useState(null as any);
const [modalTable, setModalTable] = useState(null as QTableMetaData);
let initialSelectedTab = 0;
let selectedTabKey: string = null;
if (parentWidgetMetaData && wrapWidgetsInTabPanels)
@ -121,11 +129,11 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, reco
useEffect(() =>
{
if(initialWidgetDataList && initialWidgetDataList.length > 0)
if (initialWidgetDataList && initialWidgetDataList.length > 0)
{
// todo actually, should this check each element of the array, down in the loop? yeah, when we need to, do it that way.
console.log("We already have initial widget data, so not fetching from backend.");
return
return;
}
setWidgetData([]);
@ -166,7 +174,7 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, reco
const reloadWidget = async (index: number, data: string) =>
{
(async () =>
await (async () =>
{
const urlParams = getQueryParams(widgetMetaDataList[index], data);
setCurrentUrlParams(urlParams);
@ -285,6 +293,132 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, reco
return (rs);
}
/*******************************************************************************
**
*******************************************************************************/
const closeEditChildForm = (event: object, reason: string) =>
{
if (reason === "backdropClick" || reason === "escapeKeyDown")
{
return;
}
setShowEditChildForm(null);
};
/*******************************************************************************
**
*******************************************************************************/
function deleteChildRecord(name: string, widgetIndex: number, rowIndex: number)
{
updateChildRecordList(name, "delete", rowIndex);
actionCallback(widgetData[widgetIndex]);
};
/*******************************************************************************
**
*******************************************************************************/
function openEditChildRecord(name: string, widgetData: any, rowIndex: number)
{
let defaultValues = widgetData.queryOutput.records[rowIndex].values;
let disabledFields = widgetData.disabledFieldsForNewChildRecords;
if (!disabledFields)
{
disabledFields = widgetData.defaultValuesForNewChildRecords;
}
doOpenEditChildForm(name, widgetData.childTableMetaData, rowIndex, defaultValues, disabledFields);
}
/*******************************************************************************
**
*******************************************************************************/
function doOpenEditChildForm(widgetName: string, table: QTableMetaData, rowIndex: number, defaultValues: any, disabledFields: any)
{
const showEditChildForm: any = {};
showEditChildForm.widgetName = widgetName;
showEditChildForm.table = table;
showEditChildForm.rowIndex = rowIndex;
showEditChildForm.defaultValues = defaultValues;
showEditChildForm.disabledFields = disabledFields;
setShowEditChildForm(showEditChildForm);
}
/*******************************************************************************
**
*******************************************************************************/
function submitEditChildForm(values: any)
{
updateChildRecordList(showEditChildForm.widgetName, showEditChildForm.rowIndex == null ? "insert" : "edit", showEditChildForm.rowIndex, values);
let widgetIndex = determineChildRecordListIndex(showEditChildForm.widgetName);
actionCallback(widgetData[widgetIndex]);
}
/*******************************************************************************
**
*******************************************************************************/
function determineChildRecordListIndex(widgetName: string): number
{
let widgetIndex = -1;
for (var i = 0; i < widgetMetaDataList.length; i++)
{
const widgetMetaData = widgetMetaDataList[i];
if (widgetMetaData.name == widgetName)
{
widgetIndex = i;
break;
}
}
return (widgetIndex);
}
/*******************************************************************************
**
*******************************************************************************/
function updateChildRecordList(widgetName: string, action: "insert" | "edit" | "delete", rowIndex?: number, values?: any)
{
////////////////////////////////////////////////
// find the correct child record widget index //
////////////////////////////////////////////////
let widgetIndex = determineChildRecordListIndex(widgetName);
if (!widgetData[widgetIndex].queryOutput.records)
{
widgetData[widgetIndex].queryOutput.records = [];
}
const newChildListWidgetData: ChildRecordListData = widgetData[widgetIndex];
if (!newChildListWidgetData.queryOutput.records)
{
newChildListWidgetData.queryOutput.records = [];
}
switch (action)
{
case "insert":
newChildListWidgetData.queryOutput.records.push({values: values});
break;
case "edit":
newChildListWidgetData.queryOutput.records[rowIndex] = {values: values};
break;
case "delete":
newChildListWidgetData.queryOutput.records.splice(rowIndex, 1);
break;
}
newChildListWidgetData.totalRows = newChildListWidgetData.queryOutput.records.length;
widgetData[widgetIndex] = newChildListWidgetData;
setWidgetData(widgetData);
setShowEditChildForm(null);
}
const renderWidget = (widgetMetaData: QWidgetMetaData, i: number): JSX.Element =>
{
const labelAdditionalComponentsRight: LabelComponent[] = [];
@ -324,7 +458,7 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, reco
)
}
{
widgetMetaData.type === "alert" && widgetData[i]?.html && (
widgetMetaData.type === "alert" && widgetData[i]?.html && !widgetData[i]?.hideWidget && (
<Widget
omitPadding={true}
widgetMetaData={widgetMetaData}
@ -334,7 +468,16 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, reco
labelAdditionalComponentsRight={labelAdditionalComponentsRight}
labelAdditionalComponentsLeft={labelAdditionalComponentsLeft}
>
<Alert severity={widgetData[i]?.alertType?.toLowerCase()}>{parse(widgetData[i]?.html)}</Alert>
<Alert severity={widgetData[i]?.alertType?.toLowerCase()}>
{parse(widgetData[i]?.html)}
{widgetData[i]?.bulletList && (
<div style={{fontSize: "14px"}}>
{widgetData[i].bulletList.map((bullet: string, index: number) =>
<li key={`widget-${i}-${index}`}>{parse(bullet)}</li>
)}
</div>
)}
</Alert>
</Widget>
)
}
@ -516,9 +659,7 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, reco
}
{
widgetMetaData.type === "divider" && (
<Box>
<DividerWidget />
</Box>
)
}
{
@ -552,6 +693,11 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, reco
widgetMetaData.type === "childRecordList" && (
widgetData && widgetData[i] &&
<RecordGridWidget
disableRowClick={true}
allowRecordEdit={true}
deleteRecordCallback={(rowIndex) => deleteChildRecord(widgetMetaData.name, i, rowIndex)}
editRecordCallback={(rowIndex) => openEditChildRecord(widgetMetaData.name, widgetData[i], rowIndex)}
allowRecordDelete={true}
widgetMetaData={widgetMetaData}
data={widgetData[i]}
/>
@ -653,23 +799,23 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, reco
if (!omitWrappingGridContainer)
{
const gridProps: {[key: string]: any} = {};
const gridProps: { [key: string]: any } = {};
for(let size of ["xs", "sm", "md", "lg", "xl", "xxl"])
for (let size of ["xs", "sm", "md", "lg", "xl", "xxl"])
{
const key = `gridCols:sizeClass:${size}`
if(widgetMetaData?.defaultValues?.has(key))
const key = `gridCols:sizeClass:${size}`;
if (widgetMetaData?.defaultValues?.has(key))
{
gridProps[size] = widgetMetaData?.defaultValues.get(key);
}
}
if(!gridProps["xxl"])
if (!gridProps["xxl"])
{
gridProps["xxl"] = widgetMetaData.gridColumns ? widgetMetaData.gridColumns : 12;
}
if(!gridProps["xs"])
if (!gridProps["xs"])
{
gridProps["xs"] = 12;
}
@ -725,6 +871,22 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, reco
</Grid>
)
}
{
showEditChildForm &&
<Modal open={showEditChildForm as boolean} onClose={(event, reason) => closeEditChildForm(event, reason)}>
<div className="modalEditForm">
<EntityForm
isModal={true}
closeModalHandler={closeEditChildForm}
table={showEditChildForm.table}
defaultValues={showEditChildForm.defaultValues}
disabledFields={showEditChildForm.disabledFields}
onSubmitCallback={submitEditChildForm}
overrideHeading={`${showEditChildForm.rowIndex != null ? "Editing" : "Creating New"} ${showEditChildForm.table.label}`}
/>
</div>
</Modal>
}
</>
) : null
);

View File

@ -19,13 +19,16 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import Box from "@mui/material/Box";
import Divider from "@mui/material/Divider";
function DividerWidget(): JSX.Element
{
return (
<Divider sx={{padding: "1px", background: "red"}}/>
<Box pl={3} pt={1} width="100%">
<Divider sx={{width: "100%", height: "1px", background: "grey"}} />
</Box>
);
}

View File

@ -39,15 +39,15 @@ import {Link, useNavigate} from "react-router-dom";
export interface ChildRecordListData extends WidgetData
{
title: string;
queryOutput: { records: { values: any }[] };
childTableMetaData: QTableMetaData;
tablePath: string;
viewAllLink: string;
totalRows: number;
canAddChildRecord: boolean;
defaultValuesForNewChildRecords: { [fieldName: string]: any };
disabledFieldsForNewChildRecords: { [fieldName: string]: any };
title?: string;
queryOutput?: { records: { values: any }[] };
childTableMetaData?: QTableMetaData;
tablePath?: string;
viewAllLink?: string;
totalRows?: number;
canAddChildRecord?: boolean;
defaultValuesForNewChildRecords?: { [fieldName: string]: any };
disabledFieldsForNewChildRecords?: { [fieldName: string]: any };
}
interface Props
@ -176,7 +176,7 @@ function RecordGridWidget({widgetMetaData, data, addNewRecordCallback, disableRo
setCsv(csv);
setFileName(fileName);
}
}, [data]);
}, [JSON.stringify(data?.queryOutput)]);
///////////////////
// view all link //
@ -304,7 +304,7 @@ function RecordGridWidget({widgetMetaData, data, addNewRecordCallback, disableRo
labelAdditionalComponentsRight={labelAdditionalComponentsRight}
labelBoxAdditionalSx={{position: "relative", top: "-0.375rem"}}
>
<Box mx={-3} mb={-3}>
<Box>
<Box>
<DataGridPro
autoHeight

View File

@ -0,0 +1,96 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
import Modal from "@mui/material/Modal";
import EntityForm from "qqq/components/forms/EntityForm";
import Client from "qqq/utils/qqq/Client";
import React, {useEffect, useReducer, useState} from "react";
////////////////////////////////
// structure of expected data //
////////////////////////////////
export interface ModalEditFormData
{
tableName: string;
defaultValues?: { [key: string]: string };
disabledFields?: { [key: string]: boolean } | string[];
overrideHeading?: string;
onSubmitCallback?: (values: any) => void;
initialShowModalValue?: boolean;
}
const qController = Client.getInstance();
function ModalEditForm({tableName, defaultValues, disabledFields, overrideHeading, onSubmitCallback, initialShowModalValue}: ModalEditFormData,): JSX.Element
{
const [showModal, setShowModal] = useState(initialShowModalValue);
const [table, setTable] = useState(null as QTableMetaData);
const [, forceUpdate] = useReducer((x) => x + 1, 0);
useEffect(() =>
{
if (!tableName)
{
return;
}
(async () =>
{
const tableMetaData = await qController.loadTableMetaData(tableName);
setTable(tableMetaData);
forceUpdate();
})();
}, [tableName]);
/*******************************************************************************
**
*******************************************************************************/
const closeEditChildForm = (event: object, reason: string) =>
{
if (reason === "backdropClick" || reason === "escapeKeyDown")
{
return;
}
setShowModal(null);
};
return (
table && showModal &&
<Modal open={showModal as boolean} onClose={(event, reason) => closeEditChildForm(event, reason)}>
<div className="modalEditForm">
<EntityForm
isModal={true}
closeModalHandler={closeEditChildForm}
table={table}
defaultValues={defaultValues}
disabledFields={disabledFields}
onSubmitCallback={onSubmitCallback}
overrideHeading={overrideHeading}
/>
</div>
</Modal>
);
}
export default ModalEditForm;

View File

@ -66,6 +66,7 @@ import ValidationReview from "qqq/components/processes/ValidationReview";
import {BlockData} from "qqq/components/widgets/blocks/BlockModels";
import CompositeWidget, {CompositeData} from "qqq/components/widgets/CompositeWidget";
import DashboardWidgets from "qqq/components/widgets/DashboardWidgets";
import {ChildRecordListData} from "qqq/components/widgets/misc/RecordGridWidget";
import BaseLayout from "qqq/layouts/BaseLayout";
import ProcessWidgetBlockUtils from "qqq/pages/processes/ProcessWidgetBlockUtils";
import {TABLE_VARIANT_LOCAL_STORAGE_KEY_ROOT} from "qqq/pages/records/query/RecordQuery";
@ -150,7 +151,7 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
const [previouslySeenUpdatedFieldMetaDataMap, setPreviouslySeenUpdatedFieldMetaDataMap] = useState(new Map<string, QFieldMetaData>);
const [renderedWidgets, setRenderedWidgets] = useState({} as { [step: string]: { [widgetName: string]: any } });
const [controlCallbacks, setControlCallbacks] = useState({} as {[name: string]: () => void})
const [controlCallbacks, setControlCallbacks] = useState({} as { [name: string]: () => void });
const {pageHeader, recordAnalytics, setPageHeader, helpHelpActive} = useContext(QContext);
@ -185,7 +186,7 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
{
noMoreSteps = true;
}
if(processValues["noMoreSteps"])
if (processValues["noMoreSteps"])
{
//////////////////////////////////////////////////////////////////
// this, to allow a non-linear process to request this behavior //
@ -210,7 +211,8 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
const [recordConfig, setRecordConfig] = useState({} as any);
const [pageNumber, setPageNumber] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
const [records, setRecords] = useState([] as QRecord[]);
const [records, setRecords] = useState([] as any);
const [childRecordData, setChildRecordData] = useState(null as ChildRecordListData);
//////////////////////////////
// state for bulk edit form //
@ -329,23 +331,24 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
*******************************************************************************/
function renderWidget(widgetName: string)
{
const widgetMetaData = qInstance.widgets.get(widgetName);
if (!widgetMetaData)
{
return (<Alert color="error">Unrecognized widget name: {widgetName}</Alert>);
}
if (!renderedWidgets[activeStep.name])
{
renderedWidgets[activeStep.name] = {};
setRenderedWidgets(renderedWidgets);
}
if (renderedWidgets[activeStep.name][widgetName])
let isChildRecordWidget = widgetMetaData.type == "childRecordList";
if (!isChildRecordWidget && renderedWidgets[activeStep.name][widgetName])
{
return renderedWidgets[activeStep.name][widgetName];
}
const widgetMetaData = qInstance.widgets.get(widgetName);
if (!widgetMetaData)
{
return (<Alert color="error">Unrecognized widget name: {widgetName}</Alert>);
}
const queryStringParts: string[] = [];
for (let name in processValues)
{
@ -353,14 +356,25 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
}
let initialWidgetDataList = null;
if(processValues[widgetName])
if (processValues[widgetName])
{
processValues[widgetName].hasPermission = true
initialWidgetDataList = [processValues[widgetName]]
processValues[widgetName].hasPermission = true;
initialWidgetDataList = [processValues[widgetName]];
}
let actionCallback = blockWidgetActionCallback;
if (isChildRecordWidget)
{
actionCallback = childRecordListWidgetActionCallBack;
if (childRecordData)
{
initialWidgetDataList = [childRecordData];
}
}
const renderedWidget = (<Box m={-2}>
<DashboardWidgets widgetMetaDataList={[widgetMetaData]} omitWrappingGridContainer={true} childUrlParams={queryStringParts.join("&")} initialWidgetDataList={initialWidgetDataList} values={processValues} actionCallback={blockWidgetActionCallback} />
<DashboardWidgets widgetMetaDataList={[widgetMetaData]} omitWrappingGridContainer={true} childUrlParams={queryStringParts.join("&")} initialWidgetDataList={initialWidgetDataList} values={processValues} actionCallback={actionCallback} />
</Box>);
renderedWidgets[activeStep.name][widgetName] = renderedWidget;
return renderedWidget;
@ -374,46 +388,57 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
{
const split = controlCode.split(":", 2);
let controlCallbackName: string;
let controlCallbackValue: any
if(split.length == 2)
let controlCallbackValue: any;
if (split.length == 2)
{
if(split[0] == "showModal")
if (split[0] == "showModal")
{
processValues[split[1]] = true
controlCallbackName = split[1]
controlCallbackValue = true
processValues[split[1]] = true;
controlCallbackName = split[1];
controlCallbackValue = true;
}
else if(split[0] == "hideModal")
else if (split[0] == "hideModal")
{
processValues[split[1]] = false
controlCallbackName = split[1]
controlCallbackValue = false
processValues[split[1]] = false;
controlCallbackName = split[1];
controlCallbackValue = false;
}
else if(split[0] == "toggleModal")
else if (split[0] == "toggleModal")
{
const currentValue = processValues[split[1]]
const currentValue = processValues[split[1]];
processValues[split[1]] = !!!currentValue;
controlCallbackName = split[1]
controlCallbackValue = processValues[split[1]]
controlCallbackName = split[1];
controlCallbackValue = processValues[split[1]];
}
else
{
console.log(`Unexpected part[0] (before colon) in controlCode: [${controlCode}]`)
console.log(`Unexpected part[0] (before colon) in controlCode: [${controlCode}]`);
}
}
else
{
console.log(`Expected controlCode to have 2 colon-delimited parts, but was: [${controlCode}]`)
console.log(`Expected controlCode to have 2 colon-delimited parts, but was: [${controlCode}]`);
}
if(controlCallbackName && controlCallbacks[controlCallbackName])
if (controlCallbackName && controlCallbacks[controlCallbackName])
{
// @ts-ignore ... args are hard
controlCallbacks[controlCallbackName](controlCallbackValue)
controlCallbacks[controlCallbackName](controlCallbackValue);
}
}
/***************************************************************************
** callback used by child list widget
***************************************************************************/
function childRecordListWidgetActionCallBack(data: any): boolean
{
console.log(`in childRecordListWidgetActionCallBack: ${JSON.stringify(data)}`);
setChildRecordData(data as ChildRecordListData);
return (true);
}
/***************************************************************************
** callback used by widget blocks, e.g., for input-text-enter-on-submit,
** and action buttons.
@ -422,11 +447,11 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
{
console.log(`in blockWidgetActionCallback, called by block: ${JSON.stringify(blockData)}`);
if(eventValues?.registerControlCallbackName && eventValues?.registerControlCallbackFunction)
if (eventValues?.registerControlCallbackName && eventValues?.registerControlCallbackFunction)
{
controlCallbacks[eventValues.registerControlCallbackName] = eventValues.registerControlCallbackFunction;
setControlCallbacks(controlCallbacks)
return (true)
setControlCallbacks(controlCallbacks);
return (true);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -449,29 +474,29 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
// }
let doSubmit = false;
if(blockData?.blockTypeName == "BUTTON" && eventValues?.actionCode)
if (blockData?.blockTypeName == "BUTTON" && eventValues?.actionCode)
{
doSubmit = true
doSubmit = true;
}
else if(blockData?.blockTypeName == "BUTTON" && eventValues?.controlCode)
else if (blockData?.blockTypeName == "BUTTON" && eventValues?.controlCode)
{
handleControlCode(eventValues.controlCode);
doSubmit = false
doSubmit = false;
}
else if(blockData?.blockTypeName == "INPUT_FIELD")
else if (blockData?.blockTypeName == "INPUT_FIELD")
{
///////////////////////////////////////////////////////////////////////////////////////////////
// if action callback was fired from an input field, assume that means we're good to submit. //
///////////////////////////////////////////////////////////////////////////////////////////////
doSubmit = true
doSubmit = true;
}
//////////////////
// ok - submit! //
//////////////////
if(doSubmit)
if (doSubmit)
{
handleSubmit(eventValues);
handleFormSubmit(eventValues);
return (true);
}
}
@ -658,7 +683,7 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
let helpRoles = ["PROCESS_SCREEN", "ALL_SCREENS"];
const showHelp = helpHelpActive || hasHelpContent(step.helpContents, helpRoles);
const formattedHelpContent = <HelpContent helpContents={step.helpContents} roles={helpRoles} helpContentKey={`process:${processName};step:${step?.name}`} />;
const isFormatScanner = step?.format?.toLowerCase() == "scanner"
const isFormatScanner = step?.format?.toLowerCase() == "scanner";
return (
<>
@ -1114,7 +1139,7 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
// if this is the last step or not - and by default that radio will be true, to make this //
// NOT the last step - so set this value. //
//////////////////////////////////////////////////////////////////////////////////////////////
if(!processValues["validationSummary"] && processValues["supportsFullValidation"])
if (!processValues["validationSummary"] && processValues["supportsFullValidation"])
{
setOverrideOnLastStep(false);
}
@ -1133,7 +1158,7 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
{
const dynamicField = DynamicFormUtils.getDynamicField(fieldMetaData);
const validation = DynamicFormUtils.getValidationForField(fieldMetaData);
addField(fieldMetaData.name, dynamicField, processValues[fieldMetaData.name], validation)
addField(fieldMetaData.name, dynamicField, processValues[fieldMetaData.name], validation);
});
}
@ -1295,6 +1320,11 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
const {records} = response;
setRecords(records);
if (!childRecordData || childRecordData.length == 0)
{
setChildRecordData(convertRecordsToChildRecordData(records));
}
/////////////////////////////////////////////////////////////////////////////////////////
// re-construct the recordConfig object, so the setState call triggers a new rendering //
/////////////////////////////////////////////////////////////////////////////////////////
@ -1317,6 +1347,30 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
}, [needRecords]);
/***************************************************************************
**
***************************************************************************/
function convertRecordsToChildRecordData(records: QRecord[])
{
const frontendRecords = [] as any[];
records.forEach((record: QRecord) =>
{
const object = {
"tableName": record.tableName,
"recordLabel": record.recordLabel,
"errors": record.errors,
"warnings": record.warnings,
"values": Object.fromEntries(record.values),
"displayValues": Object.fromEntries(record.displayValues),
};
frontendRecords.push(object);
});
const newChildListData = {} as ChildRecordListData;
newChildListData.queryOutput = {records: frontendRecords};
return (newChildListData);
}
/***************************************************************************
**
***************************************************************************/
@ -1690,11 +1744,35 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
setNewStep(activeStepIndex - 1);
};
////////////////////////////////////////////
// handle user submitting changed records //
////////////////////////////////////////////
const doSubmit = async (formData: FormData) =>
{
const formDataHeaders = {
"content-type": "multipart/form-data; boundary=--------------------------320289315924586491558366",
};
setTimeout(async () =>
{
recordAnalytics({category: "processEvents", action: "processStep", label: activeStep.label});
const processResponse = await Client.getInstance().processStep(
processName,
processUUID,
activeStep.name,
formData,
formDataHeaders
);
setLastProcessResponse(processResponse);
});
};
//////////////////////////////////////////////////////////////////////////////////////////
// handle user submitting the form - which in qqq means moving forward from any screen. //
// caller can pass in a map of values to be added to the form data too //
//////////////////////////////////////////////////////////////////////////////////////////
const handleSubmit = async (values: any) =>
const handleFormSubmit = async (values: any) =>
{
setFormError(null);
@ -1733,28 +1811,20 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
formData.append("bulkEditEnabledFields", bulkEditEnabledFields.join(","));
}
const formDataHeaders = {
"content-type": "multipart/form-data; boundary=--------------------------320289315924586491558366",
};
/////////////////////////////////////////////////////////////
// convert to regular objects so that they can be jsonized //
/////////////////////////////////////////////////////////////
if (childRecordData)
{
formData.append("frontendRecords", JSON.stringify(childRecordData.queryOutput.records));
}
setProcessValues({});
setRecords([]);
setOverrideOnLastStep(null);
setLastProcessResponse(new QJobRunning({message: "Working..."}));
setTimeout(async () =>
{
recordAnalytics({category: "processEvents", action: "processStep", label: activeStep.label});
const processResponse = await Client.getInstance().processStep(
processName,
processUUID,
activeStep.name,
formData,
formDataHeaders,
);
setLastProcessResponse(processResponse);
});
doSubmit(formData);
};
@ -1785,7 +1855,7 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
const formStyles: any = {};
if(isWidget)
if (isWidget)
{
formStyles.display = "flex";
formStyles.flexGrow = 1;
@ -1799,7 +1869,7 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
{
const mainCardStyles: any = {};
if(!isWidget && !isModal)
if (!isWidget && !isModal)
{
////////////////////////////////////////////////////////////////
// remove margin around card for non-widget, non-modal, small //
@ -1829,7 +1899,7 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
mainCardStyles.display = "flex";
}
return mainCardStyles
return mainCardStyles;
}
let nextButtonLabel = "Next";
@ -1854,7 +1924,7 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
initialValues={initialValues}
validationSchema={validationScheme}
validation={validationFunction}
onSubmit={handleSubmit}
onSubmit={handleFormSubmit}
>
{({
values, errors, touched, isSubmitting, setFieldValue, setTouched