mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-17 21:00:45 +00:00
Merge pull request #62 from Kingsrook/feature/CE-938-order-release-automation
Feature/ce 938 order release automation
This commit is contained in:
@ -1,12 +0,0 @@
|
||||
import {defineConfig} from "cypress";
|
||||
|
||||
export default defineConfig({
|
||||
e2e: {
|
||||
viewportHeight: 1000,
|
||||
viewportWidth: 1200,
|
||||
setupNodeEvents(on, config)
|
||||
{
|
||||
// implement node event listeners here
|
||||
},
|
||||
},
|
||||
});
|
2174
package-lock.json
generated
2174
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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.101",
|
||||
"@kingsrook/qqq-frontend-core": "1.0.102",
|
||||
"@mui/icons-material": "5.4.1",
|
||||
"@mui/material": "5.11.1",
|
||||
"@mui/styles": "5.11.1",
|
||||
|
@ -97,7 +97,7 @@ export const getAutocompleteOutlinedStyle = (isDisabled: boolean) =>
|
||||
borderColor: inputBorderColor
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const qController = Client.getInstance();
|
||||
@ -108,13 +108,13 @@ function DynamicSelect({tableName, processName, fieldName, possibleValueSourceNa
|
||||
const [options, setOptions] = useState<readonly QPossibleValue[]>([]);
|
||||
const [searchTerm, setSearchTerm] = useState(null);
|
||||
const [firstRender, setFirstRender] = useState(true);
|
||||
const [otherValuesWhenResultsWereLoaded, setOtherValuesWhenResultsWereLoaded] = useState(JSON.stringify(Object.fromEntries((otherValues))))
|
||||
const [otherValuesWhenResultsWereLoaded, setOtherValuesWhenResultsWereLoaded] = useState(JSON.stringify(Object.fromEntries((otherValues))));
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if (tableName && processName)
|
||||
{
|
||||
console.log("DynamicSelect - you may not provide both a tableName and a processName")
|
||||
console.log("DynamicSelect - you may not provide both a tableName and a processName");
|
||||
}
|
||||
if (tableName && !fieldName)
|
||||
{
|
||||
@ -233,7 +233,7 @@ function DynamicSelect({tableName, processName, fieldName, possibleValueSourceNa
|
||||
setOtherValuesWhenResultsWereLoaded(JSON.stringify(Object.fromEntries(otherValues)));
|
||||
})();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const inputChanged = (event: React.SyntheticEvent, value: string, reason: string) =>
|
||||
{
|
||||
@ -248,7 +248,7 @@ function DynamicSelect({tableName, processName, fieldName, possibleValueSourceNa
|
||||
const handleBlur = (x: any) =>
|
||||
{
|
||||
setSearchTerm(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleChanged = (event: React.SyntheticEvent, value: any | any[], reason: string, details?: string) =>
|
||||
{
|
||||
@ -280,7 +280,7 @@ function DynamicSelect({tableName, processName, fieldName, possibleValueSourceNa
|
||||
// get options whose text/label matches the input (e.g., not ids that match) //
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
return (options);
|
||||
}
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
const renderOption = (props: Object, option: any, {selected}) =>
|
||||
@ -289,13 +289,13 @@ function DynamicSelect({tableName, processName, fieldName, possibleValueSourceNa
|
||||
|
||||
try
|
||||
{
|
||||
const field = tableMetaData?.fields.get(fieldName)
|
||||
const field = tableMetaData?.fields.get(fieldName);
|
||||
if (field)
|
||||
{
|
||||
const adornment = field.getAdornment(AdornmentType.CHIP);
|
||||
if (adornment)
|
||||
{
|
||||
const color = adornment.getValue("color." + option.id) ?? "default"
|
||||
const color = adornment.getValue("color." + option.id) ?? "default";
|
||||
const iconName = adornment.getValue("icon." + option.id) ?? null;
|
||||
const iconElement = iconName ? <Icon>{iconName}</Icon> : null;
|
||||
content = (<Chip label={option.label} color={color} icon={iconElement} size="small" variant="outlined" sx={{fontWeight: 500}} />);
|
||||
@ -303,7 +303,8 @@ function DynamicSelect({tableName, processName, fieldName, possibleValueSourceNa
|
||||
}
|
||||
}
|
||||
catch (e)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
if (isMultiple)
|
||||
{
|
||||
@ -327,7 +328,7 @@ function DynamicSelect({tableName, processName, fieldName, possibleValueSourceNa
|
||||
{content}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const bulkEditSwitchChanged = () =>
|
||||
{
|
||||
@ -382,7 +383,7 @@ function DynamicSelect({tableName, processName, fieldName, possibleValueSourceNa
|
||||
option = option[0];
|
||||
}
|
||||
// @ts-ignore
|
||||
return option.label
|
||||
return option.label;
|
||||
}}
|
||||
options={options}
|
||||
loading={loading}
|
||||
@ -446,7 +447,8 @@ function DynamicSelect({tableName, processName, fieldName, possibleValueSourceNa
|
||||
id={`bulkEditSwitch-${fieldName}`}
|
||||
checked={switchChecked}
|
||||
onClick={bulkEditSwitchChanged}
|
||||
sx={{top: "-4px",
|
||||
sx={{
|
||||
top: "-4px",
|
||||
"& .MuiSwitch-track": {
|
||||
height: 20,
|
||||
borderRadius: 10,
|
||||
@ -465,7 +467,7 @@ function DynamicSelect({tableName, processName, fieldName, possibleValueSourceNa
|
||||
else
|
||||
{
|
||||
return (
|
||||
<Box mb={1.5}>
|
||||
<Box>
|
||||
{autocomplete}
|
||||
</Box>
|
||||
);
|
||||
|
@ -44,9 +44,9 @@ 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 FilterAndColumnsSetupWidget from "qqq/components/widgets/misc/FilterAndColumnsSetupWidget";
|
||||
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";
|
||||
import {FieldRule, FieldRuleAction, FieldRuleTrigger} from "qqq/models/fields/FieldRules";
|
||||
import HtmlUtils from "qqq/utils/HtmlUtils";
|
||||
import Client from "qqq/utils/qqq/Client";
|
||||
@ -88,7 +88,7 @@ EntityForm.defaultProps = {
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
let formikSetFieldValueFunction = (field: string, value: any, shouldValidate?: boolean): void =>
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
function EntityForm(props: Props): JSX.Element
|
||||
{
|
||||
@ -119,6 +119,7 @@ function EntityForm(props: Props): JSX.Element
|
||||
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
||||
|
||||
const [showEditChildForm, setShowEditChildForm] = useState(null as any);
|
||||
const [modalDataChangedCounter, setModalDataChangedCount] = useState(0);
|
||||
|
||||
const [notAllowedError, setNotAllowedError] = useState(null as string);
|
||||
|
||||
@ -204,7 +205,7 @@ function EntityForm(props: Props): JSX.Element
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
const deleteChildRecord = (name: string, widgetData: any, rowIndex: number) =>
|
||||
function deleteChildRecord(name: string, widgetData: any, rowIndex: number)
|
||||
{
|
||||
updateChildRecordList(name, "delete", rowIndex);
|
||||
};
|
||||
@ -282,6 +283,8 @@ function EntityForm(props: Props): JSX.Element
|
||||
setRenderedWidgetSections(newRenderedWidgetSections);
|
||||
forceUpdate();
|
||||
|
||||
setModalDataChangedCount(modalDataChangedCounter + 1);
|
||||
|
||||
setShowEditChildForm(null);
|
||||
}
|
||||
|
||||
@ -351,7 +354,6 @@ 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.
|
||||
@ -375,8 +377,8 @@ function EntityForm(props: Props): JSX.Element
|
||||
widgetData.viewAllLink = null;
|
||||
widgetMetaData.showExportButton = false;
|
||||
|
||||
return <RecordGridWidget
|
||||
key={new Date().getTime()} // added so that editing values actually re-renders...
|
||||
return Object.keys(childListWidgetData).length > 0 && (<RecordGridWidget
|
||||
key={`${formValues["tableName"]}-${modalDataChangedCounter}`}
|
||||
widgetMetaData={widgetMetaData}
|
||||
data={widgetData}
|
||||
disableRowClick
|
||||
@ -385,18 +387,28 @@ function EntityForm(props: Props): JSX.Element
|
||||
addNewRecordCallback={() => openAddChildRecord(widgetMetaData.name, widgetData)}
|
||||
editRecordCallback={(rowIndex) => openEditChildRecord(widgetMetaData.name, widgetData, rowIndex)}
|
||||
deleteRecordCallback={(rowIndex) => deleteChildRecord(widgetMetaData.name, widgetData, rowIndex)}
|
||||
/>;
|
||||
/>);
|
||||
}
|
||||
|
||||
if(widgetMetaData.type == "reportSetup")
|
||||
if (widgetMetaData.type == "filterAndColumnsSetup")
|
||||
{
|
||||
return <ReportSetupWidget
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if the widget metadata specifies a table name, set form values to that so widget knows which to use //
|
||||
// (for the case when it is not being specified by a separate field in the record) //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if (widgetData?.tableName)
|
||||
{
|
||||
formValues["tableName"] = widgetData?.tableName;
|
||||
}
|
||||
|
||||
return <FilterAndColumnsSetupWidget
|
||||
key={formValues["tableName"]} // todo, is this good? it was added so that editing values actually re-renders...
|
||||
isEditable={true}
|
||||
widgetMetaData={widgetMetaData}
|
||||
widgetData={widgetData}
|
||||
recordValues={formValues}
|
||||
onSaveCallback={setFormFieldValuesFromWidget}
|
||||
/>
|
||||
/>;
|
||||
}
|
||||
|
||||
if (widgetMetaData.type == "pivotTableSetup")
|
||||
@ -407,7 +419,7 @@ function EntityForm(props: Props): JSX.Element
|
||||
widgetMetaData={widgetMetaData}
|
||||
recordValues={formValues}
|
||||
onSaveCallback={setFormFieldValuesFromWidget}
|
||||
/>
|
||||
/>;
|
||||
}
|
||||
|
||||
if (widgetMetaData.type == "dynamicForm")
|
||||
@ -420,10 +432,10 @@ function EntityForm(props: Props): JSX.Element
|
||||
recordValues={formValues}
|
||||
record={record}
|
||||
onSaveCallback={setFormFieldValuesFromWidget}
|
||||
/>
|
||||
/>;
|
||||
}
|
||||
|
||||
return (<Box>Unsupported widget type: {widgetMetaData.type}</Box>)
|
||||
return (<Box>Unsupported widget type: {widgetMetaData.type}</Box>);
|
||||
}
|
||||
|
||||
|
||||
@ -469,6 +481,8 @@ function EntityForm(props: Props): JSX.Element
|
||||
//////////////////
|
||||
// initial load //
|
||||
//////////////////
|
||||
useEffect(() =>
|
||||
{
|
||||
if (!asyncLoadInited)
|
||||
{
|
||||
setAsyncLoadInited(true);
|
||||
@ -488,7 +502,7 @@ function EntityForm(props: Props): JSX.Element
|
||||
/////////////////////////////////////////////////
|
||||
const tableSections = TableUtils.getSectionsForRecordSidebar(tableMetaData, [...tableMetaData.fields.keys()], (section: QTableSection) =>
|
||||
{
|
||||
const widget = metaData.widgets.get(section.widgetName);
|
||||
const widget = metaData?.widgets.get(section.widgetName);
|
||||
if (widget)
|
||||
{
|
||||
if (widget.type == "childRecordList" && widget.defaultValues?.has("manageAssociationName"))
|
||||
@ -496,7 +510,7 @@ function EntityForm(props: Props): JSX.Element
|
||||
return (true);
|
||||
}
|
||||
|
||||
if(widget.type == "reportSetup" || widget.type == "pivotTableSetup" || widget.type == "dynamicForm")
|
||||
if (widget.type == "filterAndColumnsSetup" || widget.type == "pivotTableSetup" || widget.type == "dynamicForm")
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
@ -719,7 +733,7 @@ function EntityForm(props: Props): JSX.Element
|
||||
}
|
||||
else
|
||||
{
|
||||
const widgetMetaData = metaData.widgets.get(section.widgetName);
|
||||
const widgetMetaData = metaData?.widgets.get(section.widgetName);
|
||||
const widgetData = await qController.widget(widgetMetaData.name, makeQueryStringWithIdAndObject(tableMetaData, defaultValues));
|
||||
|
||||
newRenderedWidgetSections[section.widgetName] = getWidgetSection(widgetMetaData, widgetData);
|
||||
@ -751,6 +765,7 @@ function EntityForm(props: Props): JSX.Element
|
||||
forceUpdate();
|
||||
})();
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
@ -870,11 +885,22 @@ function EntityForm(props: Props): JSX.Element
|
||||
let haveAssociationsToPost = false;
|
||||
for (let name of Object.keys(childListWidgetData))
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if cannot find association name, continue loop, since cannot tell backend which association this is for //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
const manageAssociationName = metaData.widgets.get(name)?.defaultValues?.get("manageAssociationName");
|
||||
if (!manageAssociationName)
|
||||
{
|
||||
console.log(`Cannot send association data to backend - missing a manageAssociationName defaultValue in widget meta data for widget name ${name}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if the records array exists, add to associations to post - note: even if empty list, the backend will expect this //
|
||||
// association name to be present if it is to act on it (for the case when all associations have been deleted) //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if (childListWidgetData[name].queryOutput.records)
|
||||
{
|
||||
associationsToPost[manageAssociationName] = [];
|
||||
haveAssociationsToPost = true;
|
||||
for (let i = 0; i < childListWidgetData[name].queryOutput?.records?.length; i++)
|
||||
@ -882,6 +908,7 @@ function EntityForm(props: Props): JSX.Element
|
||||
associationsToPost[manageAssociationName].push(childListWidgetData[name].queryOutput.records[i].values);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (haveAssociationsToPost)
|
||||
{
|
||||
valuesToPost["associations"] = JSON.stringify(associationsToPost);
|
||||
@ -1005,14 +1032,14 @@ function EntityForm(props: Props): JSX.Element
|
||||
const queryParamsArray: string[] = [];
|
||||
if (props.id)
|
||||
{
|
||||
queryParamsArray.push(`${tableMetaData.primaryKeyField}=${encodeURIComponent(props.id)}`)
|
||||
queryParamsArray.push(`${tableMetaData.primaryKeyField}=${encodeURIComponent(props.id)}`);
|
||||
}
|
||||
|
||||
if (object)
|
||||
{
|
||||
for (let key in object)
|
||||
{
|
||||
queryParamsArray.push(`${key}=${encodeURIComponent(object[key])}`)
|
||||
queryParamsArray.push(`${key}=${encodeURIComponent(object[key])}`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1194,7 +1221,7 @@ function EntityForm(props: Props): JSX.Element
|
||||
setFieldValue(fieldName, valueChangesToMake[fieldName], false);
|
||||
}
|
||||
|
||||
setFormValues(formValues)
|
||||
setFormValues(formValues);
|
||||
setFormValuesJSON(JSON.stringify(values));
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ function QBreadcrumbs({icon, title, route, light}: Props): JSX.Element
|
||||
}
|
||||
|
||||
return (routeToLabel(route));
|
||||
}
|
||||
};
|
||||
|
||||
let pageTitle = branding?.appName ?? "";
|
||||
const fullRoutes: string[] = [];
|
||||
@ -94,9 +94,9 @@ function QBreadcrumbs({icon, title, route, light}: Props): JSX.Element
|
||||
{
|
||||
////////////////////////////////////////////////////////
|
||||
// avoid showing "saved view" as a breadcrumb element //
|
||||
// e.g., if at /app/table/savedView/1 (so where i==2) //
|
||||
// e.g., if at /app/table/savedView/1 //
|
||||
////////////////////////////////////////////////////////
|
||||
if(routes[i] === "savedView" && i == 2)
|
||||
if (routes[i] === "savedView" && i == routes.length - 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -25,7 +25,8 @@ import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QT
|
||||
import {QJobComplete} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobComplete";
|
||||
import {QJobError} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobError";
|
||||
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
|
||||
import {Alert, Box, Button} from "@mui/material";
|
||||
import {Alert, Button} from "@mui/material";
|
||||
import Box from "@mui/material/Box";
|
||||
import Dialog from "@mui/material/Dialog";
|
||||
import DialogActions from "@mui/material/DialogActions";
|
||||
import DialogContent from "@mui/material/DialogContent";
|
||||
@ -94,12 +95,12 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
|
||||
const {accentColor, accentColorLight, userId: currentUserId} = useContext(QContext);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// this component is used by <RecordQuery> - but that component has different usages - //
|
||||
// e.g., the full-fledged query screen, but also, within other screens (e.g., a modal //
|
||||
// under the ReportSetupWidget). So, there are some behaviors we only want when we're //
|
||||
// on the full-fledged query screen, such as changing the URL with saved view ids. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
// under the FilterAndColumnsSetupWidget). So, there are some behaviors we only want when //
|
||||
// we're on the full-fledged query screen, such as changing the URL with saved view ids. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
const isQueryScreen = queryScreenUsage == "queryScreen";
|
||||
|
||||
const openSavedViewsMenu = (event: any) => setSavedViewsMenu(event.currentTarget);
|
||||
|
@ -79,6 +79,8 @@ interface BasicAndAdvancedQueryControlsProps
|
||||
|
||||
queryScreenUsage: QueryScreenUsage;
|
||||
|
||||
allowVariables?: boolean;
|
||||
|
||||
mode: string;
|
||||
setMode: (mode: string) => void;
|
||||
}
|
||||
@ -676,6 +678,7 @@ const BasicAndAdvancedQueryControls = forwardRef((props: BasicAndAdvancedQueryCo
|
||||
|
||||
return (<QuickFilter
|
||||
key={fieldName}
|
||||
allowVariables={props.allowVariables}
|
||||
fullFieldName={fieldName}
|
||||
tableMetaData={tableMetaData}
|
||||
updateCriteria={updateQuickCriteria}
|
||||
@ -701,6 +704,7 @@ const BasicAndAdvancedQueryControls = forwardRef((props: BasicAndAdvancedQueryCo
|
||||
updateCriteria={updateQuickCriteria}
|
||||
criteriaParam={getQuickCriteriaParam(fieldName)}
|
||||
fieldMetaData={field}
|
||||
allowVariables={props.allowVariables}
|
||||
defaultOperator={defaultOperator}
|
||||
queryScreenUsage={queryScreenUsage}
|
||||
handleRemoveQuickFilterField={handleRemoveQuickFilterField} />);
|
||||
|
@ -179,6 +179,7 @@ export const CustomFilterPanel = forwardRef<any, GridFilterPanelProps>(
|
||||
updateCriteria={(newCriteria, needDebounce) => updateCriteria(newCriteria, index, needDebounce)}
|
||||
removeCriteria={() => removeCriteria(index)}
|
||||
updateBooleanOperator={(newValue) => updateBooleanOperator(newValue)}
|
||||
allowVariables={props.allowVariables}
|
||||
queryScreenUsage={props.queryScreenUsage}
|
||||
/>
|
||||
{/*JSON.stringify(criteria)*/}
|
||||
|
@ -199,6 +199,7 @@ interface FilterCriteriaRowProps
|
||||
removeCriteria: () => void;
|
||||
updateBooleanOperator: (newValue: string) => void;
|
||||
queryScreenUsage?: QueryScreenUsage;
|
||||
allowVariables?: boolean;
|
||||
}
|
||||
|
||||
FilterCriteriaRow.defaultProps =
|
||||
@ -267,7 +268,7 @@ export function validateCriteria(criteria: QFilterCriteria, operatorSelectedValu
|
||||
return {criteriaIsValid, criteriaStatusTooltip};
|
||||
}
|
||||
|
||||
export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria, booleanOperator, updateCriteria, removeCriteria, updateBooleanOperator, queryScreenUsage}: FilterCriteriaRowProps): JSX.Element
|
||||
export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria, booleanOperator, updateCriteria, removeCriteria, updateBooleanOperator, queryScreenUsage, allowVariables}: FilterCriteriaRowProps): JSX.Element
|
||||
{
|
||||
// console.log(`FilterCriteriaRow: criteria: ${JSON.stringify(criteria)}`);
|
||||
const [operatorSelectedValue, setOperatorSelectedValue] = useState(null as OperatorOption);
|
||||
@ -516,6 +517,7 @@ export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria,
|
||||
table={fieldTable}
|
||||
valueChangeHandler={(event, valueIndex, newValue) => handleValueChange(event, valueIndex, newValue)}
|
||||
queryScreenUsage={queryScreenUsage}
|
||||
allowVariables={allowVariables}
|
||||
/>
|
||||
</Box>
|
||||
<Box display="inline-block">
|
||||
|
@ -39,7 +39,7 @@ import FilterCriteriaPaster from "qqq/components/query/FilterCriteriaPaster";
|
||||
import {OperatorOption, ValueMode} from "qqq/components/query/FilterCriteriaRow";
|
||||
import {QueryScreenUsage} from "qqq/pages/records/query/RecordQuery";
|
||||
import ValueUtils from "qqq/utils/qqq/ValueUtils";
|
||||
import React, {SyntheticEvent, useReducer, useState} from "react";
|
||||
import React, {SyntheticEvent, useReducer} from "react";
|
||||
|
||||
interface Props
|
||||
{
|
||||
@ -50,6 +50,7 @@ interface Props
|
||||
valueChangeHandler: (event: React.ChangeEvent | SyntheticEvent, valueIndex?: number | "all", newValue?: any) => void;
|
||||
initiallyOpenMultiValuePvs?: boolean;
|
||||
queryScreenUsage?: QueryScreenUsage;
|
||||
allowVariables?: boolean;
|
||||
}
|
||||
|
||||
FilterCriteriaRowValues.defaultProps =
|
||||
@ -187,10 +188,9 @@ export const makeTextField = (field: QFieldMetaData, criteria: QFilterCriteriaWi
|
||||
};
|
||||
|
||||
|
||||
function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueChangeHandler, initiallyOpenMultiValuePvs, queryScreenUsage}: Props): JSX.Element
|
||||
function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueChangeHandler, initiallyOpenMultiValuePvs, queryScreenUsage, allowVariables}: Props): JSX.Element
|
||||
{
|
||||
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
||||
const [allowVariables, setAllowVariables] = useState(queryScreenUsage == "reportSetup");
|
||||
|
||||
if (!operatorOption)
|
||||
{
|
||||
|
@ -52,6 +52,7 @@ interface QuickFilterProps
|
||||
defaultOperator?: QCriteriaOperator;
|
||||
handleRemoveQuickFilterField?: (fieldName: string) => void;
|
||||
queryScreenUsage?: QueryScreenUsage;
|
||||
allowVariables?: boolean;
|
||||
}
|
||||
|
||||
QuickFilter.defaultProps =
|
||||
@ -141,7 +142,7 @@ const getOperatorSelectedValue = (operatorOptions: OperatorOption[], criteria: Q
|
||||
** Component to render a QuickFilter - that is - a button, with a Menu under it,
|
||||
** with Operator and Value controls.
|
||||
*******************************************************************************/
|
||||
export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData, criteriaParam, updateCriteria, defaultOperator, handleRemoveQuickFilterField, queryScreenUsage}: QuickFilterProps): JSX.Element
|
||||
export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData, criteriaParam, updateCriteria, defaultOperator, handleRemoveQuickFilterField, queryScreenUsage, allowVariables}: QuickFilterProps): JSX.Element
|
||||
{
|
||||
const operatorOptions = fieldMetaData ? getOperatorOptions(tableMetaData, fullFieldName) : [];
|
||||
const [_, tableForField] = TableUtils.getFieldAndTable(tableMetaData, fullFieldName);
|
||||
@ -549,6 +550,7 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData
|
||||
criteria={criteria}
|
||||
field={fieldMetaData}
|
||||
table={tableForField}
|
||||
allowVariables={allowVariables}
|
||||
valueChangeHandler={(event, valueIndex, newValue) => handleValueChange(event, valueIndex, newValue)}
|
||||
initiallyOpenMultiValuePvs={true} // todo - maybe not?
|
||||
/>
|
||||
|
@ -40,10 +40,10 @@ 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 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 ReportSetupWidget from "qqq/components/widgets/misc/ReportSetupWidget";
|
||||
import ScriptViewer from "qqq/components/widgets/misc/ScriptViewer";
|
||||
import StepperCard from "qqq/components/widgets/misc/StepperCard";
|
||||
import USMapWidget from "qqq/components/widgets/misc/USMapWidget";
|
||||
@ -598,9 +598,9 @@ function DashboardWidgets({widgetMetaDataList, tableName, entityPrimaryKey, reco
|
||||
)
|
||||
}
|
||||
{
|
||||
widgetMetaData.type === "reportSetup" && (
|
||||
widgetData && widgetData[i] && widgetData[i].queryParams &&
|
||||
<ReportSetupWidget isEditable={false} widgetMetaData={widgetMetaData} recordValues={convertQRecordValuesFromMapToObject(record)} onSaveCallback={() =>
|
||||
widgetMetaData.type === "filterAndColumnsSetup" && (
|
||||
widgetData && widgetData[i] &&
|
||||
<FilterAndColumnsSetupWidget isEditable={false} widgetMetaData={widgetMetaData} widgetData={widgetData[i]} recordValues={convertQRecordValuesFromMapToObject(record)} onSaveCallback={() =>
|
||||
{
|
||||
}} />
|
||||
)
|
||||
|
@ -22,6 +22,8 @@
|
||||
|
||||
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||
import {QWidgetMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QWidgetMetaData";
|
||||
import {QCriteriaOperator} from "@kingsrook/qqq-frontend-core/lib/model/query/QCriteriaOperator";
|
||||
import {QFilterCriteria} from "@kingsrook/qqq-frontend-core/lib/model/query/QFilterCriteria";
|
||||
import {QQueryFilter} from "@kingsrook/qqq-frontend-core/lib/model/query/QQueryFilter";
|
||||
import {Alert, Collapse} from "@mui/material";
|
||||
import Box from "@mui/material/Box";
|
||||
@ -42,15 +44,16 @@ import Client from "qqq/utils/qqq/Client";
|
||||
import FilterUtils from "qqq/utils/qqq/FilterUtils";
|
||||
import React, {useContext, useEffect, useRef, useState} from "react";
|
||||
|
||||
interface ReportSetupWidgetProps
|
||||
interface FilterAndColumnsSetupWidgetProps
|
||||
{
|
||||
isEditable: boolean;
|
||||
widgetMetaData: QWidgetMetaData;
|
||||
widgetData: any;
|
||||
recordValues: { [name: string]: any };
|
||||
onSaveCallback?: (values: { [name: string]: any }) => void;
|
||||
}
|
||||
|
||||
ReportSetupWidget.defaultProps = {
|
||||
FilterAndColumnsSetupWidget.defaultProps = {
|
||||
onSaveCallback: null
|
||||
};
|
||||
|
||||
@ -80,9 +83,10 @@ const qController = Client.getInstance();
|
||||
/*******************************************************************************
|
||||
** Component for editing the main setup of a report - that is: filter & columns
|
||||
*******************************************************************************/
|
||||
export default function ReportSetupWidget({isEditable, widgetMetaData, recordValues, onSaveCallback}: ReportSetupWidgetProps): JSX.Element
|
||||
export default function FilterAndColumnsSetupWidget({isEditable, widgetMetaData, widgetData, recordValues, onSaveCallback}: FilterAndColumnsSetupWidgetProps): JSX.Element
|
||||
{
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [hideColumns, setHideColumns] = useState(widgetData?.hideColumns);
|
||||
const [tableMetaData, setTableMetaData] = useState(null as QTableMetaData);
|
||||
|
||||
const [alertContent, setAlertContent] = useState(null as string);
|
||||
@ -101,15 +105,42 @@ export default function ReportSetupWidget({isEditable, widgetMetaData, recordVal
|
||||
/////////////////////////////
|
||||
// load values from record //
|
||||
/////////////////////////////
|
||||
let queryFilter = recordValues["queryFilterJson"] && JSON.parse(recordValues["queryFilterJson"]) as QQueryFilter;
|
||||
let columns: QQueryColumns = null;
|
||||
let usingDefaultEmptyFilter = false;
|
||||
let queryFilter = recordValues["queryFilterJson"] && JSON.parse(recordValues["queryFilterJson"]) as QQueryFilter;
|
||||
const defaultFilterFields = widgetData?.filterDefaultFieldNames;
|
||||
if (!queryFilter)
|
||||
{
|
||||
queryFilter = new QQueryFilter();
|
||||
if (defaultFilterFields?.length == 0)
|
||||
{
|
||||
usingDefaultEmptyFilter = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
queryFilter = Object.assign(new QQueryFilter(), queryFilter);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if there are default fields from which a query should be seeded, add/update the filter with them //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if (defaultFilterFields?.length > 0)
|
||||
{
|
||||
defaultFilterFields.forEach((fieldName: string) =>
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if a value for the default field exists, remove the criteria for it in our query first //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
queryFilter.criteria = queryFilter.criteria?.filter(c => c.fieldName != fieldName);
|
||||
|
||||
if (recordValues[fieldName])
|
||||
{
|
||||
queryFilter.addCriteria(new QFilterCriteria(fieldName, QCriteriaOperator.EQUALS, [recordValues[fieldName]]));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let columns: QQueryColumns = null;
|
||||
if (recordValues["columnsJson"])
|
||||
{
|
||||
columns = QQueryColumns.buildFromJSON(recordValues["columnsJson"]);
|
||||
@ -120,11 +151,20 @@ export default function ReportSetupWidget({isEditable, widgetMetaData, recordVal
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
useEffect(() =>
|
||||
{
|
||||
if (recordValues["tableName"] && (tableMetaData == null || tableMetaData.name != recordValues["tableName"]))
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if a default table name specified, use it, otherwise use it from the record values //
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
let tableName = widgetData?.tableName;
|
||||
if (!tableName && recordValues["tableName"] && (tableMetaData == null || tableMetaData.name != recordValues["tableName"]))
|
||||
{
|
||||
tableName = recordValues["tableName"];
|
||||
}
|
||||
|
||||
if (tableName)
|
||||
{
|
||||
(async () =>
|
||||
{
|
||||
const tableMetaData = await qController.loadTableMetaData(recordValues["tableName"]);
|
||||
const tableMetaData = await qController.loadTableMetaData(tableName);
|
||||
setTableMetaData(tableMetaData);
|
||||
|
||||
const queryFilterForFrontend = Object.assign({}, queryFilter);
|
||||
@ -132,7 +172,7 @@ export default function ReportSetupWidget({isEditable, widgetMetaData, recordVal
|
||||
setFrontendQueryFilter(queryFilterForFrontend);
|
||||
})();
|
||||
}
|
||||
}, [recordValues]);
|
||||
}, [JSON.stringify(recordValues)]);
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -140,8 +180,27 @@ export default function ReportSetupWidget({isEditable, widgetMetaData, recordVal
|
||||
*******************************************************************************/
|
||||
function openEditor()
|
||||
{
|
||||
let missingRequiredFields = [] as string[];
|
||||
widgetData?.filterDefaultFieldNames?.forEach((fieldName: string) =>
|
||||
{
|
||||
if (!recordValues[fieldName])
|
||||
{
|
||||
missingRequiredFields.push(tableMetaData.fields.get(fieldName).label);
|
||||
}
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// display an alert and return if any required fields are missing //
|
||||
////////////////////////////////////////////////////////////////////
|
||||
if (missingRequiredFields.length > 0)
|
||||
{
|
||||
setAlertContent("The following fields must first be selected to edit the filter: '" + missingRequiredFields.join(", ") + "'");
|
||||
return;
|
||||
}
|
||||
|
||||
if (recordValues["tableName"])
|
||||
{
|
||||
setAlertContent(null);
|
||||
setModalOpen(true);
|
||||
}
|
||||
}
|
||||
@ -271,9 +330,16 @@ export default function ReportSetupWidget({isEditable, widgetMetaData, recordVal
|
||||
const selectTableFirstTooltipTitle = tableMetaData ? null : "You must select a table before you can set up your report filters and columns";
|
||||
const labelAdditionalElementsRight: JSX.Element[] = [];
|
||||
if (isEditable)
|
||||
{
|
||||
if (!hideColumns)
|
||||
{
|
||||
labelAdditionalElementsRight.push(<HeaderLinkButtonComponent key="filterAndColumnsHeader" label="Edit Filters and Columns" onClickCallback={openEditor} disabled={tableMetaData == null} disabledTooltip={selectTableFirstTooltipTitle} />);
|
||||
}
|
||||
else
|
||||
{
|
||||
labelAdditionalElementsRight.push(<HeaderLinkButtonComponent key="filterAndColumnsHeader" label="Edit Filters" onClickCallback={openEditor} disabled={tableMetaData == null} disabledTooltip={selectTableFirstTooltipTitle} />);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return (<Widget widgetMetaData={widgetMetaData} labelAdditionalElementsRight={labelAdditionalElementsRight}>
|
||||
@ -306,11 +372,12 @@ export default function ReportSetupWidget({isEditable, widgetMetaData, recordVal
|
||||
</Tooltip>
|
||||
}
|
||||
{
|
||||
!isEditable && <Box color={colors.gray.main}>Your report has no filters.</Box>
|
||||
!isEditable && <Box color={colors.gray.main}>No filters are configured.</Box>
|
||||
}
|
||||
</Box>
|
||||
}
|
||||
</Box>
|
||||
{!hideColumns && (
|
||||
<Box pt="1rem">
|
||||
<h5>Columns</h5>
|
||||
<Box display="flex" flexWrap="wrap" fontSize="1rem">
|
||||
@ -328,12 +395,13 @@ export default function ReportSetupWidget({isEditable, widgetMetaData, recordVal
|
||||
</Tooltip>
|
||||
}
|
||||
{
|
||||
!isEditable && <Box color={colors.gray.main}>Your report has no columns.</Box>
|
||||
!isEditable && <Box color={colors.gray.main}>No columns are selected.</Box>
|
||||
}
|
||||
</Box>
|
||||
}
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
{
|
||||
modalOpen &&
|
||||
<Modal open={modalOpen} onClose={(event, reason) => closeEditor(event, reason)}>
|
||||
@ -349,6 +417,7 @@ export default function ReportSetupWidget({isEditable, widgetMetaData, recordVal
|
||||
}
|
||||
{
|
||||
tableMetaData && <RecordQuery
|
||||
allowVariables={widgetData?.allowVariables}
|
||||
ref={recordQueryRef}
|
||||
table={tableMetaData}
|
||||
usage="reportSetup"
|
@ -39,9 +39,9 @@ import colors from "qqq/assets/theme/base/colors";
|
||||
import {QCancelButton, QSaveButton} from "qqq/components/buttons/DefaultButtons";
|
||||
import FieldAutoComplete from "qqq/components/misc/FieldAutoComplete";
|
||||
import HelpContent, {hasHelpContent} from "qqq/components/misc/HelpContent";
|
||||
import {buttonSX, unborderedButtonSX} from "qqq/components/widgets/misc/FilterAndColumnsSetupWidget";
|
||||
import {PivotTableGroupByElement} from "qqq/components/widgets/misc/PivotTableGroupByElement";
|
||||
import {PivotTableValueElement} from "qqq/components/widgets/misc/PivotTableValueElement";
|
||||
import {buttonSX, unborderedButtonSX} from "qqq/components/widgets/misc/ReportSetupWidget";
|
||||
import Widget, {HeaderToggleComponent} from "qqq/components/widgets/Widget";
|
||||
import {PivotObjectKey, PivotTableDefinition, PivotTableFunction, pivotTableFunctionLabels, PivotTableGroupBy, PivotTableValue} from "qqq/models/misc/PivotTableDefinitionModels";
|
||||
import QQueryColumns from "qqq/models/query/QQueryColumns";
|
||||
|
@ -40,7 +40,7 @@ import {Link, useNavigate} from "react-router-dom";
|
||||
export interface ChildRecordListData extends WidgetData
|
||||
{
|
||||
title: string;
|
||||
queryOutput: {records: {values: any}[]}
|
||||
queryOutput: { records: { values: any }[] };
|
||||
childTableMetaData: QTableMetaData;
|
||||
tablePath: string;
|
||||
viewAllLink: string;
|
||||
@ -75,9 +75,9 @@ function RecordGridWidget({widgetMetaData, data, addNewRecordCallback, disableRo
|
||||
{
|
||||
const instance = useRef({timer: null});
|
||||
const [rows, setRows] = useState([]);
|
||||
const [records, setRecords] = useState([] as QRecord[])
|
||||
const [records, setRecords] = useState([] as QRecord[]);
|
||||
const [columns, setColumns] = useState([]);
|
||||
const [allColumns, setAllColumns] = useState([])
|
||||
const [allColumns, setAllColumns] = useState([]);
|
||||
const [csv, setCsv] = useState(null as string);
|
||||
const [fileName, setFileName] = useState(null as string);
|
||||
const [gridMouseDownX, setGridMouseDownX] = useState(0);
|
||||
@ -123,7 +123,7 @@ function RecordGridWidget({widgetMetaData, data, addNewRecordCallback, disableRo
|
||||
if (data.defaultValuesForNewChildRecords[columns[i].field])
|
||||
{
|
||||
columns.splice(i, 1);
|
||||
i--
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -145,19 +145,19 @@ function RecordGridWidget({widgetMetaData, data, addNewRecordCallback, disableRo
|
||||
return <Box>
|
||||
{allowRecordEdit && <IconButton onClick={() => editRecordCallback(params.row.__rowIndex)}><Icon>edit</Icon></IconButton>}
|
||||
{allowRecordDelete && <IconButton onClick={() => deleteRecordCallback(params.row.__rowIndex)}><Icon>delete</Icon></IconButton>}
|
||||
</Box>
|
||||
})
|
||||
</Box>;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
setRows(rows);
|
||||
setRecords(records)
|
||||
setRecords(records);
|
||||
setColumns(columns);
|
||||
|
||||
let csv = "";
|
||||
for (let i = 0; i < allColumns.length; i++)
|
||||
{
|
||||
csv += `${i > 0 ? "," : ""}"${ValueUtils.cleanForCsv(allColumns[i].headerName)}"`
|
||||
csv += `${i > 0 ? "," : ""}"${ValueUtils.cleanForCsv(allColumns[i].headerName)}"`;
|
||||
}
|
||||
csv += "\n";
|
||||
|
||||
@ -165,8 +165,8 @@ function RecordGridWidget({widgetMetaData, data, addNewRecordCallback, disableRo
|
||||
{
|
||||
for (let j = 0; j < allColumns.length; j++)
|
||||
{
|
||||
const value = records[i].displayValues.get(allColumns[j].field) ?? records[i].values.get(allColumns[j].field)
|
||||
csv += `${j > 0 ? "," : ""}"${ValueUtils.cleanForCsv(value)}"`
|
||||
const value = records[i].displayValues.get(allColumns[j].field) ?? records[i].values.get(allColumns[j].field);
|
||||
csv += `${j > 0 ? "," : ""}"${ValueUtils.cleanForCsv(value)}"`;
|
||||
}
|
||||
csv += "\n";
|
||||
}
|
||||
@ -188,7 +188,7 @@ function RecordGridWidget({widgetMetaData, data, addNewRecordCallback, disableRo
|
||||
<Typography key={"viewAllLink"} variant="body2" p={2} display="inline" fontSize=".875rem" pt="0" position="relative">
|
||||
<Link to={data.viewAllLink}>View All</Link>
|
||||
</Typography>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
///////////////////
|
||||
@ -202,7 +202,7 @@ function RecordGridWidget({widgetMetaData, data, addNewRecordCallback, disableRo
|
||||
|
||||
if (data.totalRows && data.queryOutput.records.length < data.totalRows)
|
||||
{
|
||||
tooltipTitle = "Export these " + data.queryOutput.records.length + " records."
|
||||
tooltipTitle = "Export these " + data.queryOutput.records.length + " records.";
|
||||
if (data.viewAllLink)
|
||||
{
|
||||
tooltipTitle += "\nClick View All to export all records.";
|
||||
@ -218,9 +218,9 @@ function RecordGridWidget({widgetMetaData, data, addNewRecordCallback, disableRo
|
||||
}
|
||||
else
|
||||
{
|
||||
alert("There is no data available to export.")
|
||||
}
|
||||
alert("There is no data available to export.");
|
||||
}
|
||||
};
|
||||
|
||||
if (widgetMetaData?.showExportButton)
|
||||
{
|
||||
@ -234,7 +234,7 @@ function RecordGridWidget({widgetMetaData, data, addNewRecordCallback, disableRo
|
||||
////////////////////
|
||||
// add new button //
|
||||
////////////////////
|
||||
const labelAdditionalComponentsRight: LabelComponent[] = []
|
||||
const labelAdditionalComponentsRight: LabelComponent[] = [];
|
||||
if (data && data.canAddChildRecord)
|
||||
{
|
||||
let disabledFields = data.disabledFieldsForNewChildRecords;
|
||||
@ -242,7 +242,7 @@ function RecordGridWidget({widgetMetaData, data, addNewRecordCallback, disableRo
|
||||
{
|
||||
disabledFields = data.defaultValuesForNewChildRecords;
|
||||
}
|
||||
labelAdditionalComponentsRight.push(new AddNewRecordButton(data.childTableMetaData, data.defaultValuesForNewChildRecords, "Add new", disabledFields, addNewRecordCallback))
|
||||
labelAdditionalComponentsRight.push(new AddNewRecordButton(data.childTableMetaData, data.defaultValuesForNewChildRecords, "Add new", disabledFields, addNewRecordCallback));
|
||||
}
|
||||
|
||||
|
||||
@ -258,8 +258,8 @@ function RecordGridWidget({widgetMetaData, data, addNewRecordCallback, disableRo
|
||||
|
||||
(async () =>
|
||||
{
|
||||
const qInstance = await qController.loadMetaData()
|
||||
let tablePath = qInstance.getTablePathByName(data.childTableMetaData.name)
|
||||
const qInstance = await qController.loadMetaData();
|
||||
let tablePath = qInstance.getTablePathByName(data.childTableMetaData.name);
|
||||
if (tablePath)
|
||||
{
|
||||
tablePath = `${tablePath}/${params.row[data.childTableMetaData.primaryKeyField]}`;
|
||||
@ -304,8 +304,8 @@ function RecordGridWidget({widgetMetaData, data, addNewRecordCallback, disableRo
|
||||
labelAdditionalComponentsRight={labelAdditionalComponentsRight}
|
||||
labelBoxAdditionalSx={{position: "relative", top: "-0.375rem"}}
|
||||
>
|
||||
<Box mx={-2} mb={-3}>
|
||||
<Box className="recordGridWidget">
|
||||
<Box mx={-3} mb={-3}>
|
||||
<Box>
|
||||
<DataGridPro
|
||||
autoHeight
|
||||
sx={{
|
||||
|
@ -94,7 +94,7 @@ const BACKOFF_AMOUNT = 1.5;
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
let formikSetFieldValueFunction = (field: string, value: any, shouldValidate?: boolean): void =>
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, isReport, recordIds, closeModalHandler, forceReInit, overrideLabel}: Props): JSX.Element
|
||||
{
|
||||
@ -311,12 +311,12 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
const queryStringParts: string[] = [];
|
||||
for (let name in processValues)
|
||||
{
|
||||
queryStringParts.push(`${name}=${encodeURIComponent(processValues[name])}`)
|
||||
queryStringParts.push(`${name}=${encodeURIComponent(processValues[name])}`);
|
||||
}
|
||||
|
||||
const renderedWidget = (<Box m={-2}>
|
||||
<DashboardWidgets widgetMetaDataList={[widgetMetaData]} omitWrappingGridContainer={true} childUrlParams={queryStringParts.join("&")} />
|
||||
</Box>)
|
||||
</Box>);
|
||||
renderedWidgets[activeStep.name][widgetName] = renderedWidget;
|
||||
return renderedWidget;
|
||||
}
|
||||
@ -367,8 +367,8 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
</MDTypography>
|
||||
<Box component="div" py={3}>
|
||||
<Grid container justifyContent="flex-end" spacing={3}>
|
||||
{isModal ? <QCancelButton onClickHandler={handleCancelClicked} disabled={false} label="Close" />
|
||||
: !isWidget && <QCancelButton onClickHandler={handleCancelClicked} disabled={false} />
|
||||
{isModal ? <QCancelButton onClickHandler={() => handleCancelClicked(true)} disabled={false} label="Close" />
|
||||
: !isWidget && <QCancelButton onClickHandler={() => handleCancelClicked(true)} disabled={false} />
|
||||
}
|
||||
</Grid>
|
||||
</Box>
|
||||
@ -1401,8 +1401,20 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
});
|
||||
};
|
||||
|
||||
const handleCancelClicked = () =>
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
const handleCancelClicked = (isClose: boolean) =>
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// unless this is a 'close', then tell backend we're cancelling //
|
||||
//////////////////////////////////////////////////////////////////
|
||||
if (!isClose)
|
||||
{
|
||||
Client.getInstance().processCancel(processName, processUUID);
|
||||
}
|
||||
|
||||
if (isModal && closeModalHandler)
|
||||
{
|
||||
closeModalHandler(null, "cancelClicked");
|
||||
@ -1415,6 +1427,7 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
navigate(path, {replace: true});
|
||||
};
|
||||
|
||||
|
||||
const mainCardStyles: any = {};
|
||||
const formStyles: any = {};
|
||||
mainCardStyles.minHeight = `calc(100vh - ${isModal ? 150 : 400}px)`;
|
||||
@ -1526,7 +1539,7 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
)}
|
||||
{
|
||||
noMoreSteps && <QCancelButton
|
||||
onClickHandler={handleCancelClicked}
|
||||
onClickHandler={() => handleCancelClicked(true)}
|
||||
label={isModal ? "Close" : "Return"}
|
||||
iconName={isModal ? "cancel" : "arrow_back"}
|
||||
disabled={isSubmitting} />
|
||||
@ -1537,7 +1550,7 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
<Grid container justifyContent="flex-end" spacing={3}>
|
||||
{
|
||||
!isWidget && (
|
||||
<QCancelButton onClickHandler={handleCancelClicked} disabled={isSubmitting} />
|
||||
<QCancelButton onClickHandler={() => handleCancelClicked(false)} disabled={isSubmitting} />
|
||||
)
|
||||
}
|
||||
<QSubmitButton label={nextButtonLabel} iconName={nextButtonIcon} disabled={isSubmitting} />
|
||||
@ -1552,13 +1565,13 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
</Box>
|
||||
</Card>
|
||||
</Form>
|
||||
)
|
||||
);
|
||||
}}
|
||||
</Formik>
|
||||
);
|
||||
|
||||
const body = (
|
||||
<Box py={3} mb={20}>
|
||||
<Box py={3} mb={20} className="processRun">
|
||||
<Grid container justifyContent="center" alignItems="center" sx={{height: "100%", mt: 8}}>
|
||||
<Grid item xs={12} lg={10} xl={8}>
|
||||
{form}
|
||||
|
@ -94,6 +94,7 @@ interface Props
|
||||
isModal?: boolean;
|
||||
initialQueryFilter?: QQueryFilter;
|
||||
initialColumns?: QQueryColumns;
|
||||
allowVariables?: boolean;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
@ -125,7 +126,7 @@ const getLoadingScreen = (isModal: boolean) =>
|
||||
**
|
||||
** Yuge component. The best. Lots of very smart people are saying so.
|
||||
*******************************************************************************/
|
||||
const RecordQuery = forwardRef(({table, usage, isModal, initialQueryFilter, initialColumns}: Props, ref) =>
|
||||
const RecordQuery = forwardRef(({table, usage, isModal, allowVariables, initialQueryFilter, initialColumns}: Props, ref) =>
|
||||
{
|
||||
const tableName = table.name;
|
||||
const [searchParams] = useSearchParams();
|
||||
@ -2884,6 +2885,7 @@ const RecordQuery = forwardRef(({table, usage, isModal, initialQueryFilter, init
|
||||
gridApiRef={gridApiRef}
|
||||
mode={mode}
|
||||
queryScreenUsage={usage}
|
||||
allowVariables={allowVariables}
|
||||
setMode={doSetMode}
|
||||
savedViewsComponent={savedViewsComponent}
|
||||
columnMenuComponent={buildColumnMenu()}
|
||||
@ -2912,6 +2914,7 @@ const RecordQuery = forwardRef(({table, usage, isModal, initialQueryFilter, init
|
||||
metaData: metaData,
|
||||
queryFilter: queryFilter,
|
||||
updateFilter: doSetQueryFilter,
|
||||
allowVariables: allowVariables
|
||||
}
|
||||
}}
|
||||
localeText={{
|
||||
|
@ -421,6 +421,14 @@ input[type="search"]::-webkit-search-results-decoration
|
||||
font-size: 2rem !important;
|
||||
}
|
||||
|
||||
.dashboard-table-actions-icon
|
||||
{
|
||||
font-size: 1.5rem !important;
|
||||
position: relative;
|
||||
top: -5px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.dashboard-schedule-icon
|
||||
{
|
||||
font-size: 1.1rem !important;
|
||||
@ -703,17 +711,13 @@ input[type="search"]::-webkit-search-results-decoration
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.recordView .widget .recordGridWidget
|
||||
{
|
||||
margin: -8px;
|
||||
}
|
||||
|
||||
.MuiPickersDay-root.Mui-selected, .MuiPickersDay-root.MuiPickersDay-dayWithMargin:hover
|
||||
{
|
||||
color: white;
|
||||
background-color: #0062FF !important;
|
||||
}
|
||||
|
||||
/* several styles below here for user-defined alert inside helpContent */
|
||||
.helpContentAlert
|
||||
{
|
||||
padding: 6px 16px;
|
||||
@ -776,3 +780,10 @@ input[type="search"]::-webkit-search-results-decoration
|
||||
{
|
||||
color: #F44335;
|
||||
}
|
||||
|
||||
/* the alert widget, was built with minimal (no?) margins, for embedding in
|
||||
a parent widget; but for using it on a process, give it some breathing room */
|
||||
.processRun .widget .MuiAlert-root
|
||||
{
|
||||
margin: 2rem 1rem;
|
||||
}
|
||||
|
@ -822,7 +822,7 @@
|
||||
"reportSetupWidget": {
|
||||
"name": "reportSetupWidget",
|
||||
"label": "Filters and Columns",
|
||||
"type": "reportSetup",
|
||||
"type": "filterAndColumnsSetup",
|
||||
"isCard": true,
|
||||
"storeDropdownSelections": false,
|
||||
"showReloadButton": true,
|
||||
|
Reference in New Issue
Block a user