CE-1115 - support having the ReportSetupWidget and PivotTableSetupWidget actually edit the form values used on the page

This commit is contained in:
2024-04-09 15:58:19 -05:00
parent e5e49a6db8
commit 3558a91e7b

View File

@ -43,7 +43,9 @@ import DynamicFormUtils from "qqq/components/forms/DynamicFormUtils";
import MDTypography from "qqq/components/legacy/MDTypography"; import MDTypography from "qqq/components/legacy/MDTypography";
import HelpContent from "qqq/components/misc/HelpContent"; import HelpContent from "qqq/components/misc/HelpContent";
import QRecordSidebar from "qqq/components/misc/RecordSidebar"; import QRecordSidebar from "qqq/components/misc/RecordSidebar";
import PivotTableSetupWidget from "qqq/components/widgets/misc/PivotTableSetupWidget";
import RecordGridWidget, {ChildRecordListData} from "qqq/components/widgets/misc/RecordGridWidget"; import RecordGridWidget, {ChildRecordListData} from "qqq/components/widgets/misc/RecordGridWidget";
import ReportSetupWidget from "qqq/components/widgets/misc/ReportSetupWidget";
import HtmlUtils from "qqq/utils/HtmlUtils"; import HtmlUtils from "qqq/utils/HtmlUtils";
import Client from "qqq/utils/qqq/Client"; import Client from "qqq/utils/qqq/Client";
import TableUtils from "qqq/utils/qqq/TableUtils"; import TableUtils from "qqq/utils/qqq/TableUtils";
@ -77,6 +79,15 @@ EntityForm.defaultProps = {
onSubmitCallback: null, onSubmitCallback: null,
}; };
////////////////////////////////////////////////////////////////////////////
// define a function that we can make referenes to, which we'll overwrite //
// with formik's setFieldValue function, once we're inside formik. //
////////////////////////////////////////////////////////////////////////////
let formikSetFieldValueFunction = (field: string, value: any, shouldValidate?: boolean): void =>
{
}
function EntityForm(props: Props): JSX.Element function EntityForm(props: Props): JSX.Element
{ {
const qController = Client.getInstance(); const qController = Client.getInstance();
@ -108,6 +119,9 @@ function EntityForm(props: Props): JSX.Element
const [notAllowedError, setNotAllowedError] = useState(null as string); const [notAllowedError, setNotAllowedError] = useState(null as string);
const [recordValuesJSON, setRecordValuesJSON] = useState("");
const [formValues, setFormValues] = useState({} as {[name: string]: any});
const {pageHeader, setPageHeader} = useContext(QContext); const {pageHeader, setPageHeader} = useContext(QContext);
const navigate = useNavigate(); const navigate = useNavigate();
@ -269,6 +283,21 @@ function EntityForm(props: Props): JSX.Element
} }
/*******************************************************************************
** Watch the record values - if they change, re-render widgets
*******************************************************************************/
useEffect(() =>
{
const newRenderedWidgetSections: {[name: string]: JSX.Element} = {};
for (let widgetName in renderedWidgetSections)
{
const widgetMetaData = metaData.widgets.get(widgetName);
newRenderedWidgetSections[widgetName] = getWidgetSection(widgetMetaData, childListWidgetData[widgetName]);
}
setRenderedWidgetSections(newRenderedWidgetSections);
}, [recordValuesJSON]);
/******************************************************************************* /*******************************************************************************
** render a section (full of fields) as a form ** render a section (full of fields) as a form
*******************************************************************************/ *******************************************************************************/
@ -319,25 +348,66 @@ function EntityForm(props: Props): JSX.Element
} }
/*******************************************************************************
** if we have a widget that wants to set form-field values, they can take this
** function in as a callback, and then call it with their values.
*******************************************************************************/
function setFormFieldValuesFromWidget(values: {[name: string]: any})
{
for (let key in values)
{
formikSetFieldValueFunction(key, values[key]);
}
}
/******************************************************************************* /*******************************************************************************
** render a section as a widget ** render a section as a widget
*******************************************************************************/ *******************************************************************************/
function getWidgetSection(widgetMetaData: QWidgetMetaData, widgetData: any): JSX.Element function getWidgetSection(widgetMetaData: QWidgetMetaData, widgetData: any): JSX.Element
{ {
widgetData.viewAllLink = null; if(widgetMetaData.type == "childRecordList")
widgetMetaData.showExportButton = false; {
widgetData.viewAllLink = null;
widgetMetaData.showExportButton = false;
return <RecordGridWidget return <RecordGridWidget
key={new Date().getTime()} // added so that editing values actually re-renders... key={new Date().getTime()} // added so that editing values actually re-renders...
widgetMetaData={widgetMetaData} widgetMetaData={widgetMetaData}
data={widgetData} data={widgetData}
disableRowClick disableRowClick
allowRecordEdit allowRecordEdit
allowRecordDelete allowRecordDelete
addNewRecordCallback={() => openAddChildRecord(widgetMetaData.name, widgetData)} addNewRecordCallback={() => openAddChildRecord(widgetMetaData.name, widgetData)}
editRecordCallback={(rowIndex) => openEditChildRecord(widgetMetaData.name, widgetData, rowIndex)} editRecordCallback={(rowIndex) => openEditChildRecord(widgetMetaData.name, widgetData, rowIndex)}
deleteRecordCallback={(rowIndex) => deleteChildRecord(widgetMetaData.name, widgetData, rowIndex)} deleteRecordCallback={(rowIndex) => deleteChildRecord(widgetMetaData.name, widgetData, rowIndex)}
/>; />;
}
if(widgetMetaData.type == "reportSetup")
{
return <ReportSetupWidget
key={formValues["tableName"]} // todo, is this good? it was added so that editing values actually re-renders...
isEditable={true}
widgetMetaData={widgetMetaData}
recordValues={formValues}
onSaveCallback={setFormFieldValuesFromWidget}
/>
}
if(widgetMetaData.type == "pivotTableSetup")
{
return <PivotTableSetupWidget
key={formValues["tableName"]} // todo, is this good? it was added so that editing values actually re-renders...
isEditable={true}
widgetMetaData={widgetMetaData}
recordValues={formValues}
onSaveCallback={setFormFieldValuesFromWidget}
/>
}
return (<Box>Unsupported widget type: {widgetMetaData.type}</Box>)
} }
@ -373,7 +443,21 @@ function EntityForm(props: Props): JSX.Element
///////////////////////////////////////////////// /////////////////////////////////////////////////
const tableSections = TableUtils.getSectionsForRecordSidebar(tableMetaData, [...tableMetaData.fields.keys()], (section: QTableSection) => const tableSections = TableUtils.getSectionsForRecordSidebar(tableMetaData, [...tableMetaData.fields.keys()], (section: QTableSection) =>
{ {
return section.widgetName && metaData.widgets.get(section.widgetName)?.type == "childRecordList" && metaData.widgets.get(section.widgetName)?.defaultValues?.has("manageAssociationName"); const widget = metaData.widgets.get(section.widgetName);
if(widget)
{
if(widget.type == "childRecordList" && widget.defaultValues?.has("manageAssociationName"))
{
return (true);
}
if(widget.type == "reportSetup" || widget.type == "pivotTableSetup")
{
return (true);
}
}
return (false);
}); });
setTableSections(tableSections); setTableSections(tableSections);
@ -549,13 +633,7 @@ function EntityForm(props: Props): JSX.Element
} }
const hasFields = section.fieldNames && section.fieldNames.length > 0; const hasFields = section.fieldNames && section.fieldNames.length > 0;
const hasChildRecordListWidget = section.widgetName && metaData.widgets.get(section.widgetName)?.type == "childRecordList"; if(hasFields)
if (!hasFields && !hasChildRecordListWidget)
{
continue;
}
if (hasFields)
{ {
for (let j = 0; j < section.fieldNames.length; j++) for (let j = 0; j < section.fieldNames.length; j++)
{ {
@ -599,6 +677,7 @@ function EntityForm(props: Props): JSX.Element
newRenderedWidgetSections[section.widgetName] = getWidgetSection(widgetMetaData, widgetData); newRenderedWidgetSections[section.widgetName] = getWidgetSection(widgetMetaData, widgetData);
newChildListWidgetData[section.widgetName] = widgetData; newChildListWidgetData[section.widgetName] = widgetData;
} }
////////////////////////////////////// //////////////////////////////////////
// capture the tier1 section's name // // capture the tier1 section's name //
////////////////////////////////////// //////////////////////////////////////
@ -924,51 +1003,71 @@ function EntityForm(props: Props): JSX.Element
errors, errors,
touched, touched,
isSubmitting, isSubmitting,
}) => ( setFieldValue,
<Form id={formId} autoComplete="off"> }) =>
<ScrollToFirstError /> {
if(values)
{
const newRecordValuesJSON = JSON.stringify(values);
if(recordValuesJSON != newRecordValuesJSON)
{
setRecordValuesJSON(newRecordValuesJSON);
setFormValues(values)
}
}
<Box pb={3} pt={0}> ///////////////////////////////////////////////////////////////////
<Card id={`${t1sectionName}`} sx={{overflow: "visible", pb: 2, scrollMarginTop: "100px"}} elevation={cardElevation}> // once we're in the formik form, use its setFieldValue function //
<Box display="flex" p={3} pb={1}> // over top of the default one we created globally //
<Box mr={1.5}> ///////////////////////////////////////////////////////////////////
<Avatar sx={{bgcolor: accentColor}}> formikSetFieldValueFunction = setFieldValue;
<Icon>
{tableMetaData?.iconName} return (
</Icon> <Form id={formId} autoComplete="off">
</Avatar> <ScrollToFirstError />
</Box>
<Box display="flex" alignItems="center"> <Box pb={3} pt={0}>
<MDTypography variant="h5">{formTitle}</MDTypography> <Card id={`${t1sectionName}`} sx={{overflow: "visible", pb: 2, scrollMarginTop: "100px"}} elevation={cardElevation}>
</Box> <Box display="flex" p={3} pb={1}>
</Box> <Box mr={1.5}>
{t1section && getSectionHelp(t1section)} <Avatar sx={{bgcolor: accentColor}}>
{ <Icon>
t1sectionName && formFields ? ( {tableMetaData?.iconName}
<Box px={3}> </Icon>
<Box pb={"0.25rem"} width="100%"> </Avatar>
{getFormSection(t1section, values, touched, formFields.get(t1sectionName), errors, true)}
</Box>
</Box> </Box>
) : null <Box display="flex" alignItems="center">
} <MDTypography variant="h5">{formTitle}</MDTypography>
</Card> </Box>
</Box> </Box>
{formFields && nonT1Sections.length ? nonT1Sections.map((section: QTableSection) => ( {t1section && getSectionHelp(t1section)}
<Box key={`edit-card-${section.name}`} pb={3}> {
{renderSection(section, values, touched, formFields, errors)} t1sectionName && formFields ? (
<Box px={3}>
<Box pb={"0.25rem"} width="100%">
{getFormSection(t1section, values, touched, formFields.get(t1sectionName), errors, true)}
</Box>
</Box>
) : null
}
</Card>
</Box> </Box>
)) : null} {formFields && nonT1Sections.length ? nonT1Sections.map((section: QTableSection) => (
<Box key={`edit-card-${section.name}`} pb={3}>
{renderSection(section, values, touched, formFields, errors)}
</Box>
)) : null}
<Box component="div" p={3}> <Box component="div" p={3}>
<Grid container justifyContent="flex-end" spacing={3}> <Grid container justifyContent="flex-end" spacing={3}>
<QCancelButton onClickHandler={props.isModal ? props.closeModalHandler : handleCancelClicked} disabled={isSubmitting} /> <QCancelButton onClickHandler={props.isModal ? props.closeModalHandler : handleCancelClicked} disabled={isSubmitting} />
<QSaveButton disabled={isSubmitting} /> <QSaveButton disabled={isSubmitting} />
</Grid> </Grid>
</Box> </Box>
</Form> </Form>
)} );
}}
</Formik> </Formik>
{ {