mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-18 05:10:45 +00:00
Add sections and autocomplete (QDynamicSelect) on bulk-edit
This commit is contained in:
@ -155,6 +155,7 @@ function EntityForm({table, id}: Props): JSX.Element
|
|||||||
dynamicFormFields,
|
dynamicFormFields,
|
||||||
formValidations,
|
formValidations,
|
||||||
} = DynamicFormUtils.getFormData(fieldArray);
|
} = DynamicFormUtils.getFormData(fieldArray);
|
||||||
|
DynamicFormUtils.addPossibleValueProps(dynamicFormFields, fieldArray, tableName, record?.displayValues);
|
||||||
|
|
||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
// group the formFields by section //
|
// group the formFields by section //
|
||||||
@ -180,24 +181,6 @@ function EntityForm({table, id}: Props): JSX.Element
|
|||||||
{
|
{
|
||||||
sectionDynamicFormFields.push(dynamicFormFields[fieldName]);
|
sectionDynamicFormFields.push(dynamicFormFields[fieldName]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////
|
|
||||||
// add props for possible value fields //
|
|
||||||
/////////////////////////////////////////
|
|
||||||
if(field.possibleValueSourceName)
|
|
||||||
{
|
|
||||||
let initialDisplayValue = null;
|
|
||||||
if(record && record.displayValues)
|
|
||||||
{
|
|
||||||
initialDisplayValue = record.displayValues.get(field.name);
|
|
||||||
}
|
|
||||||
dynamicFormFields[fieldName].possibleValueProps =
|
|
||||||
{
|
|
||||||
isPossibleValue: true,
|
|
||||||
tableName: tableName,
|
|
||||||
initialDisplayValue: initialDisplayValue,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sectionDynamicFormFields.length === 0)
|
if (sectionDynamicFormFields.length === 0)
|
||||||
|
@ -136,6 +136,8 @@ function QDynamicForm(props: Props): JSX.Element
|
|||||||
fieldLabel={field.label}
|
fieldLabel={field.label}
|
||||||
initialValue={values[fieldName]}
|
initialValue={values[fieldName]}
|
||||||
initialDisplayValue={field.possibleValueProps.initialDisplayValue}
|
initialDisplayValue={field.possibleValueProps.initialDisplayValue}
|
||||||
|
bulkEditMode={bulkEditMode}
|
||||||
|
bulkEditSwitchChangeHandler={bulkEditSwitchChanged}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
|
@ -95,6 +95,33 @@ class DynamicFormUtils
|
|||||||
}
|
}
|
||||||
return (null);
|
return (null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static addPossibleValueProps(dynamicFormFields: any, qFields: QFieldMetaData[], tableName: string, displayValues: Map<string, string>)
|
||||||
|
{
|
||||||
|
for (let i = 0; i < qFields.length; i++)
|
||||||
|
{
|
||||||
|
const field = qFields[i];
|
||||||
|
|
||||||
|
/////////////////////////////////////////
|
||||||
|
// add props for possible value fields //
|
||||||
|
/////////////////////////////////////////
|
||||||
|
if (field.possibleValueSourceName && dynamicFormFields[field.name])
|
||||||
|
{
|
||||||
|
let initialDisplayValue = null;
|
||||||
|
if (displayValues)
|
||||||
|
{
|
||||||
|
initialDisplayValue = displayValues.get(field.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamicFormFields[field.name].possibleValueProps =
|
||||||
|
{
|
||||||
|
isPossibleValue: true,
|
||||||
|
tableName: tableName,
|
||||||
|
initialDisplayValue: initialDisplayValue,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DynamicFormUtils;
|
export default DynamicFormUtils;
|
||||||
|
@ -123,7 +123,7 @@ function QDynamicFormField({
|
|||||||
onClick={bulkEditSwitchChanged}
|
onClick={bulkEditSwitchChanged}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box width="100%">
|
<Box width="100%" sx={{background: (type == "checkbox" && isDisabled) ? "#f0f2f5!important" : "initial"}}>
|
||||||
{/* for checkboxes, if we put the whole thing in a label, we get bad overly aggressive toggling of the outer switch... */}
|
{/* for checkboxes, if we put the whole thing in a label, we get bad overly aggressive toggling of the outer switch... */}
|
||||||
{(type == "checkbox" ?
|
{(type == "checkbox" ?
|
||||||
field() :
|
field() :
|
||||||
|
@ -22,9 +22,12 @@
|
|||||||
import {QPossibleValue} from "@kingsrook/qqq-frontend-core/lib/model/QPossibleValue";
|
import {QPossibleValue} from "@kingsrook/qqq-frontend-core/lib/model/QPossibleValue";
|
||||||
import {CircularProgress, FilterOptionsState} from "@mui/material";
|
import {CircularProgress, FilterOptionsState} from "@mui/material";
|
||||||
import Autocomplete from "@mui/material/Autocomplete";
|
import Autocomplete from "@mui/material/Autocomplete";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import Switch from "@mui/material/Switch";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
import {useFormikContext} from "formik";
|
import {useFormikContext} from "formik";
|
||||||
import React, {useEffect, useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
import QClient from "qqq/utils/QClient";
|
import QClient from "qqq/utils/QClient";
|
||||||
|
|
||||||
interface Props
|
interface Props
|
||||||
@ -35,7 +38,10 @@ interface Props
|
|||||||
inForm: boolean;
|
inForm: boolean;
|
||||||
initialValue?: any;
|
initialValue?: any;
|
||||||
initialDisplayValue?: string;
|
initialDisplayValue?: string;
|
||||||
onChange?: any
|
onChange?: any;
|
||||||
|
isEditable?: boolean;
|
||||||
|
bulkEditMode?: boolean;
|
||||||
|
bulkEditSwitchChangeHandler?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDynamicSelect.defaultProps = {
|
QDynamicSelect.defaultProps = {
|
||||||
@ -43,11 +49,16 @@ QDynamicSelect.defaultProps = {
|
|||||||
initialValue: null,
|
initialValue: null,
|
||||||
initialDisplayValue: null,
|
initialDisplayValue: null,
|
||||||
onChange: null,
|
onChange: null,
|
||||||
|
isEditable: true,
|
||||||
|
bulkEditMode: false,
|
||||||
|
bulkEditSwitchChangeHandler: () =>
|
||||||
|
{
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const qController = QClient.getInstance();
|
const qController = QClient.getInstance();
|
||||||
|
|
||||||
function QDynamicSelect({tableName, fieldName, fieldLabel, inForm, initialValue, initialDisplayValue, onChange}: Props)
|
function QDynamicSelect({tableName, fieldName, fieldLabel, inForm, initialValue, initialDisplayValue, onChange, isEditable, bulkEditMode, bulkEditSwitchChangeHandler}: Props)
|
||||||
{
|
{
|
||||||
const [ open, setOpen ] = useState(false);
|
const [ open, setOpen ] = useState(false);
|
||||||
const [ options, setOptions ] = useState<readonly QPossibleValue[]>([]);
|
const [ options, setOptions ] = useState<readonly QPossibleValue[]>([]);
|
||||||
@ -56,6 +67,8 @@ function QDynamicSelect({tableName, fieldName, fieldLabel, inForm, initialValue,
|
|||||||
const [defaultValue, _] = useState(initialValue && initialDisplayValue ? {id: initialValue, label: initialDisplayValue} : null);
|
const [defaultValue, _] = useState(initialValue && initialDisplayValue ? {id: initialValue, label: initialDisplayValue} : null);
|
||||||
// const loading = open && options.length === 0;
|
// const loading = open && options.length === 0;
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [ switchChecked, setSwitchChecked ] = useState(false);
|
||||||
|
const [ isDisabled, setIsDisabled ] = useState(!isEditable || bulkEditMode);
|
||||||
|
|
||||||
let setFieldValueRef: (field: string, value: any, shouldValidate?: boolean) => void = null;
|
let setFieldValueRef: (field: string, value: any, shouldValidate?: boolean) => void = null;
|
||||||
if(inForm)
|
if(inForm)
|
||||||
@ -152,9 +165,18 @@ function QDynamicSelect({tableName, fieldName, fieldLabel, inForm, initialValue,
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
const bulkEditSwitchChanged = () =>
|
||||||
|
{
|
||||||
|
const newSwitchValue = !switchChecked;
|
||||||
|
setSwitchChecked(newSwitchValue);
|
||||||
|
setIsDisabled(!newSwitchValue);
|
||||||
|
bulkEditSwitchChangeHandler(fieldName, newSwitchValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
const autocomplete = (
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
id={fieldName}
|
id={fieldName}
|
||||||
|
sx={{background: isDisabled ? "#f0f2f5!important" : "initial"}}
|
||||||
open={open}
|
open={open}
|
||||||
fullWidth
|
fullWidth
|
||||||
onOpen={() =>
|
onOpen={() =>
|
||||||
@ -190,6 +212,7 @@ function QDynamicSelect({tableName, fieldName, fieldLabel, inForm, initialValue,
|
|||||||
}}
|
}}
|
||||||
renderOption={renderOption}
|
renderOption={renderOption}
|
||||||
filterOptions={filterOptions}
|
filterOptions={filterOptions}
|
||||||
|
disabled={isDisabled}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextField
|
||||||
{...params}
|
{...params}
|
||||||
@ -210,6 +233,35 @@ function QDynamicSelect({tableName, fieldName, fieldLabel, inForm, initialValue,
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
if (bulkEditMode)
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<Box mb={1.5} display="flex" flexDirection="row">
|
||||||
|
<Box alignItems="baseline" pt={1}>
|
||||||
|
<Switch
|
||||||
|
id={`bulkEditSwitch-${fieldName}`}
|
||||||
|
checked={switchChecked}
|
||||||
|
onClick={bulkEditSwitchChanged}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box width="100%">
|
||||||
|
{autocomplete}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<MDBox mb={1.5}>
|
||||||
|
{autocomplete}
|
||||||
|
</MDBox>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default QDynamicSelect;
|
export default QDynamicSelect;
|
||||||
|
@ -36,8 +36,14 @@ interface Props
|
|||||||
metaData?: QTableMetaData;
|
metaData?: QTableMetaData;
|
||||||
widgetMetaDataList?: QWidgetMetaData[];
|
widgetMetaDataList?: QWidgetMetaData[];
|
||||||
light?: boolean;
|
light?: boolean;
|
||||||
|
stickyTop?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRecordSidebar.defaultProps = {
|
||||||
|
light: false,
|
||||||
|
stickyTop: "100px",
|
||||||
|
};
|
||||||
|
|
||||||
interface SidebarEntry
|
interface SidebarEntry
|
||||||
{
|
{
|
||||||
iconName: string;
|
iconName: string;
|
||||||
@ -45,7 +51,7 @@ interface SidebarEntry
|
|||||||
label: string;
|
label: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function QRecordSidebar({tableSections, widgetMetaDataList, light}: Props): JSX.Element
|
function QRecordSidebar({tableSections, widgetMetaDataList, light, stickyTop}: Props): JSX.Element
|
||||||
{
|
{
|
||||||
/////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////
|
||||||
// insert widgets after identity (first) table section //
|
// insert widgets after identity (first) table section //
|
||||||
@ -65,7 +71,7 @@ function QRecordSidebar({tableSections, widgetMetaDataList, light}: Props): JSX.
|
|||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card sx={{borderRadius: ({borders: {borderRadius}}) => borderRadius.lg, position: "sticky", top: "100px"}}>
|
<Card sx={{borderRadius: ({borders: {borderRadius}}) => borderRadius.lg, position: "sticky", top: stickyTop}}>
|
||||||
<MDBox component="ul" display="flex" flexDirection="column" p={2} m={0} sx={{listStyle: "none"}}>
|
<MDBox component="ul" display="flex" flexDirection="column" p={2} m={0} sx={{listStyle: "none"}}>
|
||||||
{
|
{
|
||||||
sidebarEntries ? sidebarEntries.map((entry: SidebarEntry, key: number) => (
|
sidebarEntries ? sidebarEntries.map((entry: SidebarEntry, key: number) => (
|
||||||
@ -109,8 +115,4 @@ function QRecordSidebar({tableSections, widgetMetaDataList, light}: Props): JSX.
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
QRecordSidebar.defaultProps = {
|
|
||||||
light: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default QRecordSidebar;
|
export default QRecordSidebar;
|
||||||
|
@ -40,7 +40,6 @@ import Modal from "@mui/material/Modal";
|
|||||||
import {
|
import {
|
||||||
DataGridPro,
|
DataGridPro,
|
||||||
getGridDateOperators,
|
getGridDateOperators,
|
||||||
getGridNumericOperators,
|
|
||||||
GridCallbackDetails,
|
GridCallbackDetails,
|
||||||
GridColDef,
|
GridColDef,
|
||||||
GridColumnOrderChangeParams,
|
GridColumnOrderChangeParams,
|
||||||
@ -116,12 +115,12 @@ async function getDefaultFilter(tableMetaData: QTableMetaData, searchParams: URL
|
|||||||
const defaultFilter = {items: []} as GridFilterModel;
|
const defaultFilter = {items: []} as GridFilterModel;
|
||||||
let id = 1;
|
let id = 1;
|
||||||
|
|
||||||
for(let i = 0; i < qQueryFilter.criteria.length; i++)
|
for (let i = 0; i < qQueryFilter.criteria.length; i++)
|
||||||
{
|
{
|
||||||
const criteria = qQueryFilter.criteria[i];
|
const criteria = qQueryFilter.criteria[i];
|
||||||
const field = tableMetaData.fields.get(criteria.fieldName);
|
const field = tableMetaData.fields.get(criteria.fieldName);
|
||||||
let values = criteria.values;
|
let values = criteria.values;
|
||||||
if(field.possibleValueSourceName)
|
if (field.possibleValueSourceName)
|
||||||
{
|
{
|
||||||
//////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////
|
||||||
// possible-values in query-string are expected to only be their id values. //
|
// possible-values in query-string are expected to only be their id values. //
|
||||||
@ -129,7 +128,7 @@ async function getDefaultFilter(tableMetaData: QTableMetaData, searchParams: URL
|
|||||||
// but we need them to be possibleValue objects (w/ id & label) so the label //
|
// but we need them to be possibleValue objects (w/ id & label) so the label //
|
||||||
// can be shown in the filter dropdown. So, make backend call to look them up. //
|
// can be shown in the filter dropdown. So, make backend call to look them up. //
|
||||||
//////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////
|
||||||
if(values && values.length > 0)
|
if (values && values.length > 0)
|
||||||
{
|
{
|
||||||
values = await qController.possibleValues(tableMetaData.name, field.name, "", values);
|
values = await qController.possibleValues(tableMetaData.name, field.name, "", values);
|
||||||
}
|
}
|
||||||
@ -144,7 +143,7 @@ async function getDefaultFilter(tableMetaData: QTableMetaData, searchParams: URL
|
|||||||
}
|
}
|
||||||
|
|
||||||
defaultFilter.linkOperator = GridLinkOperator.And;
|
defaultFilter.linkOperator = GridLinkOperator.And;
|
||||||
if(qQueryFilter.booleanOperator === "OR")
|
if (qQueryFilter.booleanOperator === "OR")
|
||||||
{
|
{
|
||||||
defaultFilter.linkOperator = GridLinkOperator.Or;
|
defaultFilter.linkOperator = GridLinkOperator.Or;
|
||||||
}
|
}
|
||||||
@ -171,7 +170,7 @@ async function getDefaultFilter(tableMetaData: QTableMetaData, searchParams: URL
|
|||||||
function EntityList({table, launchProcess}: Props): JSX.Element
|
function EntityList({table, launchProcess}: Props): JSX.Element
|
||||||
{
|
{
|
||||||
const tableName = table.name;
|
const tableName = table.name;
|
||||||
const [searchParams] = useSearchParams();
|
const [ searchParams ] = useSearchParams();
|
||||||
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -202,10 +201,10 @@ function EntityList({table, launchProcess}: Props): JSX.Element
|
|||||||
defaultRowsPerPage = JSON.parse(localStorage.getItem(rowsPerPageLocalStorageKey));
|
defaultRowsPerPage = JSON.parse(localStorage.getItem(rowsPerPageLocalStorageKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
const [filterModel, setFilterModel] = useState({items: []} as GridFilterModel);
|
const [ filterModel, setFilterModel ] = useState({items: []} as GridFilterModel);
|
||||||
const [columnSortModel, setColumnSortModel] = useState(defaultSort);
|
const [ columnSortModel, setColumnSortModel ] = useState(defaultSort);
|
||||||
const [columnVisibilityModel, setColumnVisibilityModel] = useState(defaultVisibility);
|
const [ columnVisibilityModel, setColumnVisibilityModel ] = useState(defaultVisibility);
|
||||||
const [rowsPerPage, setRowsPerPage] = useState(defaultRowsPerPage);
|
const [ rowsPerPage, setRowsPerPage ] = useState(defaultRowsPerPage);
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// for some reason, if we set the filterModel to what is in local storage, an onChange event //
|
// for some reason, if we set the filterModel to what is in local storage, an onChange event //
|
||||||
@ -213,47 +212,47 @@ function EntityList({table, launchProcess}: Props): JSX.Element
|
|||||||
// when that happens put the default back - it needs to be in state //
|
// when that happens put the default back - it needs to be in state //
|
||||||
// const [defaultFilter1] = useState(defaultFilter); //
|
// const [defaultFilter1] = useState(defaultFilter); //
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
const [defaultFilter] = useState({items: []} as GridFilterModel);
|
const [ defaultFilter ] = useState({items: []} as GridFilterModel);
|
||||||
|
|
||||||
const [tableState, setTableState] = useState("");
|
const [ tableState, setTableState ] = useState("");
|
||||||
const [tableMetaData, setTableMetaData] = useState(null as QTableMetaData);
|
const [ tableMetaData, setTableMetaData ] = useState(null as QTableMetaData);
|
||||||
const [defaultFilterLoaded, setDefaultFilterLoaded] = useState(false);
|
const [ defaultFilterLoaded, setDefaultFilterLoaded ] = useState(false);
|
||||||
const [, setFiltersMenu] = useState(null);
|
const [ , setFiltersMenu ] = useState(null);
|
||||||
const [actionsMenu, setActionsMenu] = useState(null);
|
const [ actionsMenu, setActionsMenu ] = useState(null);
|
||||||
const [tableProcesses, setTableProcesses] = useState([] as QProcessMetaData[]);
|
const [ tableProcesses, setTableProcesses ] = useState([] as QProcessMetaData[]);
|
||||||
const [allTableProcesses, setAllTableProcesses] = useState([] as QProcessMetaData[]);
|
const [ allTableProcesses, setAllTableProcesses ] = useState([] as QProcessMetaData[]);
|
||||||
const [pageNumber, setPageNumber] = useState(0);
|
const [ pageNumber, setPageNumber ] = useState(0);
|
||||||
const [totalRecords, setTotalRecords] = useState(0);
|
const [ totalRecords, setTotalRecords ] = useState(0);
|
||||||
const [selectedIds, setSelectedIds] = useState([] as string[]);
|
const [ selectedIds, setSelectedIds ] = useState([] as string[]);
|
||||||
const [selectFullFilterState, setSelectFullFilterState] = useState("n/a" as "n/a" | "checked" | "filter");
|
const [ selectFullFilterState, setSelectFullFilterState ] = useState("n/a" as "n/a" | "checked" | "filter");
|
||||||
const [columns, setColumns] = useState([] as GridColDef[]);
|
const [ columns, setColumns ] = useState([] as GridColDef[]);
|
||||||
const [rows, setRows] = useState([] as GridRowsProp[]);
|
const [ rows, setRows ] = useState([] as GridRowsProp[]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [ loading, setLoading ] = useState(true);
|
||||||
const [alertContent, setAlertContent] = useState("");
|
const [ alertContent, setAlertContent ] = useState("");
|
||||||
const [tableLabel, setTableLabel] = useState("");
|
const [ tableLabel, setTableLabel ] = useState("");
|
||||||
const [gridMouseDownX, setGridMouseDownX] = useState(0);
|
const [ gridMouseDownX, setGridMouseDownX ] = useState(0);
|
||||||
const [gridMouseDownY, setGridMouseDownY] = useState(0);
|
const [ gridMouseDownY, setGridMouseDownY ] = useState(0);
|
||||||
const [pinnedColumns, setPinnedColumns] = useState({left: ["__check__", "id"]});
|
const [ pinnedColumns, setPinnedColumns ] = useState({left: [ "__check__", "id" ]});
|
||||||
|
|
||||||
const [activeModalProcess, setActiveModalProcess] = useState(null as QProcessMetaData)
|
const [ activeModalProcess, setActiveModalProcess ] = useState(null as QProcessMetaData);
|
||||||
const [launchingProcess, setLaunchingProcess] = useState(launchProcess);
|
const [ launchingProcess, setLaunchingProcess ] = useState(launchProcess);
|
||||||
|
|
||||||
const instance = useRef({timer: null});
|
const instance = useRef({timer: null});
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// use all these states to avoid showing results from an "old" query, that finishes loading after a newer one //
|
// use all these states to avoid showing results from an "old" query, that finishes loading after a newer one //
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
const [latestQueryId, setLatestQueryId] = useState(0);
|
const [ latestQueryId, setLatestQueryId ] = useState(0);
|
||||||
const [countResults, setCountResults] = useState({} as any);
|
const [ countResults, setCountResults ] = useState({} as any);
|
||||||
const [receivedCountTimestamp, setReceivedCountTimestamp] = useState(new Date());
|
const [ receivedCountTimestamp, setReceivedCountTimestamp ] = useState(new Date());
|
||||||
const [queryResults, setQueryResults] = useState({} as any);
|
const [ queryResults, setQueryResults ] = useState({} as any);
|
||||||
const [receivedQueryTimestamp, setReceivedQueryTimestamp] = useState(new Date());
|
const [ receivedQueryTimestamp, setReceivedQueryTimestamp ] = useState(new Date());
|
||||||
const [queryErrors, setQueryErrors] = useState({} as any);
|
const [ queryErrors, setQueryErrors ] = useState({} as any);
|
||||||
const [receivedQueryErrorTimestamp, setReceivedQueryErrorTimestamp] = useState(new Date());
|
const [ receivedQueryErrorTimestamp, setReceivedQueryErrorTimestamp ] = useState(new Date());
|
||||||
|
|
||||||
const {pageHeader, setPageHeader} = useContext(QContext);
|
const {pageHeader, setPageHeader} = useContext(QContext);
|
||||||
|
|
||||||
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
const [ , forceUpdate ] = useReducer((x) => x + 1, 0);
|
||||||
|
|
||||||
const openActionsMenu = (event: any) => setActionsMenu(event.currentTarget);
|
const openActionsMenu = (event: any) => setActionsMenu(event.currentTarget);
|
||||||
const closeActionsMenu = () => setActionsMenu(null);
|
const closeActionsMenu = () => setActionsMenu(null);
|
||||||
@ -269,11 +268,11 @@ function EntityList({table, launchProcess}: Props): JSX.Element
|
|||||||
// the path for a process looks like: .../table/process //
|
// the path for a process looks like: .../table/process //
|
||||||
// so if our tableName is in the -2 index, try to open process //
|
// so if our tableName is in the -2 index, try to open process //
|
||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
if(pathParts[pathParts.length - 2] === tableName)
|
if (pathParts[pathParts.length - 2] === tableName)
|
||||||
{
|
{
|
||||||
const processName = pathParts[pathParts.length - 1];
|
const processName = pathParts[pathParts.length - 1];
|
||||||
const processList = allTableProcesses.filter(p => p.name.endsWith(processName));
|
const processList = allTableProcesses.filter(p => p.name.endsWith(processName));
|
||||||
if(processList.length > 0)
|
if (processList.length > 0)
|
||||||
{
|
{
|
||||||
setActiveModalProcess(processList[0]);
|
setActiveModalProcess(processList[0]);
|
||||||
return;
|
return;
|
||||||
@ -284,7 +283,7 @@ function EntityList({table, launchProcess}: Props): JSX.Element
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(e)
|
catch (e)
|
||||||
{
|
{
|
||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
}
|
||||||
@ -294,7 +293,7 @@ function EntityList({table, launchProcess}: Props): JSX.Element
|
|||||||
////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
setActiveModalProcess(null);
|
setActiveModalProcess(null);
|
||||||
}, [location]);
|
}, [ location ]);
|
||||||
|
|
||||||
const buildQFilter = (filterModel: GridFilterModel) =>
|
const buildQFilter = (filterModel: GridFilterModel) =>
|
||||||
{
|
{
|
||||||
@ -320,7 +319,7 @@ function EntityList({table, launchProcess}: Props): JSX.Element
|
|||||||
});
|
});
|
||||||
|
|
||||||
qFilter.booleanOperator = "AND";
|
qFilter.booleanOperator = "AND";
|
||||||
if(filterModel.linkOperator == "or")
|
if (filterModel.linkOperator == "or")
|
||||||
{
|
{
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// by default qFilter uses AND - so only if we see linkOperator=or do we need to set it //
|
// by default qFilter uses AND - so only if we see linkOperator=or do we need to set it //
|
||||||
@ -350,7 +349,7 @@ function EntityList({table, launchProcess}: Props): JSX.Element
|
|||||||
if (!defaultFilterLoaded)
|
if (!defaultFilterLoaded)
|
||||||
{
|
{
|
||||||
setDefaultFilterLoaded(true);
|
setDefaultFilterLoaded(true);
|
||||||
localFilterModel = await getDefaultFilter(tableMetaData, searchParams, filterLocalStorageKey)
|
localFilterModel = await getDefaultFilter(tableMetaData, searchParams, filterLocalStorageKey);
|
||||||
setFilterModel(localFilterModel);
|
setFilterModel(localFilterModel);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -364,7 +363,7 @@ function EntityList({table, launchProcess}: Props): JSX.Element
|
|||||||
});
|
});
|
||||||
setColumnSortModel(columnSortModel);
|
setColumnSortModel(columnSortModel);
|
||||||
}
|
}
|
||||||
setPinnedColumns({left: ["__check__", tableMetaData.primaryKeyField]});
|
setPinnedColumns({left: [ "__check__", tableMetaData.primaryKeyField ]});
|
||||||
|
|
||||||
const qFilter = buildQFilter(localFilterModel);
|
const qFilter = buildQFilter(localFilterModel);
|
||||||
|
|
||||||
@ -433,7 +432,7 @@ function EntityList({table, launchProcess}: Props): JSX.Element
|
|||||||
}
|
}
|
||||||
setTotalRecords(countResults[latestQueryId]);
|
setTotalRecords(countResults[latestQueryId]);
|
||||||
delete countResults[latestQueryId];
|
delete countResults[latestQueryId];
|
||||||
}, [receivedCountTimestamp]);
|
}, [ receivedCountTimestamp ]);
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////
|
///////////////////////////
|
||||||
@ -455,7 +454,7 @@ function EntityList({table, launchProcess}: Props): JSX.Element
|
|||||||
const results = queryResults[latestQueryId];
|
const results = queryResults[latestQueryId];
|
||||||
delete queryResults[latestQueryId];
|
delete queryResults[latestQueryId];
|
||||||
|
|
||||||
const fields = [...tableMetaData.fields.values()];
|
const fields = [ ...tableMetaData.fields.values() ];
|
||||||
const rows = [] as any[];
|
const rows = [] as any[];
|
||||||
const columnsToRender = {} as any;
|
const columnsToRender = {} as any;
|
||||||
results.forEach((record: QRecord) =>
|
results.forEach((record: QRecord) =>
|
||||||
@ -539,12 +538,12 @@ function EntityList({table, launchProcess}: Props): JSX.Element
|
|||||||
const sizeAdornment = field.getAdornment(AdornmentType.SIZE);
|
const sizeAdornment = field.getAdornment(AdornmentType.SIZE);
|
||||||
const width: string = sizeAdornment.getValue("width");
|
const width: string = sizeAdornment.getValue("width");
|
||||||
const widths: Map<string, number> = new Map<string, number>([
|
const widths: Map<string, number> = new Map<string, number>([
|
||||||
["small", 100],
|
[ "small", 100 ],
|
||||||
["medium", 200],
|
[ "medium", 200 ],
|
||||||
["large", 400],
|
[ "large", 400 ],
|
||||||
["xlarge", 600]
|
[ "xlarge", 600 ]
|
||||||
]);
|
]);
|
||||||
if(widths.has(width))
|
if (widths.has(width))
|
||||||
{
|
{
|
||||||
columnWidth = widths.get(width);
|
columnWidth = widths.get(width);
|
||||||
}
|
}
|
||||||
@ -588,7 +587,7 @@ function EntityList({table, launchProcess}: Props): JSX.Element
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
setAlertContent(null);
|
setAlertContent(null);
|
||||||
forceUpdate();
|
forceUpdate();
|
||||||
}, [receivedQueryTimestamp]);
|
}, [ receivedQueryTimestamp ]);
|
||||||
|
|
||||||
/////////////////////////
|
/////////////////////////
|
||||||
// display query error //
|
// display query error //
|
||||||
@ -610,7 +609,7 @@ function EntityList({table, launchProcess}: Props): JSX.Element
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
setAlertContent(errorMessage);
|
setAlertContent(errorMessage);
|
||||||
|
|
||||||
}, [receivedQueryErrorTimestamp]);
|
}, [ receivedQueryErrorTimestamp ]);
|
||||||
|
|
||||||
|
|
||||||
const handlePageChange = (page: number) =>
|
const handlePageChange = (page: number) =>
|
||||||
@ -737,7 +736,7 @@ function EntityList({table, launchProcess}: Props): JSX.Element
|
|||||||
setTableProcesses(QProcessUtils.getProcessesForTable(metaData, tableName)); // these are the ones to show in the dropdown
|
setTableProcesses(QProcessUtils.getProcessesForTable(metaData, tableName)); // these are the ones to show in the dropdown
|
||||||
setAllTableProcesses(QProcessUtils.getProcessesForTable(metaData, tableName, true)); // these include hidden ones (e.g., to find the bulks)
|
setAllTableProcesses(QProcessUtils.getProcessesForTable(metaData, tableName, true)); // these include hidden ones (e.g., to find the bulks)
|
||||||
|
|
||||||
if(launchingProcess)
|
if (launchingProcess)
|
||||||
{
|
{
|
||||||
setLaunchingProcess(null);
|
setLaunchingProcess(null);
|
||||||
setActiveModalProcess(launchingProcess);
|
setActiveModalProcess(launchingProcess);
|
||||||
@ -850,7 +849,7 @@ function EntityList({table, launchProcess}: Props): JSX.Element
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRecordIdsForProcess() : string | QQueryFilter
|
function getRecordIdsForProcess(): string | QQueryFilter
|
||||||
{
|
{
|
||||||
if (selectFullFilterState === "filter")
|
if (selectFullFilterState === "filter")
|
||||||
{
|
{
|
||||||
@ -873,7 +872,7 @@ function EntityList({table, launchProcess}: Props): JSX.Element
|
|||||||
|
|
||||||
const closeModalProcess = (event: object, reason: string) =>
|
const closeModalProcess = (event: object, reason: string) =>
|
||||||
{
|
{
|
||||||
if(reason === "backdropClick")
|
if (reason === "backdropClick")
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -886,12 +885,12 @@ function EntityList({table, launchProcess}: Props): JSX.Element
|
|||||||
navigate(newPath.join("/"));
|
navigate(newPath.join("/"));
|
||||||
|
|
||||||
updateTable();
|
updateTable();
|
||||||
}
|
};
|
||||||
|
|
||||||
const openBulkProcess = (processNamePart: "Insert" | "Edit" | "Delete", processLabelPart: "Load" | "Edit" | "Delete") =>
|
const openBulkProcess = (processNamePart: "Insert" | "Edit" | "Delete", processLabelPart: "Load" | "Edit" | "Delete") =>
|
||||||
{
|
{
|
||||||
const processList = allTableProcesses.filter(p => p.name.endsWith(`.bulk${processNamePart}`));
|
const processList = allTableProcesses.filter(p => p.name.endsWith(`.bulk${processNamePart}`));
|
||||||
if(processList.length > 0)
|
if (processList.length > 0)
|
||||||
{
|
{
|
||||||
openModalProcess(processList[0]);
|
openModalProcess(processList[0]);
|
||||||
}
|
}
|
||||||
@ -899,7 +898,7 @@ function EntityList({table, launchProcess}: Props): JSX.Element
|
|||||||
{
|
{
|
||||||
setAlertContent(`Could not find Bulk ${processLabelPart} process for this table.`);
|
setAlertContent(`Could not find Bulk ${processLabelPart} process for this table.`);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const bulkLoadClicked = () =>
|
const bulkLoadClicked = () =>
|
||||||
{
|
{
|
||||||
@ -960,7 +959,7 @@ function EntityList({table, launchProcess}: Props): JSX.Element
|
|||||||
component="div"
|
component="div"
|
||||||
count={totalRecords === null ? 0 : totalRecords}
|
count={totalRecords === null ? 0 : totalRecords}
|
||||||
page={pageNumber}
|
page={pageNumber}
|
||||||
rowsPerPageOptions={[10, 25, 50, 100, 250]}
|
rowsPerPageOptions={[ 10, 25, 50, 100, 250 ]}
|
||||||
rowsPerPage={rowsPerPage}
|
rowsPerPage={rowsPerPage}
|
||||||
onPageChange={(event, value) => handlePageChange(value)}
|
onPageChange={(event, value) => handlePageChange(value)}
|
||||||
onRowsPerPageChange={(event) => handleRowsPerPageChange(Number(event.target.value))}
|
onRowsPerPageChange={(event) => handleRowsPerPageChange(Number(event.target.value))}
|
||||||
@ -1077,7 +1076,7 @@ function EntityList({table, launchProcess}: Props): JSX.Element
|
|||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
updateTable();
|
updateTable();
|
||||||
}, [pageNumber, rowsPerPage, columnSortModel]);
|
}, [ pageNumber, rowsPerPage, columnSortModel ]);
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// for state changes that DO change the filter, call to update the table - and DO clear out the totalRecords //
|
// for state changes that DO change the filter, call to update the table - and DO clear out the totalRecords //
|
||||||
@ -1086,13 +1085,13 @@ function EntityList({table, launchProcess}: Props): JSX.Element
|
|||||||
{
|
{
|
||||||
setTotalRecords(null);
|
setTotalRecords(null);
|
||||||
updateTable();
|
updateTable();
|
||||||
}, [tableState, filterModel]);
|
}, [ tableState, filterModel ]);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
document.documentElement.scrollTop = 0;
|
document.documentElement.scrollTop = 0;
|
||||||
document.scrollingElement.scrollTop = 0;
|
document.scrollingElement.scrollTop = 0;
|
||||||
}, [pageNumber, rowsPerPage]);
|
}, [ pageNumber, rowsPerPage ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardLayout>
|
<DashboardLayout>
|
||||||
@ -1160,7 +1159,7 @@ function EntityList({table, launchProcess}: Props): JSX.Element
|
|||||||
onColumnOrderChange={handleColumnOrderChange}
|
onColumnOrderChange={handleColumnOrderChange}
|
||||||
onSelectionModelChange={selectionChanged}
|
onSelectionModelChange={selectionChanged}
|
||||||
onSortModelChange={handleSortChange}
|
onSortModelChange={handleSortChange}
|
||||||
sortingOrder={["asc", "desc"]}
|
sortingOrder={[ "asc", "desc" ]}
|
||||||
sortModel={columnSortModel}
|
sortModel={columnSortModel}
|
||||||
getRowClassName={(params) => (params.indexRelativeToCurrentPage % 2 === 0 ? "even" : "odd")}
|
getRowClassName={(params) => (params.indexRelativeToCurrentPage % 2 === 0 ? "even" : "odd")}
|
||||||
/>
|
/>
|
||||||
@ -1171,7 +1170,9 @@ function EntityList({table, launchProcess}: Props): JSX.Element
|
|||||||
{
|
{
|
||||||
activeModalProcess &&
|
activeModalProcess &&
|
||||||
<Modal open={activeModalProcess !== null} onClose={(event, reason) => closeModalProcess(event, reason)}>
|
<Modal open={activeModalProcess !== null} onClose={(event, reason) => closeModalProcess(event, reason)}>
|
||||||
|
<div className="modalProcess">
|
||||||
<ProcessRun process={activeModalProcess} isModal={true} recordIds={getRecordIdsForProcess()} closeModalHandler={closeModalProcess} />
|
<ProcessRun process={activeModalProcess} isModal={true} recordIds={getRecordIdsForProcess()} closeModalHandler={closeModalProcess} />
|
||||||
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -451,7 +451,9 @@ function ViewContents({id, table, launchProcess}: Props): JSX.Element
|
|||||||
{
|
{
|
||||||
activeModalProcess &&
|
activeModalProcess &&
|
||||||
<Modal open={activeModalProcess !== null} onClose={(event, reason) => closeModalProcess(event, reason)}>
|
<Modal open={activeModalProcess !== null} onClose={(event, reason) => closeModalProcess(event, reason)}>
|
||||||
|
<div className="modalProcess">
|
||||||
<ProcessRun process={activeModalProcess} isModal={true} recordIds={id} closeModalHandler={closeModalProcess} />
|
<ProcessRun process={activeModalProcess} isModal={true} recordIds={id} closeModalHandler={closeModalProcess} />
|
||||||
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ import {QFrontendComponent} from "@kingsrook/qqq-frontend-core/lib/model/metaDat
|
|||||||
import {QFrontendStepMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFrontendStepMetaData";
|
import {QFrontendStepMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFrontendStepMetaData";
|
||||||
import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance";
|
import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance";
|
||||||
import {QProcessMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QProcessMetaData";
|
import {QProcessMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QProcessMetaData";
|
||||||
|
import {QTableSection} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableSection";
|
||||||
import {QJobComplete} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobComplete";
|
import {QJobComplete} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobComplete";
|
||||||
import {QJobError} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobError";
|
import {QJobError} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobError";
|
||||||
import {QJobRunning} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobRunning";
|
import {QJobRunning} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobRunning";
|
||||||
@ -52,6 +53,7 @@ import BaseLayout from "qqq/components/BaseLayout";
|
|||||||
import {QCancelButton, QSubmitButton} from "qqq/components/QButtons";
|
import {QCancelButton, QSubmitButton} from "qqq/components/QButtons";
|
||||||
import QDynamicForm from "qqq/components/QDynamicForm";
|
import QDynamicForm from "qqq/components/QDynamicForm";
|
||||||
import DynamicFormUtils from "qqq/components/QDynamicForm/utils/DynamicFormUtils";
|
import DynamicFormUtils from "qqq/components/QDynamicForm/utils/DynamicFormUtils";
|
||||||
|
import QRecordSidebar from "qqq/components/QRecordSidebar";
|
||||||
import MDBox from "qqq/components/Temporary/MDBox";
|
import MDBox from "qqq/components/Temporary/MDBox";
|
||||||
import MDButton from "qqq/components/Temporary/MDButton";
|
import MDButton from "qqq/components/Temporary/MDButton";
|
||||||
import MDProgress from "qqq/components/Temporary/MDProgress";
|
import MDProgress from "qqq/components/Temporary/MDProgress";
|
||||||
@ -59,6 +61,7 @@ import MDTypography from "qqq/components/Temporary/MDTypography";
|
|||||||
import {QGoogleDriveFolderPickerWrapper} from "qqq/pages/process-run/components/QGoogleDriveFolderPickerWrapper";
|
import {QGoogleDriveFolderPickerWrapper} from "qqq/pages/process-run/components/QGoogleDriveFolderPickerWrapper";
|
||||||
import QValidationReview from "qqq/pages/process-run/components/QValidationReview";
|
import QValidationReview from "qqq/pages/process-run/components/QValidationReview";
|
||||||
import QClient from "qqq/utils/QClient";
|
import QClient from "qqq/utils/QClient";
|
||||||
|
import QTableUtils from "qqq/utils/QTableUtils";
|
||||||
import QValueUtils from "qqq/utils/QValueUtils";
|
import QValueUtils from "qqq/utils/QValueUtils";
|
||||||
import QProcessSummaryResults from "./components/QProcessSummaryResults";
|
import QProcessSummaryResults from "./components/QProcessSummaryResults";
|
||||||
|
|
||||||
@ -66,9 +69,9 @@ interface Props
|
|||||||
{
|
{
|
||||||
process?: QProcessMetaData;
|
process?: QProcessMetaData;
|
||||||
defaultProcessValues?: any;
|
defaultProcessValues?: any;
|
||||||
isModal?: boolean
|
isModal?: boolean;
|
||||||
recordIds?: string | QQueryFilter
|
recordIds?: string | QQueryFilter;
|
||||||
closeModalHandler?: (event: object, reason: string) => void
|
closeModalHandler?: (event: object, reason: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const INITIAL_RETRY_MILLIS = 1_500;
|
const INITIAL_RETRY_MILLIS = 1_500;
|
||||||
@ -95,6 +98,7 @@ function ProcessRun({process, defaultProcessValues, isModal, recordIds, closeMod
|
|||||||
const [needInitialLoad, setNeedInitialLoad] = useState(true);
|
const [needInitialLoad, setNeedInitialLoad] = useState(true);
|
||||||
const [processMetaData, setProcessMetaData] = useState(null);
|
const [processMetaData, setProcessMetaData] = useState(null);
|
||||||
const [tableMetaData, setTableMetaData] = useState(null);
|
const [tableMetaData, setTableMetaData] = useState(null);
|
||||||
|
const [tableSections, setTableSections] = useState(null as QTableSection[]);
|
||||||
const [qInstance, setQInstance] = useState(null as QInstance);
|
const [qInstance, setQInstance] = useState(null as QInstance);
|
||||||
const [processValues, setProcessValues] = useState({} as any);
|
const [processValues, setProcessValues] = useState({} as any);
|
||||||
const [processError, _setProcessError] = useState(null as string);
|
const [processError, _setProcessError] = useState(null as string);
|
||||||
@ -303,6 +307,17 @@ function ProcessRun({process, defaultProcessValues, isModal, recordIds, closeMod
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const {formFields, values, errors, touched} = formData;
|
||||||
|
let localTableSections = tableSections;
|
||||||
|
if(localTableSections == null)
|
||||||
|
{
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// if the table sections (ones that actually have fields to edit) haven't been built yet, do so now //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
localTableSections = tableMetaData ? QTableUtils.getSectionsForRecordSidebar(tableMetaData, Object.keys(formFields)) : null;
|
||||||
|
setTableSections(localTableSections);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<MDTypography variation="h5" component="div" fontWeight="bold">
|
<MDTypography variation="h5" component="div" fontWeight="bold">
|
||||||
@ -337,7 +352,61 @@ function ProcessRun({process, defaultProcessValues, isModal, recordIds, closeMod
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
component.type === QComponentType.BULK_EDIT_FORM && (
|
component.type === QComponentType.BULK_EDIT_FORM && (
|
||||||
<QDynamicForm formData={formData} bulkEditMode bulkEditSwitchChangeHandler={bulkEditSwitchChanged} />
|
tableMetaData && localTableSections ?
|
||||||
|
<Grid container spacing={3} mt={2}>
|
||||||
|
<Grid item xs={12} lg={3}>
|
||||||
|
<QRecordSidebar tableSections={localTableSections} stickyTop="20px" />
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} lg={9}>
|
||||||
|
|
||||||
|
{localTableSections.map((section: QTableSection, index: number) =>
|
||||||
|
{
|
||||||
|
const name = section.name
|
||||||
|
console.log(formData);
|
||||||
|
console.log(section.fieldNames);
|
||||||
|
|
||||||
|
const sectionFormFields = {};
|
||||||
|
for(let i = 0; i<section.fieldNames.length; i++)
|
||||||
|
{
|
||||||
|
const fieldName = section.fieldNames[i];
|
||||||
|
if(formFields[fieldName])
|
||||||
|
{
|
||||||
|
// @ts-ignore
|
||||||
|
sectionFormFields[fieldName] = formFields[fieldName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Object.keys(sectionFormFields).length > 0)
|
||||||
|
{
|
||||||
|
const sectionFormData = {
|
||||||
|
formFields: sectionFormFields,
|
||||||
|
values: values,
|
||||||
|
errors: errors,
|
||||||
|
touched: touched
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box key={name} pb={3}>
|
||||||
|
<Card id={name} sx={{scrollMarginTop: "20px"}} elevation={5}>
|
||||||
|
<MDTypography variant="h5" p={3} pb={1}>
|
||||||
|
{section.label}
|
||||||
|
</MDTypography>
|
||||||
|
<Box px={2}>
|
||||||
|
<QDynamicForm formData={sectionFormData} bulkEditMode bulkEditSwitchChangeHandler={bulkEditSwitchChanged} />
|
||||||
|
</Box>
|
||||||
|
</Card>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (<br />);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
: <QDynamicForm formData={formData} bulkEditMode bulkEditSwitchChangeHandler={bulkEditSwitchChanged} />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@ -505,7 +574,7 @@ function ProcessRun({process, defaultProcessValues, isModal, recordIds, closeMod
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (rs);
|
return (rs);
|
||||||
}
|
};
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// handle moving to another step in the process - e.g., after the backend told us what screen to show next. //
|
// handle moving to another step in the process - e.g., after the backend told us what screen to show next. //
|
||||||
@ -517,7 +586,7 @@ function ProcessRun({process, defaultProcessValues, isModal, recordIds, closeMod
|
|||||||
console.log("No process meta data yet, so returning early");
|
console.log("No process meta data yet, so returning early");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setPageHeader(processMetaData.label)
|
setPageHeader(processMetaData.label);
|
||||||
|
|
||||||
let newIndex = null;
|
let newIndex = null;
|
||||||
if (typeof newStep === "number")
|
if (typeof newStep === "number")
|
||||||
@ -560,6 +629,8 @@ function ProcessRun({process, defaultProcessValues, isModal, recordIds, closeMod
|
|||||||
{
|
{
|
||||||
let fullFieldList = getFullFieldList(activeStep, processValues);
|
let fullFieldList = getFullFieldList(activeStep, processValues);
|
||||||
const formData = DynamicFormUtils.getFormData(fullFieldList);
|
const formData = DynamicFormUtils.getFormData(fullFieldList);
|
||||||
|
DynamicFormUtils.addPossibleValueProps(formData.dynamicFormFields, fullFieldList, tableMetaData.name, null);
|
||||||
|
|
||||||
dynamicFormFields = formData.dynamicFormFields;
|
dynamicFormFields = formData.dynamicFormFields;
|
||||||
formValidations = formData.formValidations;
|
formValidations = formData.formValidations;
|
||||||
|
|
||||||
@ -592,7 +663,7 @@ function ProcessRun({process, defaultProcessValues, isModal, recordIds, closeMod
|
|||||||
dynamicFormFields[fieldName] = dynamicFormValue;
|
dynamicFormFields[fieldName] = dynamicFormValue;
|
||||||
initialValues[fieldName] = initialValue;
|
initialValues[fieldName] = initialValue;
|
||||||
formValidations[fieldName] = validation;
|
formValidations[fieldName] = validation;
|
||||||
}
|
};
|
||||||
|
|
||||||
if (doesStepHaveComponent(activeStep, QComponentType.VALIDATION_REVIEW_SCREEN))
|
if (doesStepHaveComponent(activeStep, QComponentType.VALIDATION_REVIEW_SCREEN))
|
||||||
{
|
{
|
||||||
@ -602,9 +673,9 @@ function ProcessRun({process, defaultProcessValues, isModal, recordIds, closeMod
|
|||||||
|
|
||||||
if (doesStepHaveComponent(activeStep, QComponentType.GOOGLE_DRIVE_SELECT_FOLDER))
|
if (doesStepHaveComponent(activeStep, QComponentType.GOOGLE_DRIVE_SELECT_FOLDER))
|
||||||
{
|
{
|
||||||
addField("googleDriveAccessToken", {type: "hidden", omitFromQDynamicForm: true}, null, null);
|
addField("googleDriveAccessToken", {type: "hidden", omitFromQDynamicForm: true}, "", null);
|
||||||
addField("googleDriveFolderId", {type: "hidden", omitFromQDynamicForm: true}, null, null);
|
addField("googleDriveFolderId", {type: "hidden", omitFromQDynamicForm: true}, "", null);
|
||||||
addField("googleDriveFolderName", {type: "hidden", omitFromQDynamicForm: true}, null, null);
|
addField("googleDriveFolderName", {type: "hidden", omitFromQDynamicForm: true}, "", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Object.keys(dynamicFormFields).length > 0)
|
if (Object.keys(dynamicFormFields).length > 0)
|
||||||
@ -673,6 +744,8 @@ function ProcessRun({process, defaultProcessValues, isModal, recordIds, closeMod
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
DynamicFormUtils.addPossibleValueProps(newDynamicFormFields, fullFieldList, tableMetaData.name, null);
|
||||||
|
|
||||||
setFormFields(newDynamicFormFields);
|
setFormFields(newDynamicFormFields);
|
||||||
setValidationScheme(Yup.object().shape(newFormValidations));
|
setValidationScheme(Yup.object().shape(newFormValidations));
|
||||||
}
|
}
|
||||||
@ -853,9 +926,9 @@ function ProcessRun({process, defaultProcessValues, isModal, recordIds, closeMod
|
|||||||
// queryStringPairsForInit.push("recordsParam=filterId");
|
// queryStringPairsForInit.push("recordsParam=filterId");
|
||||||
// queryStringPairsForInit.push(`filterId=${urlSearchParams.get("filterId")}`);
|
// queryStringPairsForInit.push(`filterId=${urlSearchParams.get("filterId")}`);
|
||||||
// }
|
// }
|
||||||
else if(recordIds)
|
else if (recordIds)
|
||||||
{
|
{
|
||||||
if(typeof recordIds === "string")
|
if (typeof recordIds === "string")
|
||||||
{
|
{
|
||||||
queryStringPairsForInit.push("recordsParam=recordIds");
|
queryStringPairsForInit.push("recordsParam=recordIds");
|
||||||
queryStringPairsForInit.push(`recordIds=${recordIds}`);
|
queryStringPairsForInit.push(`recordIds=${recordIds}`);
|
||||||
@ -991,7 +1064,7 @@ function ProcessRun({process, defaultProcessValues, isModal, recordIds, closeMod
|
|||||||
|
|
||||||
const handleCancelClicked = () =>
|
const handleCancelClicked = () =>
|
||||||
{
|
{
|
||||||
if(isModal && closeModalHandler)
|
if (isModal && closeModalHandler)
|
||||||
{
|
{
|
||||||
closeModalHandler(null, "cancelClicked");
|
closeModalHandler(null, "cancelClicked");
|
||||||
return;
|
return;
|
||||||
@ -1121,7 +1194,7 @@ function ProcessRun({process, defaultProcessValues, isModal, recordIds, closeMod
|
|||||||
</MDBox>
|
</MDBox>
|
||||||
);
|
);
|
||||||
|
|
||||||
if(isModal)
|
if (isModal)
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
<Box sx={{position: "absolute", overflowY: "auto", maxHeight: "100%", width: "100%"}}>
|
<Box sx={{position: "absolute", overflowY: "auto", maxHeight: "100%", width: "100%"}}>
|
||||||
|
@ -177,3 +177,9 @@ input[type="search"]::-webkit-search-decoration,
|
|||||||
input[type="search"]::-webkit-search-cancel-button,
|
input[type="search"]::-webkit-search-cancel-button,
|
||||||
input[type="search"]::-webkit-search-results-button,
|
input[type="search"]::-webkit-search-results-button,
|
||||||
input[type="search"]::-webkit-search-results-decoration { display: none; }
|
input[type="search"]::-webkit-search-results-decoration { display: none; }
|
||||||
|
|
||||||
|
/* Shrink the big margin-bottom on modal processes */
|
||||||
|
.modalProcess>.MuiBox-root>.MuiBox-root
|
||||||
|
{
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
@ -28,16 +28,60 @@ import {QTableSection} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTa
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
class QTableUtils
|
class QTableUtils
|
||||||
{
|
{
|
||||||
public static getSectionsForRecordSidebar(tableMetaData: QTableMetaData): QTableSection[]
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static getSectionsForRecordSidebar(tableMetaData: QTableMetaData, allowedKeys: any = null): QTableSection[]
|
||||||
{
|
{
|
||||||
if (tableMetaData.sections)
|
if (tableMetaData.sections)
|
||||||
{
|
{
|
||||||
return (tableMetaData.sections);
|
if (allowedKeys)
|
||||||
|
{
|
||||||
|
const allowedKeySet = new Set<string>();
|
||||||
|
allowedKeys.forEach((k: string) => allowedKeySet.add(k));
|
||||||
|
|
||||||
|
const allowedSections: QTableSection[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < tableMetaData.sections.length; i++)
|
||||||
|
{
|
||||||
|
const section = tableMetaData.sections[i];
|
||||||
|
if (section.fieldNames)
|
||||||
|
{
|
||||||
|
for (let j = 0; j < section.fieldNames.length; j++)
|
||||||
|
{
|
||||||
|
if (allowedKeySet.has(section.fieldNames[j]))
|
||||||
|
{
|
||||||
|
allowedSections.push(section);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (allowedSections);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
return (tableMetaData.sections);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
let fieldNames = [...tableMetaData.fields.keys()];
|
||||||
|
if (allowedKeys)
|
||||||
|
{
|
||||||
|
fieldNames = [];
|
||||||
|
for (const fieldName in tableMetaData.fields.keys())
|
||||||
|
{
|
||||||
|
if (allowedKeys[fieldName])
|
||||||
|
{
|
||||||
|
fieldNames.push(fieldName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return ([new QTableSection({
|
return ([new QTableSection({
|
||||||
iconName: "description", label: "All Fields", name: "allFields", fieldNames: [...tableMetaData.fields.keys()],
|
iconName: "description", label: "All Fields", name: "allFields", fieldNames: [...fieldNames],
|
||||||
})]);
|
})]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user