Compare commits

...

4 Commits

3 changed files with 109 additions and 67 deletions

View File

@ -63,7 +63,7 @@ interface Props
disabledFields: { [key: string]: boolean } | string[]; disabledFields: { [key: string]: boolean } | string[];
isCopy?: boolean; isCopy?: boolean;
onSubmitCallback?: (values: any) => void; onSubmitCallback?: (values: any) => void;
overrideHeading?: string overrideHeading?: string;
} }
EntityForm.defaultProps = { EntityForm.defaultProps = {
@ -128,21 +128,26 @@ function EntityForm(props: Props): JSX.Element
{ {
try try
{ {
const parts = hashParts[i].split("=") const parts = hashParts[i].split("=");
if (parts.length > 1 && parts[0] == "defaultValues") if (parts.length > 1)
{ {
defaultValues = JSON.parse(decodeURIComponent(parts[1])) as { [key: string]: any }; const name = parts[0].replace(/^#/, "");
const value = parts[1];
if (name == "defaultValues")
{
defaultValues = JSON.parse(decodeURIComponent(value)) as { [key: string]: any };
} }
if (parts.length > 1 && parts[0] == "disabledFields") if (name == "disabledFields")
{ {
disabledFields = JSON.parse(decodeURIComponent(parts[1])) as { [key: string]: any }; disabledFields = JSON.parse(decodeURIComponent(value)) as { [key: string]: any };
}
} }
} }
catch (e) catch (e)
{} {
}
} }
/******************************************************************************* /*******************************************************************************
@ -234,7 +239,7 @@ function EntityForm(props: Props): JSX.Element
const metaData = await qController.loadMetaData(); const metaData = await qController.loadMetaData();
const widgetMetaData = metaData.widgets.get(widgetName); const widgetMetaData = metaData.widgets.get(widgetName);
const newChildListWidgetData: {[name: string]: ChildRecordListData} = Object.assign({}, childListWidgetData) const newChildListWidgetData: { [name: string]: ChildRecordListData } = Object.assign({}, childListWidgetData);
if (!newChildListWidgetData[widgetName].queryOutput.records) if (!newChildListWidgetData[widgetName].queryOutput.records)
{ {
newChildListWidgetData[widgetName].queryOutput.records = []; newChildListWidgetData[widgetName].queryOutput.records = [];
@ -243,7 +248,7 @@ function EntityForm(props: Props): JSX.Element
switch (action) switch (action)
{ {
case "insert": case "insert":
newChildListWidgetData[widgetName].queryOutput.records.push({values: values}) newChildListWidgetData[widgetName].queryOutput.records.push({values: values});
break; break;
case "edit": case "edit":
newChildListWidgetData[widgetName].queryOutput.records[rowIndex] = {values: values}; newChildListWidgetData[widgetName].queryOutput.records[rowIndex] = {values: values};
@ -255,7 +260,7 @@ function EntityForm(props: Props): JSX.Element
newChildListWidgetData[widgetName].totalRows = newChildListWidgetData[widgetName].queryOutput.records.length; newChildListWidgetData[widgetName].totalRows = newChildListWidgetData[widgetName].queryOutput.records.length;
setChildListWidgetData(newChildListWidgetData); setChildListWidgetData(newChildListWidgetData);
const newRenderedWidgetSections = Object.assign({}, renderedWidgetSections) const newRenderedWidgetSections = Object.assign({}, renderedWidgetSections);
newRenderedWidgetSections[widgetName] = getWidgetSection(widgetMetaData, newChildListWidgetData[widgetName]); newRenderedWidgetSections[widgetName] = getWidgetSection(widgetMetaData, newChildListWidgetData[widgetName]);
setRenderedWidgetSections(newRenderedWidgetSections); setRenderedWidgetSections(newRenderedWidgetSections);
forceUpdate(); forceUpdate();
@ -293,11 +298,11 @@ function EntityForm(props: Props): JSX.Element
return <div>Error: No form fields in section {section.name}</div>; return <div>Error: No form fields in section {section.name}</div>;
} }
const helpRoles = [props.id ? "EDIT_SCREEN" : "INSERT_SCREEN", "WRITE_SCREENS", "ALL_SCREENS"] const helpRoles = [props.id ? "EDIT_SCREEN" : "INSERT_SCREEN", "WRITE_SCREENS", "ALL_SCREENS"];
if (omitWrapper) if (omitWrapper)
{ {
return <QDynamicForm formData={formData} record={record} helpRoles={helpRoles} helpContentKeyPrefix={`table:${tableName};`} /> return <QDynamicForm formData={formData} record={record} helpRoles={helpRoles} helpContentKeyPrefix={`table:${tableName};`} />;
} }
return <Card id={section.name} sx={{overflow: "visible", scrollMarginTop: "100px"}} elevation={cardElevation}> return <Card id={section.name} sx={{overflow: "visible", scrollMarginTop: "100px"}} elevation={cardElevation}>
@ -310,7 +315,7 @@ function EntityForm(props: Props): JSX.Element
<QDynamicForm formData={formData} record={record} helpRoles={helpRoles} helpContentKeyPrefix={`table:${tableName};`} /> <QDynamicForm formData={formData} record={record} helpRoles={helpRoles} helpContentKeyPrefix={`table:${tableName};`} />
</Box> </Box>
</Box> </Box>
</Card> </Card>;
} }
@ -332,7 +337,7 @@ function EntityForm(props: Props): JSX.Element
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)}
/> />;
} }
@ -347,7 +352,7 @@ function EntityForm(props: Props): JSX.Element
} }
else else
{ {
return renderedWidgetSections[section.widgetName] ?? <Box>Loading {section.label}...</Box> return renderedWidgetSections[section.widgetName] ?? <Box>Loading {section.label}...</Box>;
} }
} }
@ -368,7 +373,7 @@ 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") return section.widgetName && metaData.widgets.get(section.widgetName)?.type == "childRecordList" && metaData.widgets.get(section.widgetName)?.defaultValues?.has("manageAssociationName");
}); });
setTableSections(tableSections); setTableSections(tableSections);
@ -544,7 +549,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" const hasChildRecordListWidget = section.widgetName && metaData.widgets.get(section.widgetName)?.type == "childRecordList";
if (!hasFields && !hasChildRecordListWidget) if (!hasFields && !hasChildRecordListWidget)
{ {
continue; continue;
@ -701,7 +706,7 @@ function EntityForm(props: Props): JSX.Element
////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (fieldMetaData.type === QFieldType.DATE_TIME && valuesToPost[fieldName]) if (fieldMetaData.type === QFieldType.DATE_TIME && valuesToPost[fieldName])
{ {
console.log(`DateTime ${fieldName}: Initial value: [${initialValues[fieldName]}] -> [${valuesToPost[fieldName]}]`) console.log(`DateTime ${fieldName}: Initial value: [${initialValues[fieldName]}] -> [${valuesToPost[fieldName]}]`);
if (initialValues[fieldName] == valuesToPost[fieldName]) if (initialValues[fieldName] == valuesToPost[fieldName])
{ {
console.log(" - Is the same, so, deleting from the post"); console.log(" - Is the same, so, deleting from the post");
@ -734,11 +739,11 @@ function EntityForm(props: Props): JSX.Element
} }
} }
const associationsToPost: any = {} const associationsToPost: any = {};
let haveAssociationsToPost = false; let haveAssociationsToPost = false;
for (let name of Object.keys(childListWidgetData)) for (let name of Object.keys(childListWidgetData))
{ {
const manageAssociationName = metaData.widgets.get(name)?.defaultValues?.get("manageAssociationName") const manageAssociationName = metaData.widgets.get(name)?.defaultValues?.get("manageAssociationName");
if (!manageAssociationName) if (!manageAssociationName)
{ {
console.log(`Cannot send association data to backend - missing a manageAssociationName defaultValue in widget meta data for widget name ${name}`); console.log(`Cannot send association data to backend - missing a manageAssociationName defaultValue in widget meta data for widget name ${name}`);
@ -850,15 +855,15 @@ function EntityForm(props: Props): JSX.Element
const getSectionHelp = (section: QTableSection) => const getSectionHelp = (section: QTableSection) =>
{ {
const helpRoles = [props.id ? "EDIT_SCREEN" : "INSERT_SCREEN", "WRITE_SCREENS", "ALL_SCREENS"] const helpRoles = [props.id ? "EDIT_SCREEN" : "INSERT_SCREEN", "WRITE_SCREENS", "ALL_SCREENS"];
const formattedHelpContent = <HelpContent helpContents={section.helpContents} roles={helpRoles} helpContentKey={`table:${tableMetaData.name};section:${section.name}`} />; const formattedHelpContent = <HelpContent helpContents={section.helpContents} roles={helpRoles} helpContentKey={`table:${tableMetaData.name};section:${section.name}`} />;
return formattedHelpContent && ( return formattedHelpContent && (
<Box px={"1.5rem"} fontSize={"0.875rem"}> <Box px={"1.5rem"} fontSize={"0.875rem"}>
{formattedHelpContent} {formattedHelpContent}
</Box> </Box>
) );
} };
if (notAllowedError) if (notAllowedError)
{ {
@ -1007,7 +1012,7 @@ function EntityForm(props: Props): JSX.Element
function ScrollToFirstError(): JSX.Element function ScrollToFirstError(): JSX.Element
{ {
const {submitCount, isValid} = useFormikContext() const {submitCount, isValid} = useFormikContext();
useEffect(() => useEffect(() =>
{ {
@ -1037,8 +1042,8 @@ function ScrollToFirstError(): JSX.Element
} }
firstErrorMessage.scrollIntoView({block: "center"}); firstErrorMessage.scrollIntoView({block: "center"});
}, 100) }, 100);
}, [submitCount, isValid]) }, [submitCount, isValid]);
return null; return null;
} }

View File

@ -87,7 +87,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
const RENAME_OPTION = "Rename..."; const RENAME_OPTION = "Rename...";
const DELETE_OPTION = "Delete..."; const DELETE_OPTION = "Delete...";
const CLEAR_OPTION = "New View"; const CLEAR_OPTION = "New View";
const dropdownOptions = [DUPLICATE_OPTION, RENAME_OPTION, DELETE_OPTION, CLEAR_OPTION]; const NEW_REPORT_OPTION = "Create Report from Current View";
const {accentColor, accentColorLight} = useContext(QContext); const {accentColor, accentColorLight} = useContext(QContext);
@ -187,10 +187,26 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
case DELETE_OPTION: case DELETE_OPTION:
setIsDeleteFilter(true) setIsDeleteFilter(true)
break; break;
case NEW_REPORT_OPTION:
createNewReport();
break;
} }
} }
/*******************************************************************************
**
*******************************************************************************/
function createNewReport()
{
const defaultValues: {[key: string]: any} = {};
defaultValues.tableName = tableMetaData.name;
defaultValues.queryFilterJson = JSON.stringify(view.queryFilter, null, 3);
defaultValues.columnsJson = JSON.stringify(view.queryColumns, null, 3);
navigate(`${metaData.getTablePathByName("savedReport")}/create#defaultValues=${encodeURIComponent(JSON.stringify(defaultValues))}`);
}
/******************************************************************************* /*******************************************************************************
** fired when save or delete button saved on confirmation dialogs ** fired when save or delete button saved on confirmation dialogs
@ -376,6 +392,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
const hasStorePermission = metaData?.processes.has("storeSavedView"); const hasStorePermission = metaData?.processes.has("storeSavedView");
const hasDeletePermission = metaData?.processes.has("deleteSavedView"); const hasDeletePermission = metaData?.processes.has("deleteSavedView");
const hasQueryPermission = metaData?.processes.has("querySavedView"); const hasQueryPermission = metaData?.processes.has("querySavedView");
const hasSavedReportsPermission = metaData?.tables.has("savedReport");
const tooltipMaxWidth = (maxWidth: string) => const tooltipMaxWidth = (maxWidth: string) =>
{ {
@ -429,7 +446,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
</Tooltip> </Tooltip>
} }
{ {
hasStorePermission && currentSavedView != null && hasDeletePermission && currentSavedView != null &&
<Tooltip {...menuTooltipAttribs} title="Delete this saved view."> <Tooltip {...menuTooltipAttribs} title="Delete this saved view.">
<MenuItem disabled={currentSavedView === null} onClick={() => handleDropdownOptionClick(DELETE_OPTION)}> <MenuItem disabled={currentSavedView === null} onClick={() => handleDropdownOptionClick(DELETE_OPTION)}>
<ListItemIcon><Icon>delete</Icon></ListItemIcon> <ListItemIcon><Icon>delete</Icon></ListItemIcon>
@ -445,6 +462,15 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
</MenuItem> </MenuItem>
</Tooltip> </Tooltip>
} }
{
hasSavedReportsPermission &&
<Tooltip {...menuTooltipAttribs} title="Create a new Saved Report using your current view of this table as a starting point.">
<MenuItem onClick={() => handleDropdownOptionClick(NEW_REPORT_OPTION)}>
<ListItemIcon><Icon>article</Icon></ListItemIcon>
Create Report from Current View
</MenuItem>
</Tooltip>
}
<Divider/> <Divider/>
<MenuItem disabled style={{"opacity": "initial"}}><b>Your Saved Views</b></MenuItem> <MenuItem disabled style={{"opacity": "initial"}}><b>Your Saved Views</b></MenuItem>
{ {

View File

@ -47,9 +47,6 @@ import {DataGridPro, GridColDef} from "@mui/x-data-grid-pro";
import FormData from "form-data"; import FormData from "form-data";
import {Form, Formik} from "formik"; import {Form, Formik} from "formik";
import parse from "html-react-parser"; import parse from "html-react-parser";
import React, {useContext, useEffect, useState} from "react";
import {useLocation, useNavigate, useParams} from "react-router-dom";
import * as Yup from "yup";
import QContext from "QContext"; import QContext from "QContext";
import {QCancelButton, QSubmitButton} from "qqq/components/buttons/DefaultButtons"; import {QCancelButton, QSubmitButton} from "qqq/components/buttons/DefaultButtons";
import QDynamicForm from "qqq/components/forms/DynamicForm"; import QDynamicForm from "qqq/components/forms/DynamicForm";
@ -66,6 +63,9 @@ import {TABLE_VARIANT_LOCAL_STORAGE_KEY_ROOT} from "qqq/pages/records/query/Reco
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";
import ValueUtils from "qqq/utils/qqq/ValueUtils"; import ValueUtils from "qqq/utils/qqq/ValueUtils";
import React, {useContext, useEffect, useState} from "react";
import {useLocation, useNavigate, useParams} from "react-router-dom";
import * as Yup from "yup";
interface Props interface Props
@ -226,8 +226,19 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
setShowFullHelpText(!showFullHelpText); setShowFullHelpText(!showFullHelpText);
}; };
const download = (url: string, fileName: string) => const download = (processValues: {[key: string]: string}) =>
{ {
let url;
let fileName = processValues.downloadFileName;
if(processValues.serverFilePath)
{
url = `/download/${encodeURIComponent(processValues.downloadFileName)}?filePath=${encodeURIComponent(processValues.serverFilePath)}`;
}
else if(processValues.storageTableName && processValues.storageReference)
{
url = `/download/${encodeURIComponent(processValues.downloadFileName)}?storageTableName=${encodeURIComponent(processValues.storageTableName)}&storageReference=${encodeURIComponent(processValues.storageReference)}`;
}
///////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////
// todo - this could be simplified, i think? // // todo - this could be simplified, i think? //
// it was originally built like this when we had to submit full access token to backend... // // it was originally built like this when we had to submit full access token to backend... //
@ -556,7 +567,7 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
Download Download
</Box> </Box>
<Box display="flex" py={1} pr={2}> <Box display="flex" py={1} pr={2}>
<MDTypography variant="button" fontWeight="bold" onClick={() => download(`/download/${processValues.downloadFileName}?filePath=${processValues.serverFilePath}`, processValues.downloadFileName)} sx={{cursor: "pointer"}}> <MDTypography variant="button" fontWeight="bold" onClick={() => download(processValues)} sx={{cursor: "pointer"}}>
<Box display="flex" alignItems="center" gap={1} py={1} pr={2}> <Box display="flex" alignItems="center" gap={1} py={1} pr={2}>
<Icon fontSize="large">download_for_offline</Icon> <Icon fontSize="large">download_for_offline</Icon>
{processValues.downloadFileName} {processValues.downloadFileName}