QQQ-30: got rid of 'prettier', first pass at eslint configuration pointing only to qqq specific files, reformated all qqq files

This commit is contained in:
Tim Chamberlain
2022-07-12 11:35:24 -05:00
parent cc324fd76d
commit 87d3f070fe
26 changed files with 2099 additions and 1959 deletions

View File

@ -23,18 +23,19 @@ import MDBox from "components/MDBox";
import EntityForm from "qqq/components/EntityForm";
import BaseLayout from "qqq/components/BaseLayout";
function EntityCreate(): JSX.Element {
return (
<BaseLayout>
<MDBox mt={4}>
<Grid container spacing={3}>
<Grid item xs={12} lg={12}>
<EntityForm />
</Grid>
</Grid>
</MDBox>
</BaseLayout>
);
function EntityCreate(): JSX.Element
{
return (
<BaseLayout>
<MDBox mt={4}>
<Grid container spacing={3}>
<Grid item xs={12} lg={12}>
<EntityForm />
</Grid>
</Grid>
</MDBox>
</BaseLayout>
);
}
export default EntityCreate;

View File

@ -24,26 +24,27 @@ import BaseLayout from "qqq/components/BaseLayout";
import { useParams } from "react-router-dom";
import EntityForm from "qqq/components/EntityForm";
function EntityEdit(): JSX.Element {
const { id } = useParams();
function EntityEdit(): JSX.Element
{
const { id } = useParams();
return (
<BaseLayout>
<MDBox mt={4}>
<Grid container spacing={3}>
<Grid item xs={12} lg={12}>
<MDBox mb={3}>
<Grid container spacing={3}>
<Grid item xs={12}>
<EntityForm id={id} />
</Grid>
</Grid>
</MDBox>
</Grid>
</Grid>
</MDBox>
</BaseLayout>
);
return (
<BaseLayout>
<MDBox mt={4}>
<Grid container spacing={3}>
<Grid item xs={12} lg={12}>
<MDBox mb={3}>
<Grid container spacing={3}>
<Grid item xs={12}>
<EntityForm id={id} />
</Grid>
</Grid>
</MDBox>
</Grid>
</Grid>
</MDBox>
</BaseLayout>
);
}
export default EntityEdit;

View File

@ -26,18 +26,18 @@ import Divider from "@mui/material/Divider";
import Link from "@mui/material/Link";
import { makeStyles, Alert } from "@mui/material";
import {
DataGrid,
GridCallbackDetails,
GridColDef,
GridColumnVisibilityModel,
GridFilterModel,
GridRowId,
GridRowParams,
GridRowsProp,
GridSelectionModel,
GridSortItem,
GridSortModel,
GridToolbar,
DataGrid,
GridCallbackDetails,
GridColDef,
GridColumnVisibilityModel,
GridFilterModel,
GridRowId,
GridRowParams,
GridRowsProp,
GridSelectionModel,
GridSortItem,
GridSortModel,
GridToolbar,
} from "@mui/x-data-grid";
// Material Dashboard 2 PRO React TS components
@ -66,325 +66,359 @@ interface Props {
table?: QTableMetaData;
}
function EntityList({ table }: Props): JSX.Element {
const tableNameParam = useParams().tableName;
const tableName = table === null ? tableNameParam : table.name;
function EntityList({ table }: Props): JSX.Element
{
const tableNameParam = useParams().tableName;
const tableName = table === null ? tableNameParam : table.name;
const [tableState, setTableState] = useState("");
const [filtersMenu, setFiltersMenu] = useState(null);
const [actionsMenu, setActionsMenu] = useState(null);
const [tableProcesses, setTableProcesses] = useState([] as QProcessMetaData[]);
const [pageNumber, setPageNumber] = useState(0);
const [totalRecords, setTotalRecords] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
const [selectedIds, setSelectedIds] = useState([] as string[]);
const [columns, setColumns] = useState([] as GridColDef[]);
const [rows, setRows] = useState([] as GridRowsProp[]);
const [loading, setLoading] = useState(true);
const [columnVisibilityModel, setColumnVisibilityModel] = useState({});
const [sortModel, setSortModel] = useState([] as GridSortItem[]);
const [filterModel, setFilterModel] = useState(null as GridFilterModel);
const [alertContent, setAlertContent] = useState("");
const [tableState, setTableState] = useState("");
const [filtersMenu, setFiltersMenu] = useState(null);
const [actionsMenu, setActionsMenu] = useState(null);
const [tableProcesses, setTableProcesses] = useState([] as QProcessMetaData[]);
const [pageNumber, setPageNumber] = useState(0);
const [totalRecords, setTotalRecords] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
const [selectedIds, setSelectedIds] = useState([] as string[]);
const [columns, setColumns] = useState([] as GridColDef[]);
const [rows, setRows] = useState([] as GridRowsProp[]);
const [loading, setLoading] = useState(true);
const [columnVisibilityModel, setColumnVisibilityModel] = useState({});
const [sortModel, setSortModel] = useState([] as GridSortItem[]);
const [filterModel, setFilterModel] = useState(null as GridFilterModel);
const [alertContent, setAlertContent] = useState("");
const [, forceUpdate] = useReducer((x) => x + 1, 0);
const [, forceUpdate] = useReducer((x) => x + 1, 0);
const openActionsMenu = (event: any) => setActionsMenu(event.currentTarget);
const closeActionsMenu = () => setActionsMenu(null);
const openFiltersMenu = (event: any) => setFiltersMenu(event.currentTarget);
const closeFiltersMenu = () => setFiltersMenu(null);
const openActionsMenu = (event: any) => setActionsMenu(event.currentTarget);
const closeActionsMenu = () => setActionsMenu(null);
const openFiltersMenu = (event: any) => setFiltersMenu(event.currentTarget);
const closeFiltersMenu = () => setFiltersMenu(null);
const translateCriteriaOperator = (operator: string) => {
switch (operator) {
const translateCriteriaOperator = (operator: string) =>
{
switch (operator)
{
case "contains":
return QCriteriaOperator.CONTAINS;
return QCriteriaOperator.CONTAINS;
case "starts with":
return QCriteriaOperator.STARTS_WITH;
return QCriteriaOperator.STARTS_WITH;
case "ends with":
return QCriteriaOperator.STARTS_WITH;
return QCriteriaOperator.STARTS_WITH;
case "is":
case "equals":
case "=":
return QCriteriaOperator.EQUALS;
return QCriteriaOperator.EQUALS;
case "is not":
case "!=":
return QCriteriaOperator.NOT_EQUALS;
return QCriteriaOperator.NOT_EQUALS;
case "is after":
case ">":
return QCriteriaOperator.GREATER_THAN;
return QCriteriaOperator.GREATER_THAN;
case "is on or after":
case ">=":
return QCriteriaOperator.GREATER_THAN_OR_EQUALS;
return QCriteriaOperator.GREATER_THAN_OR_EQUALS;
case "is before":
case "<":
return QCriteriaOperator.LESS_THAN;
return QCriteriaOperator.LESS_THAN;
case "is on or before":
case "<=":
return QCriteriaOperator.LESS_THAN_OR_EQUALS;
return QCriteriaOperator.LESS_THAN_OR_EQUALS;
case "is empty":
return QCriteriaOperator.IS_BLANK;
return QCriteriaOperator.IS_BLANK;
case "is not empty":
return QCriteriaOperator.IS_NOT_BLANK;
return QCriteriaOperator.IS_NOT_BLANK;
// case "is any of":
// TODO: handle this case
default:
return QCriteriaOperator.EQUALS;
}
};
return QCriteriaOperator.EQUALS;
}
};
const buildQFilter = () => {
const qFilter = new QQueryFilter();
sortModel.forEach((gridSortItem) => {
qFilter.addOrderBy(new QFilterOrderBy(gridSortItem.field, gridSortItem.sort === "asc"));
});
if (filterModel) {
filterModel.items.forEach((item) => {
qFilter.addCriteria(
new QFilterCriteria(item.columnField, translateCriteriaOperator(item.operatorValue), [
item.value,
])
);
const buildQFilter = () =>
{
const qFilter = new QQueryFilter();
sortModel.forEach((gridSortItem) =>
{
qFilter.addOrderBy(new QFilterOrderBy(gridSortItem.field, gridSortItem.sort === "asc"));
});
}
return qFilter;
};
const updateTable = () => {
(async () => {
const tableMetaData = await QClient.loadTableMetaData(tableName);
const count = await QClient.count(tableName);
setTotalRecords(count);
if (sortModel.length === 0) {
sortModel.push({ field: tableMetaData.primaryKeyField, sort: "desc" });
setSortModel(sortModel);
if (filterModel)
{
filterModel.items.forEach((item) =>
{
qFilter.addCriteria(
new QFilterCriteria(item.columnField, translateCriteriaOperator(item.operatorValue), [
item.value,
]),
);
});
}
const qFilter = buildQFilter();
const columns = [] as GridColDef[];
return qFilter;
};
const results = await QClient.query(
tableName,
qFilter,
rowsPerPage,
pageNumber * rowsPerPage
).catch((error) => {
if (error.message) {
setAlertContent(error.message);
} else {
setAlertContent(error.response.data.error);
}
throw error;
});
const updateTable = () =>
{
(async () =>
{
const tableMetaData = await QClient.loadTableMetaData(tableName);
const count = await QClient.count(tableName);
setTotalRecords(count);
const rows = [] as any[];
results.forEach((record) => {
rows.push(Object.fromEntries(record.values.entries()));
});
if (sortModel.length === 0)
{
sortModel.push({ field: tableMetaData.primaryKeyField, sort: "desc" });
setSortModel(sortModel);
}
const sortedKeys = [...tableMetaData.fields.keys()].sort();
sortedKeys.forEach((key) => {
const field = tableMetaData.fields.get(key);
const qFilter = buildQFilter();
const columns = [] as GridColDef[];
let columnType = "string";
switch (field.type) {
case QFieldType.DECIMAL:
case QFieldType.INTEGER:
columnType = "number";
break;
case QFieldType.DATE:
columnType = "date";
break;
case QFieldType.DATE_TIME:
columnType = "dateTime";
break;
case QFieldType.BOOLEAN:
columnType = "boolean";
break;
default:
const results = await QClient.query(
tableName,
qFilter,
rowsPerPage,
pageNumber * rowsPerPage,
).catch((error) =>
{
if (error.message)
{
setAlertContent(error.message);
}
else
{
setAlertContent(error.response.data.error);
}
throw error;
});
const rows = [] as any[];
results.forEach((record) =>
{
rows.push(Object.fromEntries(record.values.entries()));
});
const sortedKeys = [...tableMetaData.fields.keys()].sort();
sortedKeys.forEach((key) =>
{
const field = tableMetaData.fields.get(key);
let columnType = "string";
switch (field.type)
{
case QFieldType.DECIMAL:
case QFieldType.INTEGER:
columnType = "number";
break;
case QFieldType.DATE:
columnType = "date";
break;
case QFieldType.DATE_TIME:
columnType = "dateTime";
break;
case QFieldType.BOOLEAN:
columnType = "boolean";
break;
default:
// noop
}
}
const column = {
field: field.name,
type: columnType,
headerName: field.label,
width: 200,
};
const column = {
field: field.name,
type: columnType,
headerName: field.label,
width: 200,
};
if (key === tableMetaData.primaryKeyField) {
column.width = 75;
columns.splice(0, 0, column);
} else {
columns.push(column);
}
if (key === tableMetaData.primaryKeyField)
{
column.width = 75;
columns.splice(0, 0, column);
}
else
{
columns.push(column);
}
});
const columnVisibilityModel = localStorage.getItem(COLUMN_VISIBILITY_LOCAL_STORAGE_KEY);
if (columnVisibilityModel)
{
setColumnVisibilityModel(
JSON.parse(localStorage.getItem(COLUMN_VISIBILITY_LOCAL_STORAGE_KEY)),
);
}
setColumns(columns);
setRows(rows);
setLoading(false);
forceUpdate();
})();
};
const handlePageChange = (page: number) =>
{
setPageNumber(page);
};
const handleRowsPerPageChange = (size: number) =>
{
setRowsPerPage(size);
};
const handleRowClick = (params: GridRowParams) =>
{
document.location.href = `/${tableName}/${params.id}`;
};
const handleFilterChange = (filterModel: GridFilterModel) =>
{
setFilterModel(filterModel);
};
const selectionChanged = (selectionModel: GridSelectionModel, details: GridCallbackDetails) =>
{
const newSelectedIds: string[] = [];
selectionModel.forEach((value: GridRowId) =>
{
newSelectedIds.push(value as string);
});
setSelectedIds(newSelectedIds);
};
const columnVisibilityModel = localStorage.getItem(COLUMN_VISIBILITY_LOCAL_STORAGE_KEY);
if (columnVisibilityModel) {
setColumnVisibilityModel(
JSON.parse(localStorage.getItem(COLUMN_VISIBILITY_LOCAL_STORAGE_KEY))
);
const handleColumnVisibilityChange = (columnVisibilityModel: GridColumnVisibilityModel) =>
{
setColumnVisibilityModel(columnVisibilityModel);
localStorage.setItem(
COLUMN_VISIBILITY_LOCAL_STORAGE_KEY,
JSON.stringify(columnVisibilityModel),
);
};
const handleSortChange = (gridSort: GridSortModel) =>
{
setSortModel(gridSort);
localStorage.setItem(COLUMN_SORT_LOCAL_STORAGE_KEY, JSON.stringify(gridSort));
};
if (tableName !== tableState)
{
(async () =>
{
setTableState(tableName);
const metaData = await QClient.loadMetaData();
setTableProcesses(QProcessUtils.getProcessesForTable(metaData, tableName));
// reset rows to trigger rerender
setRows([]);
})();
}
function getRecordsQueryString()
{
if (selectedIds.length > 0)
{
return `?recordsParam=recordIds&recordIds=${selectedIds.join(",")}`;
}
setColumns(columns);
setRows(rows);
setLoading(false);
forceUpdate();
})();
};
return "";
}
const handlePageChange = (page: number) => {
setPageNumber(page);
};
const renderActionsMenu = (
<Menu
anchorEl={actionsMenu}
anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
transformOrigin={{ vertical: "top", horizontal: "left" }}
open={Boolean(actionsMenu)}
onClose={closeActionsMenu}
keepMounted
>
{tableProcesses.map((process) => (
<MenuItem key={process.name}>
<Link href={`/processes/${process.name}${getRecordsQueryString()}`}>{process.label}</Link>
</MenuItem>
))}
</Menu>
);
const handleRowsPerPageChange = (size: number) => {
setRowsPerPage(size);
};
useEffect(() =>
{
updateTable();
}, [pageNumber, rowsPerPage, tableState, sortModel, filterModel]);
const handleRowClick = (params: GridRowParams) => {
document.location.href = `/${tableName}/${params.id}`;
};
const handleFilterChange = (filterModel: GridFilterModel) => {
setFilterModel(filterModel);
};
const selectionChanged = (selectionModel: GridSelectionModel, details: GridCallbackDetails) => {
const newSelectedIds: string[] = [];
selectionModel.forEach((value: GridRowId) => {
newSelectedIds.push(value as string);
});
setSelectedIds(newSelectedIds);
};
const handleColumnVisibilityChange = (columnVisibilityModel: GridColumnVisibilityModel) => {
setColumnVisibilityModel(columnVisibilityModel);
localStorage.setItem(
COLUMN_VISIBILITY_LOCAL_STORAGE_KEY,
JSON.stringify(columnVisibilityModel)
);
};
const handleSortChange = (gridSort: GridSortModel) => {
setSortModel(gridSort);
localStorage.setItem(COLUMN_SORT_LOCAL_STORAGE_KEY, JSON.stringify(gridSort));
};
if (tableName !== tableState) {
(async () => {
setTableState(tableName);
const metaData = await QClient.loadMetaData();
setTableProcesses(QProcessUtils.getProcessesForTable(metaData, tableName));
// reset rows to trigger rerender
setRows([]);
})();
}
function getRecordsQueryString() {
if (selectedIds.length > 0) {
return `?recordsParam=recordIds&recordIds=${selectedIds.join(",")}`;
}
return "";
}
const renderActionsMenu = (
<Menu
anchorEl={actionsMenu}
anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
transformOrigin={{ vertical: "top", horizontal: "left" }}
open={Boolean(actionsMenu)}
onClose={closeActionsMenu}
keepMounted
>
{tableProcesses.map((process) => (
<MenuItem key={process.name}>
<Link href={`/processes/${process.name}${getRecordsQueryString()}`}>{process.label}</Link>
</MenuItem>
))}
</Menu>
);
useEffect(() => {
updateTable();
}, [pageNumber, rowsPerPage, tableState, sortModel, filterModel]);
return (
<DashboardLayout>
<DashboardNavbar />
<MDBox my={3}>
{alertContent ? (
<MDBox mb={3}>
<Alert severity="error">{alertContent}</Alert>
</MDBox>
) : (
""
)}
<MDBox display="flex" justifyContent="space-between" alignItems="flex-start" mb={2}>
<Link href={`/${tableName}/create`}>
<MDButton variant="gradient" color="info">
new {tableName}
</MDButton>
</Link>
<MDBox display="flex">
{tableProcesses.length > 0 && (
<MDButton
variant={actionsMenu ? "contained" : "outlined"}
color="dark"
onClick={openActionsMenu}
>
actions&nbsp;
<Icon>keyboard_arrow_down</Icon>
</MDButton>
return (
<DashboardLayout>
<DashboardNavbar />
<MDBox my={3}>
{alertContent ? (
<MDBox mb={3}>
<Alert severity="error">{alertContent}</Alert>
</MDBox>
) : (
""
)}
{renderActionsMenu}
</MDBox>
</MDBox>
<Card>
<MDBox height="100%">
<DataGrid
components={{ Toolbar: GridToolbar }}
paginationMode="server"
sortingMode="server"
filterMode="server"
page={pageNumber}
checkboxSelection
disableSelectionOnClick
autoHeight
rows={rows}
columns={columns}
rowBuffer={10}
rowCount={totalRecords}
pageSize={rowsPerPage}
rowsPerPageOptions={[10, 25, 50]}
onPageSizeChange={handleRowsPerPageChange}
onPageChange={handlePageChange}
onRowClick={handleRowClick}
density="compact"
loading={loading}
onFilterModelChange={handleFilterChange}
columnVisibilityModel={columnVisibilityModel}
onColumnVisibilityModelChange={handleColumnVisibilityChange}
onSelectionModelChange={selectionChanged}
onSortModelChange={handleSortChange}
sortingOrder={["asc", "desc"]}
sortModel={sortModel}
getRowClassName={(params) =>
params.indexRelativeToCurrentPage % 2 === 0 ? "even" : "odd"
}
/>
</MDBox>
</Card>
</MDBox>
<Footer />
</DashboardLayout>
);
<MDBox display="flex" justifyContent="space-between" alignItems="flex-start" mb={2}>
<Link href={`/${tableName}/create`}>
<MDButton variant="gradient" color="info">
new
{" "}
{tableName}
</MDButton>
</Link>
<MDBox display="flex">
{tableProcesses.length > 0 && (
<MDButton
variant={actionsMenu ? "contained" : "outlined"}
color="dark"
onClick={openActionsMenu}
>
actions&nbsp;
<Icon>keyboard_arrow_down</Icon>
</MDButton>
)}
{renderActionsMenu}
</MDBox>
</MDBox>
<Card>
<MDBox height="100%">
<DataGrid
components={{ Toolbar: GridToolbar }}
paginationMode="server"
sortingMode="server"
filterMode="server"
page={pageNumber}
checkboxSelection
disableSelectionOnClick
autoHeight
rows={rows}
columns={columns}
rowBuffer={10}
rowCount={totalRecords}
pageSize={rowsPerPage}
rowsPerPageOptions={[10, 25, 50]}
onPageSizeChange={handleRowsPerPageChange}
onPageChange={handlePageChange}
onRowClick={handleRowClick}
density="compact"
loading={loading}
onFilterModelChange={handleFilterChange}
columnVisibilityModel={columnVisibilityModel}
onColumnVisibilityModelChange={handleColumnVisibilityChange}
onSelectionModelChange={selectionChanged}
onSortModelChange={handleSortChange}
sortingOrder={["asc", "desc"]}
sortModel={sortModel}
getRowClassName={(params) => (params.indexRelativeToCurrentPage % 2 === 0 ? "even" : "odd")}
/>
</MDBox>
</Card>
</MDBox>
<Footer />
</DashboardLayout>
);
}
EntityList.defaultProps = {
table: null,
table: null,
};
export default EntityList;

View File

@ -48,161 +48,186 @@ interface Props {
id: string;
}
function ViewContents({ id }: Props): JSX.Element {
const { tableName } = useParams();
function ViewContents({ id }: Props): JSX.Element
{
const { tableName } = useParams();
const [asyncLoadInited, setAsyncLoadInited] = useState(false);
const [nameValues, setNameValues] = useState([] as JSX.Element[]);
const [open, setOpen] = useState(false);
const [tableMetaData, setTableMetaData] = useState(null);
const [tableProcesses, setTableProcesses] = useState([] as QProcessMetaData[]);
const [actionsMenu, setActionsMenu] = useState(null);
const [, forceUpdate] = useReducer((x) => x + 1, 0);
const [asyncLoadInited, setAsyncLoadInited] = useState(false);
const [nameValues, setNameValues] = useState([] as JSX.Element[]);
const [open, setOpen] = useState(false);
const [tableMetaData, setTableMetaData] = useState(null);
const [tableProcesses, setTableProcesses] = useState([] as QProcessMetaData[]);
const [actionsMenu, setActionsMenu] = useState(null);
const [, forceUpdate] = useReducer((x) => x + 1, 0);
const openActionsMenu = (event: any) => setActionsMenu(event.currentTarget);
const closeActionsMenu = () => setActionsMenu(null);
const openActionsMenu = (event: any) => setActionsMenu(event.currentTarget);
const closeActionsMenu = () => setActionsMenu(null);
if (!asyncLoadInited) {
setAsyncLoadInited(true);
if (!asyncLoadInited)
{
setAsyncLoadInited(true);
(async () => {
const tableMetaData = await qController.loadTableMetaData(tableName);
setTableMetaData(tableMetaData);
(async () =>
{
const tableMetaData = await qController.loadTableMetaData(tableName);
setTableMetaData(tableMetaData);
const metaData = await qController.loadMetaData();
setTableProcesses(QProcessUtils.getProcessesForTable(metaData, tableName));
const metaData = await qController.loadMetaData();
setTableProcesses(QProcessUtils.getProcessesForTable(metaData, tableName));
const foundRecord = await qController.get(tableName, id);
const foundRecord = await qController.get(tableName, id);
nameValues.push(
<MDBox key={tableMetaData.primaryKeyField} display="flex" py={1} pr={2}>
<MDTypography variant="button" fontWeight="bold" textTransform="capitalize">
{tableMetaData.primaryKeyField}: &nbsp;
</MDTypography>
<MDTypography variant="button" fontWeight="regular" color="text">
&nbsp;{id}
</MDTypography>
</MDBox>
);
nameValues.push(
<MDBox key={tableMetaData.primaryKeyField} display="flex" py={1} pr={2}>
<MDTypography variant="button" fontWeight="bold" textTransform="capitalize">
{tableMetaData.primaryKeyField}
: &nbsp;
</MDTypography>
<MDTypography variant="button" fontWeight="regular" color="text">
&nbsp;
{id}
</MDTypography>
</MDBox>,
);
const sortedKeys = [...foundRecord.values.keys()].sort();
sortedKeys.forEach((key) => {
if (key !== tableMetaData.primaryKeyField) {
nameValues.push(
<MDBox key={key} display="flex" py={1} pr={2}>
<MDTypography variant="button" fontWeight="bold" textTransform="capitalize">
{tableMetaData.fields.get(key).label}: &nbsp;
</MDTypography>
<MDTypography variant="button" fontWeight="regular" color="text">
&nbsp;{foundRecord.values.get(key)}
</MDTypography>
const sortedKeys = [...foundRecord.values.keys()].sort();
sortedKeys.forEach((key) =>
{
if (key !== tableMetaData.primaryKeyField)
{
nameValues.push(
<MDBox key={key} display="flex" py={1} pr={2}>
<MDTypography variant="button" fontWeight="bold" textTransform="capitalize">
{tableMetaData.fields.get(key).label}
: &nbsp;
</MDTypography>
<MDTypography variant="button" fontWeight="regular" color="text">
&nbsp;
{foundRecord.values.get(key)}
</MDTypography>
</MDBox>,
);
}
});
setNameValues(nameValues);
forceUpdate();
})();
}
const handleClickConfirmOpen = () =>
{
setOpen(true);
};
const handleClickConfirmClose = () =>
{
setOpen(false);
};
const handleDelete = (event: { preventDefault: () => void }) =>
{
event.preventDefault();
(async () =>
{
await qController.delete(tableName, id).then(() =>
{
window.location.href = `/${tableName}`;
});
})();
};
const editPath = `/${tableName}/${id}/edit`;
const renderActionsMenu = (
<Menu
anchorEl={actionsMenu}
anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
transformOrigin={{ vertical: "top", horizontal: "left" }}
open={Boolean(actionsMenu)}
onClose={closeActionsMenu}
keepMounted
>
{tableProcesses.map((process) => (
<MenuItem key={process.name}>
<Link href={`/processes/${process.name}?recordIds=${id}`}>{process.label}</Link>
</MenuItem>
))}
</Menu>
);
return (
<Card id="basic-info" sx={{ overflow: "visible" }}>
<MDBox p={3}>
<MDBox display="flex" justifyContent="space-between">
<MDTypography variant="h5">
Viewing
{" "}
{tableMetaData?.label}
{" "}
(
{id}
)
</MDTypography>
{tableProcesses.length > 0 && (
<MDButton
variant={actionsMenu ? "contained" : "outlined"}
color="dark"
onClick={openActionsMenu}
>
actions&nbsp;
<Icon>keyboard_arrow_down</Icon>
</MDButton>
)}
{renderActionsMenu}
</MDBox>
);
}
});
setNameValues(nameValues);
forceUpdate();
})();
}
const handleClickConfirmOpen = () => {
setOpen(true);
};
const handleClickConfirmClose = () => {
setOpen(false);
};
const handleDelete = (event: { preventDefault: () => void }) => {
event.preventDefault();
(async () => {
await qController.delete(tableName, id).then(() => {
window.location.href = `/${tableName}`;
});
})();
};
const editPath = `/${tableName}/${id}/edit`;
const renderActionsMenu = (
<Menu
anchorEl={actionsMenu}
anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
transformOrigin={{ vertical: "top", horizontal: "left" }}
open={Boolean(actionsMenu)}
onClose={closeActionsMenu}
keepMounted
>
{tableProcesses.map((process) => (
<MenuItem key={process.name}>
<Link href={`/processes/${process.name}?recordIds=${id}`}>{process.label}</Link>
</MenuItem>
))}
</Menu>
);
return (
<Card id="basic-info" sx={{ overflow: "visible" }}>
<MDBox p={3}>
<MDBox display="flex" justifyContent="space-between">
<MDTypography variant="h5">
Viewing {tableMetaData?.label} ({id})
</MDTypography>
{tableProcesses.length > 0 && (
<MDButton
variant={actionsMenu ? "contained" : "outlined"}
color="dark"
onClick={openActionsMenu}
>
actions&nbsp;
<Icon>keyboard_arrow_down</Icon>
</MDButton>
)}
{renderActionsMenu}
</MDBox>
</MDBox>
<MDBox p={3}>{nameValues}</MDBox>
<MDBox component="form" pb={3} px={3}>
<Grid key="tres" container spacing={3}>
<MDBox ml="auto" mr={3}>
<MDButton
variant="gradient"
color="primary"
size="small"
onClick={handleClickConfirmOpen}
>
delete {tableMetaData?.label}
</MDButton>
<Dialog
open={open}
onClose={handleClickConfirmClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">Confirm Deletion</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Are you sure you want to delete this record?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClickConfirmClose}>No</Button>
<Button onClick={handleDelete} autoFocus>
Yes
</Button>
</DialogActions>
</Dialog>
</MDBox>
<MDBox>
<MDButton variant="gradient" color="dark" size="small">
<Link href={editPath}>edit {tableMetaData?.label}</Link>
</MDButton>
</MDBox>
</Grid>
</MDBox>
</Card>
);
</MDBox>
<MDBox p={3}>{nameValues}</MDBox>
<MDBox component="form" pb={3} px={3}>
<Grid key="tres" container spacing={3}>
<MDBox ml="auto" mr={3}>
<MDButton
variant="gradient"
color="primary"
size="small"
onClick={handleClickConfirmOpen}
>
delete
{" "}
{tableMetaData?.label}
</MDButton>
<Dialog
open={open}
onClose={handleClickConfirmClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">Confirm Deletion</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Are you sure you want to delete this record?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClickConfirmClose}>No</Button>
<Button onClick={handleDelete} autoFocus>
Yes
</Button>
</DialogActions>
</Dialog>
</MDBox>
<MDBox>
<MDButton variant="gradient" color="dark" size="small">
<Link href={editPath}>
edit
{tableMetaData?.label}
</Link>
</MDButton>
</MDBox>
</Grid>
</MDBox>
</Card>
);
}
export default ViewContents;

View File

@ -26,26 +26,27 @@ import MDBox from "components/MDBox";
import BaseLayout from "qqq/components/BaseLayout";
import ViewContents from "./components/ViewContents";
function EntityView(): JSX.Element {
const { id } = useParams();
function EntityView(): JSX.Element
{
const { id } = useParams();
return (
<BaseLayout>
<MDBox mt={4}>
<Grid container spacing={3}>
<Grid item xs={12} lg={12}>
<MDBox mb={3}>
<Grid container spacing={3}>
<Grid item xs={12}>
<ViewContents id={id} />
</Grid>
</Grid>
</MDBox>
</Grid>
</Grid>
</MDBox>
</BaseLayout>
);
return (
<BaseLayout>
<MDBox mt={4}>
<Grid container spacing={3}>
<Grid item xs={12} lg={12}>
<MDBox mb={3}>
<Grid container spacing={3}>
<Grid item xs={12}>
<ViewContents id={id} />
</Grid>
</Grid>
</MDBox>
</Grid>
</Grid>
</MDBox>
</BaseLayout>
);
}
export default EntityView;

View File

@ -45,415 +45,491 @@ import { QJobStarted } from "@kingsrook/qqq-frontend-core/lib/model/processes/QJ
import { QJobComplete } from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobComplete";
import { QJobError } from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobError";
import { QJobRunning } from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobRunning";
import { DataGrid, GridColDef, GridRowParams, GridRowsProp } from "@mui/x-data-grid";
import {
DataGrid, GridColDef, GridRowParams, GridRowsProp,
} from "@mui/x-data-grid";
import QDynamicForm from "../../components/QDynamicForm";
import MDTypography from "../../../components/MDTypography";
function getDynamicStepContent(
stepIndex: number,
step: any,
formData: any,
processError: string,
processValues: any,
recordConfig: any
): JSX.Element {
const { formFields, values, errors, touched } = formData;
// console.log(`in getDynamicStepContent: step label ${step?.label}`);
stepIndex: number,
step: any,
formData: any,
processError: string,
processValues: any,
recordConfig: any,
): JSX.Element
{
const {
formFields, values, errors, touched,
} = formData;
// console.log(`in getDynamicStepContent: step label ${step?.label}`);
if (processError) {
return (
if (processError)
{
return (
<>
<MDTypography color="error" variant="h3">
Error
</MDTypography>
<div>{processError}</div>
</>
);
}
if (!Object.keys(formFields).length)
{
// console.log("in getDynamicStepContent. No fields yet, so returning 'loading'");
return <div>Loading...</div>;
}
console.log(`in getDynamicStepContent. the step looks like: ${JSON.stringify(step)}`);
return (
<>
<MDTypography color="error" variant="h3">
Error
</MDTypography>
<div>{processError}</div>
</>
);
}
if (!Object.keys(formFields).length) {
// console.log("in getDynamicStepContent. No fields yet, so returning 'loading'");
return <div>Loading...</div>;
}
console.log(`in getDynamicStepContent. the step looks like: ${JSON.stringify(step)}`);
return (
<>
{step.formFields && <QDynamicForm formData={formData} formLabel={step.label} />}
{step.viewFields && (
<div>
{step.viewFields.map((field: QFieldMetaData) => (
<div key={field.name}>
<b>{field.label}:</b> {processValues[field.name]}
{step.formFields && <QDynamicForm formData={formData} formLabel={step.label} />}
{step.viewFields && (
<div>
{step.viewFields.map((field: QFieldMetaData) => (
<div key={field.name}>
<b>
{field.label}
:
</b>
{" "}
{processValues[field.name]}
</div>
))}
</div>
))}
</div>
)}
{step.recordListFields && (
<div>
<b>Records:</b> <br />
<MDBox height="100%">
<DataGrid
page={recordConfig.pageNo}
disableSelectionOnClick
autoHeight
rows={recordConfig.rows}
columns={recordConfig.columns}
rowBuffer={10}
rowCount={recordConfig.totalRecords}
pageSize={recordConfig.rowsPerPage}
rowsPerPageOptions={[10, 25, 50]}
onPageSizeChange={recordConfig.handleRowsPerPageChange}
onPageChange={recordConfig.handlePageChange}
onRowClick={recordConfig.handleRowClick}
paginationMode="server"
density="compact"
loading={recordConfig.loading}
/>
</MDBox>
</div>
)}
</>
);
)}
{step.recordListFields && (
<div>
<b>Records:</b>
{" "}
<br />
<MDBox height="100%">
<DataGrid
page={recordConfig.pageNo}
disableSelectionOnClick
autoHeight
rows={recordConfig.rows}
columns={recordConfig.columns}
rowBuffer={10}
rowCount={recordConfig.totalRecords}
pageSize={recordConfig.rowsPerPage}
rowsPerPageOptions={[10, 25, 50]}
onPageSizeChange={recordConfig.handleRowsPerPageChange}
onPageChange={recordConfig.handlePageChange}
onRowClick={recordConfig.handleRowClick}
paginationMode="server"
density="compact"
loading={recordConfig.loading}
/>
</MDBox>
</div>
)}
</>
);
}
function trace(name: string, isComponent: boolean = false) {
if (isComponent) {
console.log(`COMPONENT: ${name}`);
} else {
console.log(` function: ${name}`);
}
function trace(name: string, isComponent: boolean = false)
{
if (isComponent)
{
console.log(`COMPONENT: ${name}`);
}
else
{
console.log(` function: ${name}`);
}
}
const qController = new QController("");
function ProcessRun(): JSX.Element {
const { processName } = useParams();
const [processUUID, setProcessUUID] = useState(null as string);
const [jobUUID, setJobUUID] = useState(null as string);
const [activeStepIndex, setActiveStepIndex] = useState(0);
const [activeStep, setActiveStep] = useState(null as QFrontendStepMetaData);
const [newStep, setNewStep] = useState(null);
const [steps, setSteps] = useState([] as QFrontendStepMetaData[]);
const [needInitialLoad, setNeedInitialLoad] = useState(true);
const [processMetaData, setProcessMetaData] = useState(null);
const [processValues, setProcessValues] = useState({} as any);
const [lastProcessResponse, setLastProcessResponse] = useState(
null as QJobStarted | QJobComplete | QJobError | QJobRunning
);
const [formId, setFormId] = useState("");
const [formFields, setFormFields] = useState({});
const [initialValues, setInitialValues] = useState({});
const [validations, setValidations] = useState({});
const [needToCheckJobStatus, setNeedToCheckJobStatus] = useState(false);
const [needRecords, setNeedRecords] = useState(false);
const [processError, setProcessError] = useState(null as string);
const [recordConfig, setRecordConfig] = useState({} as any);
const onLastStep = activeStepIndex === steps.length - 2;
const noMoreSteps = activeStepIndex === steps.length - 1;
function ProcessRun(): JSX.Element
{
const { processName } = useParams();
const [processUUID, setProcessUUID] = useState(null as string);
const [jobUUID, setJobUUID] = useState(null as string);
const [activeStepIndex, setActiveStepIndex] = useState(0);
const [activeStep, setActiveStep] = useState(null as QFrontendStepMetaData);
const [newStep, setNewStep] = useState(null);
const [steps, setSteps] = useState([] as QFrontendStepMetaData[]);
const [needInitialLoad, setNeedInitialLoad] = useState(true);
const [processMetaData, setProcessMetaData] = useState(null);
const [processValues, setProcessValues] = useState({} as any);
const [lastProcessResponse, setLastProcessResponse] = useState(
null as QJobStarted | QJobComplete | QJobError | QJobRunning,
);
const [formId, setFormId] = useState("");
const [formFields, setFormFields] = useState({});
const [initialValues, setInitialValues] = useState({});
const [validations, setValidations] = useState({});
const [needToCheckJobStatus, setNeedToCheckJobStatus] = useState(false);
const [needRecords, setNeedRecords] = useState(false);
const [processError, setProcessError] = useState(null as string);
const [recordConfig, setRecordConfig] = useState({} as any);
const onLastStep = activeStepIndex === steps.length - 2;
const noMoreSteps = activeStepIndex === steps.length - 1;
trace("ProcessRun", true);
trace("ProcessRun", true);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// handle moving to another step in the process - e.g., after the backend told us what screen to show next. //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
useEffect(() => {
trace("updateActiveStep");
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// handle moving to another step in the process - e.g., after the backend told us what screen to show next. //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
useEffect(() =>
{
trace("updateActiveStep");
if (!processMetaData) {
console.log("No process meta data yet, so returning early");
return;
}
// console.log(`Steps are: ${steps}`);
// console.log(`Setting step to ${newStep}`);
let newIndex = null;
if (typeof newStep === "number") {
newIndex = newStep as number;
} else if (typeof newStep === "string") {
for (let i = 0; i < steps.length; i++) {
if (steps[i].name === newStep) {
newIndex = i;
break;
}
}
}
if (newIndex === null) {
setProcessError(`Unknown process step ${newStep}.`);
}
setActiveStepIndex(newIndex);
if (steps) {
const activeStep = steps[newIndex];
setActiveStep(activeStep);
setFormId(activeStep.name);
///////////////////////////////////////////////////
// if this step has form fields, set up the form //
///////////////////////////////////////////////////
if (activeStep.formFields) {
const { dynamicFormFields, formValidations } = DynamicFormUtils.getFormData(
activeStep.formFields
);
const initialValues: any = {};
activeStep.formFields.forEach((field) => {
initialValues[field.name] = processValues[field.name];
});
setFormFields(dynamicFormFields);
setInitialValues(initialValues);
setValidations(Yup.object().shape(formValidations));
if (!processMetaData)
{
console.log("No process meta data yet, so returning early");
return;
}
if (activeStep.recordListFields) {
const newRecordConfig = {} as any;
newRecordConfig.pageNo = 1;
newRecordConfig.rowsPerPage = 20;
newRecordConfig.columns = [] as GridColDef[];
newRecordConfig.rows = [];
newRecordConfig.totalRecords = 0;
newRecordConfig.handleRowsPerPageChange = null;
newRecordConfig.handlePageChange = null;
newRecordConfig.handleRowClick = null;
newRecordConfig.loading = true;
activeStep.recordListFields.forEach((field) => {
newRecordConfig.columns.push({ field: field.name, headerName: field.label });
});
setRecordConfig(newRecordConfig);
setNeedRecords(true);
// console.log(`Steps are: ${steps}`);
// console.log(`Setting step to ${newStep}`);
let newIndex = null;
if (typeof newStep === "number")
{
newIndex = newStep as number;
}
else if (typeof newStep === "string")
{
for (let i = 0; i < steps.length; i++)
{
if (steps[i].name === newStep)
{
newIndex = i;
break;
}
}
}
if (newIndex === null)
{
setProcessError(`Unknown process step ${newStep}.`);
}
setActiveStepIndex(newIndex);
if (steps)
{
const activeStep = steps[newIndex];
setActiveStep(activeStep);
setFormId(activeStep.name);
///////////////////////////////////////////////////
// if this step has form fields, set up the form //
///////////////////////////////////////////////////
if (activeStep.formFields)
{
const { dynamicFormFields, formValidations } = DynamicFormUtils.getFormData(
activeStep.formFields,
);
const initialValues: any = {};
activeStep.formFields.forEach((field) =>
{
initialValues[field.name] = processValues[field.name];
});
setFormFields(dynamicFormFields);
setInitialValues(initialValues);
setValidations(Yup.object().shape(formValidations));
}
if (activeStep.recordListFields)
{
const newRecordConfig = {} as any;
newRecordConfig.pageNo = 1;
newRecordConfig.rowsPerPage = 20;
newRecordConfig.columns = [] as GridColDef[];
newRecordConfig.rows = [];
newRecordConfig.totalRecords = 0;
newRecordConfig.handleRowsPerPageChange = null;
newRecordConfig.handlePageChange = null;
newRecordConfig.handleRowClick = null;
newRecordConfig.loading = true;
activeStep.recordListFields.forEach((field) =>
{
newRecordConfig.columns.push({ field: field.name, headerName: field.label });
});
setRecordConfig(newRecordConfig);
setNeedRecords(true);
}
// console.log(`in updateActiveStep: formFields ${JSON.stringify(dynamicFormFields)}`);
// console.log(`in updateActiveStep: initialValues ${JSON.stringify(initialValues)}`);
}
}, [newStep]);
useEffect(() => {
if (needRecords) {
setNeedRecords(false);
(async () => {
const records = await qController.processRecords(
processName,
processUUID,
recordConfig.rowsPerPage * (recordConfig.pageNo - 1),
recordConfig.rowsPerPage
);
recordConfig.loading = false;
recordConfig.rows = [];
let rowId = 0;
records.forEach((record) => {
const row = Object.fromEntries(record.values.entries());
if (!row.id) {
row.id = ++rowId;
}
recordConfig.rows.push(row);
});
// todo count?
recordConfig.totalRecords = records.length;
setRecordConfig(recordConfig);
})();
}
}, [needRecords]);
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// handle a response from the server - e.g., after starting a backend job, or getting its status/result //
//////////////////////////////////////////////////////////////////////////////////////////////////////////
useEffect(() => {
if (lastProcessResponse) {
trace("handleProcessResponse");
setLastProcessResponse(null);
if (lastProcessResponse instanceof QJobComplete) {
const qJobComplete = lastProcessResponse as QJobComplete;
console.log("Setting new step.");
setNewStep(qJobComplete.nextStep);
setProcessValues(qJobComplete.values);
// console.log(`Updated process values: ${JSON.stringify(qJobComplete.values)}`);
} else if (lastProcessResponse instanceof QJobStarted) {
const qJobStarted = lastProcessResponse as QJobStarted;
setJobUUID(qJobStarted.jobUUID);
setNeedToCheckJobStatus(true);
} else if (lastProcessResponse instanceof QJobRunning) {
const qJobRunning = lastProcessResponse as QJobRunning;
setNeedToCheckJobStatus(true);
} else if (lastProcessResponse instanceof QJobError) {
const qJobError = lastProcessResponse as QJobError;
console.log(`Got an error from the backend... ${qJobError.error}`);
setProcessError(qJobError.error);
}
}
}, [lastProcessResponse]);
}, [newStep]);
/////////////////////////////////////////////////////////////////////////
// while a backend async job is running, periodically check its status //
/////////////////////////////////////////////////////////////////////////
useEffect(() => {
if (needToCheckJobStatus) {
trace("checkJobStatus");
setNeedToCheckJobStatus(false);
(async () => {
setTimeout(async () => {
const processResponse = await qController.processJobStatus(
processName,
processUUID,
jobUUID
);
setLastProcessResponse(processResponse);
}, 1500);
})();
}
}, [needToCheckJobStatus]);
//////////////////////////////////////////////////////////////////////////////////////////
// do the initial load of data for the process - that is, meta data, plus the init step //
//////////////////////////////////////////////////////////////////////////////////////////
if (needInitialLoad) {
trace("initialLoad");
setNeedInitialLoad(false);
(async () => {
const { search } = useLocation();
const urlSearchParams = new URLSearchParams(search);
let queryStringForInit = null;
if (urlSearchParams.get("recordIds")) {
queryStringForInit = `recordsParam=recordIds&recordIds=${urlSearchParams.get("recordIds")}`;
} else if (urlSearchParams.get("filterJSON")) {
queryStringForInit = `recordsParam=filterJSON&filterJSON=${urlSearchParams.get(
"filterJSON"
)}`;
useEffect(() =>
{
if (needRecords)
{
setNeedRecords(false);
(async () =>
{
const records = await qController.processRecords(
processName,
processUUID,
recordConfig.rowsPerPage * (recordConfig.pageNo - 1),
recordConfig.rowsPerPage,
);
recordConfig.loading = false;
recordConfig.rows = [];
let rowId = 0;
records.forEach((record) =>
{
const row = Object.fromEntries(record.values.entries());
if (!row.id)
{
row.id = ++rowId;
}
recordConfig.rows.push(row);
});
// todo count?
recordConfig.totalRecords = records.length;
setRecordConfig(recordConfig);
})();
}
// todo once saved filters exist
//else if(urlSearchParams.get("filterId")) {
// queryStringForInit = `recordsParam=filterId&filterId=${urlSearchParams.get("filterId")}`
// }
}, [needRecords]);
console.log(`@dk: Query String for init: ${queryStringForInit}`);
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// handle a response from the server - e.g., after starting a backend job, or getting its status/result //
//////////////////////////////////////////////////////////////////////////////////////////////////////////
useEffect(() =>
{
if (lastProcessResponse)
{
trace("handleProcessResponse");
setLastProcessResponse(null);
if (lastProcessResponse instanceof QJobComplete)
{
const qJobComplete = lastProcessResponse as QJobComplete;
console.log("Setting new step.");
setNewStep(qJobComplete.nextStep);
setProcessValues(qJobComplete.values);
// console.log(`Updated process values: ${JSON.stringify(qJobComplete.values)}`);
}
else if (lastProcessResponse instanceof QJobStarted)
{
const qJobStarted = lastProcessResponse as QJobStarted;
setJobUUID(qJobStarted.jobUUID);
setNeedToCheckJobStatus(true);
}
else if (lastProcessResponse instanceof QJobRunning)
{
const qJobRunning = lastProcessResponse as QJobRunning;
setNeedToCheckJobStatus(true);
}
else if (lastProcessResponse instanceof QJobError)
{
const qJobError = lastProcessResponse as QJobError;
console.log(`Got an error from the backend... ${qJobError.error}`);
setProcessError(qJobError.error);
}
}
}, [lastProcessResponse]);
const processMetaData = await qController.loadProcessMetaData(processName);
// console.log(processMetaData);
setProcessMetaData(processMetaData);
setSteps(processMetaData.frontendSteps);
/////////////////////////////////////////////////////////////////////////
// while a backend async job is running, periodically check its status //
/////////////////////////////////////////////////////////////////////////
useEffect(() =>
{
if (needToCheckJobStatus)
{
trace("checkJobStatus");
setNeedToCheckJobStatus(false);
(async () =>
{
setTimeout(async () =>
{
const processResponse = await qController.processJobStatus(
processName,
processUUID,
jobUUID,
);
setLastProcessResponse(processResponse);
}, 1500);
})();
}
}, [needToCheckJobStatus]);
const processResponse = await qController.processInit(processName, queryStringForInit);
setProcessUUID(processResponse.processUUID);
setLastProcessResponse(processResponse);
//////////////////////////////////////////////////////////////////////////////////////////
// do the initial load of data for the process - that is, meta data, plus the init step //
//////////////////////////////////////////////////////////////////////////////////////////
if (needInitialLoad)
{
trace("initialLoad");
setNeedInitialLoad(false);
(async () =>
{
const { search } = useLocation();
const urlSearchParams = new URLSearchParams(search);
let queryStringForInit = null;
if (urlSearchParams.get("recordIds"))
{
queryStringForInit = `recordsParam=recordIds&recordIds=${urlSearchParams.get(
"recordIds",
)}`;
}
else if (urlSearchParams.get("filterJSON"))
{
queryStringForInit = `recordsParam=filterJSON&filterJSON=${urlSearchParams.get(
"filterJSON",
)}`;
}
// todo once saved filters exist
//else if(urlSearchParams.get("filterId")) {
// queryStringForInit = `recordsParam=filterId&filterId=${urlSearchParams.get("filterId")}`
// }
console.log(`@dk: Query String for init: ${queryStringForInit}`);
const processMetaData = await qController.loadProcessMetaData(processName);
// console.log(processMetaData);
setProcessMetaData(processMetaData);
setSteps(processMetaData.frontendSteps);
const processResponse = await qController.processInit(processName, queryStringForInit);
setProcessUUID(processResponse.processUUID);
setLastProcessResponse(processResponse);
// console.log(processResponse);
})();
}
})();
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
// handle the back button - todo - not really done at all //
// e.g., qqq needs to say when back is or isn't allowed, and we need to hit the backend upon backs. //
//////////////////////////////////////////////////////////////////////////////////////////////////////
const handleBack = () => {
trace("handleBack");
setNewStep(activeStepIndex - 1);
};
//////////////////////////////////////////////////////////////////////////////////////////////////////
// handle the back button - todo - not really done at all //
// e.g., qqq needs to say when back is or isn't allowed, and we need to hit the backend upon backs. //
//////////////////////////////////////////////////////////////////////////////////////////////////////
const handleBack = () =>
{
trace("handleBack");
setNewStep(activeStepIndex - 1);
};
//////////////////////////////////////////////////////////////////////////////////////////
// handle user submitting the form - which in qqq means moving forward from any screen. //
//////////////////////////////////////////////////////////////////////////////////////////
const handleSubmit = async (values: any, actions: any) => {
trace("handleSubmit");
//////////////////////////////////////////////////////////////////////////////////////////
// handle user submitting the form - which in qqq means moving forward from any screen. //
//////////////////////////////////////////////////////////////////////////////////////////
const handleSubmit = async (values: any, actions: any) =>
{
trace("handleSubmit");
// todo - post?
let queryString = "";
Object.keys(values).forEach((key) => {
queryString += `${key}=${encodeURIComponent(values[key])}&`;
});
// todo - post?
let queryString = "";
Object.keys(values).forEach((key) =>
{
queryString += `${key}=${encodeURIComponent(values[key])}&`;
});
actions.setSubmitting(false);
actions.resetForm();
actions.setSubmitting(false);
actions.resetForm();
const processResponse = await qController.processStep(
processName,
processUUID,
activeStep.name,
queryString
);
setLastProcessResponse(processResponse);
};
const processResponse = await qController.processStep(
processName,
processUUID,
activeStep.name,
queryString,
);
setLastProcessResponse(processResponse);
};
return (
<DashboardLayout>
<DashboardNavbar />
<MDBox py={3} mb={20} height="65vh">
<Grid container justifyContent="center" alignItems="center" sx={{ height: "100%", mt: 8 }}>
<Grid item xs={12} lg={8}>
<Formik
enableReinitialize
initialValues={initialValues}
validationSchema={validations}
onSubmit={handleSubmit}
return (
<DashboardLayout>
<DashboardNavbar />
<MDBox py={3} mb={20} height="65vh">
<Grid
container
justifyContent="center"
alignItems="center"
sx={{ height: "100%", mt: 8 }}
>
{({ values, errors, touched, isSubmitting }) => (
<Form id={formId} autoComplete="off">
<Card sx={{ height: "100%" }}>
<MDBox mx={2} mt={-3}>
<Stepper activeStep={activeStepIndex} alternativeLabel>
{steps.map((step) => (
<Step key={step.name}>
<StepLabel>{step.label}</StepLabel>
</Step>
))}
</Stepper>
</MDBox>
<MDBox p={3}>
<MDBox>
{/***************************************************************************
** step content - e.g., the appropriate form or other screen for the step **
***************************************************************************/}
{getDynamicStepContent(
activeStepIndex,
activeStep,
{
values,
touched,
formFields,
errors,
},
processError,
processValues,
recordConfig
)}
{/********************************
** back &| next/submit buttons **
********************************/}
<MDBox mt={2} width="100%" display="flex" justifyContent="space-between">
{true || activeStepIndex === 0 ? (
<MDBox />
) : (
<MDButton variant="gradient" color="light" onClick={handleBack}>
back
</MDButton>
)}
{noMoreSteps || processError ? (
<MDBox />
) : (
<MDButton
disabled={isSubmitting}
type="submit"
variant="gradient"
color="dark"
>
{onLastStep ? "submit" : "next"}
</MDButton>
)}
</MDBox>
</MDBox>
</MDBox>
</Card>
</Form>
)}
</Formik>
</Grid>
</Grid>
</MDBox>
<Footer />
</DashboardLayout>
);
<Grid item xs={12} lg={8}>
<Formik
enableReinitialize
initialValues={initialValues}
validationSchema={validations}
onSubmit={handleSubmit}
>
{({
values, errors, touched, isSubmitting,
}) => (
<Form id={formId} autoComplete="off">
<Card sx={{ height: "100%" }}>
<MDBox mx={2} mt={-3}>
<Stepper activeStep={activeStepIndex} alternativeLabel>
{steps.map((step) => (
<Step key={step.name}>
<StepLabel>{step.label}</StepLabel>
</Step>
))}
</Stepper>
</MDBox>
<MDBox p={3}>
<MDBox>
{/***************************************************************************
** step content - e.g., the appropriate form or other screen for the step **
***************************************************************************/}
{getDynamicStepContent(
activeStepIndex,
activeStep,
{
values,
touched,
formFields,
errors,
},
processError,
processValues,
recordConfig,
)}
{/********************************
** back &| next/submit buttons **
********************************/}
<MDBox
mt={2}
width="100%"
display="flex"
justifyContent="space-between"
>
{true || activeStepIndex === 0 ? (
<MDBox />
) : (
<MDButton
variant="gradient"
color="light"
onClick={handleBack}
>
back
</MDButton>
)}
{noMoreSteps || processError ? (
<MDBox />
) : (
<MDButton
disabled={isSubmitting}
type="submit"
variant="gradient"
color="dark"
>
{onLastStep ? "submit" : "next"}
</MDButton>
)}
</MDBox>
</MDBox>
</MDBox>
</Card>
</Form>
)}
</Formik>
</Grid>
</Grid>
</MDBox>
<Footer />
</DashboardLayout>
);
}
export default ProcessRun;