mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-17 21:00:45 +00:00
QQQ-37 checkpoint - cleanup of various kinds on process validation & result
This commit is contained in:
@ -48,15 +48,17 @@ function QProcessSummaryResults({
|
||||
qInstance, process, table = null, processValues, step,
|
||||
}: Props): JSX.Element
|
||||
{
|
||||
const sourceTable = qInstance.tables.get(processValues.sourceTable);
|
||||
|
||||
const resultValidationList = (
|
||||
<List sx={{mt: 2}}>
|
||||
{
|
||||
processValues?.recordCount && table && (
|
||||
processValues?.recordCount !== undefined && sourceTable && (
|
||||
<ListItem sx={{my: 2}}>
|
||||
<ListItemText primaryTypographyProps={{fontSize: 16}}>
|
||||
{processValues.recordCount.toLocaleString()}
|
||||
{" "}
|
||||
{table.label}
|
||||
{sourceTable.label}
|
||||
{" "}
|
||||
records were processed.
|
||||
</ListItemText>
|
||||
@ -65,7 +67,7 @@ function QProcessSummaryResults({
|
||||
}
|
||||
<List>
|
||||
{
|
||||
processValues.processResults && processValues.processResults.map((processSummaryLine: ProcessSummaryLine, i: number) => (new ProcessSummaryLine(processSummaryLine).getProcessSummaryListItem(i, table, qInstance, true)))
|
||||
processValues.processResults && processValues.processResults.map((processSummaryLine: ProcessSummaryLine, i: number) => (new ProcessSummaryLine(processSummaryLine).getProcessSummaryListItem(i, sourceTable, qInstance, true)))
|
||||
}
|
||||
</List>
|
||||
</List>
|
||||
@ -79,8 +81,8 @@ function QProcessSummaryResults({
|
||||
<MDBox border="1px solid rgb(70%, 70%, 70%)" borderRadius="lg" p={2} mt={2}>
|
||||
<MDBox mt={-5} p={1} sx={{width: "fit-content"}} bgColor="success" borderRadius=".25em" width="initial" color="white">
|
||||
<MDBox display="flex" alignItems="center" color="white">
|
||||
<Icon fontSize="medium" sx={{mr: 1}}>{process.iconName}</Icon>
|
||||
{`${process.label} : ${step.label}`}
|
||||
{process.iconName && <Icon fontSize="medium" sx={{mr: 1}}>{process.iconName}</Icon>}
|
||||
Process Summary
|
||||
</MDBox>
|
||||
</MDBox>
|
||||
{resultValidationList}
|
||||
|
@ -64,6 +64,7 @@ function QValidationReview({
|
||||
}: Props): JSX.Element
|
||||
{
|
||||
const [previewRecordIndex, setPreviewRecordIndex] = useState(0);
|
||||
const sourceTable = qInstance.tables.get(processValues.sourceTable);
|
||||
|
||||
const updatePreviewRecordIndex = (offset: number) =>
|
||||
{
|
||||
@ -89,15 +90,44 @@ function QValidationReview({
|
||||
},
|
||||
});
|
||||
|
||||
const buildDoFullValidationRadioListItem = (value: "true" | "false", labelText: string, tooltipHTML: JSX.Element): JSX.Element =>
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// split up the label into words - then we'll display the last word by itself with a non-breaking space, no-wrap-glued to the button. //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
const labelWords = labelText.split(" ");
|
||||
const lastWord = labelWords[labelWords.length - 1];
|
||||
labelWords.splice(labelWords.length - 1, 1);
|
||||
|
||||
return (
|
||||
<ListItem sx={{pl: 2}}>
|
||||
<FormControlLabel
|
||||
value={value}
|
||||
control={<Radio />}
|
||||
label={(
|
||||
<ListItemText primaryTypographyProps={{fontSize: 16, pt: 0.625}}>
|
||||
{`${labelWords.join(" ")} `}
|
||||
<span style={{whiteSpace: "nowrap"}}>
|
||||
{/* eslint-disable-next-line react/jsx-one-expression-per-line */}
|
||||
{lastWord}. <CustomWidthTooltip title={tooltipHTML}>
|
||||
<IconButton sx={{py: 0}}><Icon fontSize="small">info_outlined</Icon></IconButton>
|
||||
{/* eslint-disable-next-line react/jsx-closing-tag-location */}
|
||||
</CustomWidthTooltip>
|
||||
</span>
|
||||
</ListItemText>
|
||||
)}
|
||||
/>
|
||||
</ListItem>
|
||||
);
|
||||
};
|
||||
|
||||
const preValidationList = (
|
||||
<List sx={{mt: 2}}>
|
||||
{
|
||||
processValues?.recordCount && table && (
|
||||
processValues?.recordCount !== undefined && sourceTable && (
|
||||
<ListItem sx={{my: 2}}>
|
||||
<ListItemText primaryTypographyProps={{fontSize: 16}}>
|
||||
You selected
|
||||
{` ${processValues.recordCount.toLocaleString()} ${table?.label} `}
|
||||
records.
|
||||
{`Input: ${processValues.recordCount.toLocaleString()} ${sourceTable?.label} record${processValues.recordCount === 1 ? "" : "s"}.`}
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
)
|
||||
@ -108,19 +138,13 @@ function QValidationReview({
|
||||
<ListItem sx={{mb: 1, mt: 6}}>
|
||||
<ListItemText primaryTypographyProps={{fontSize: 16}}>How would you like to proceed?</ListItemText>
|
||||
</ListItem>
|
||||
<List>
|
||||
<List className="doFullValidationRadios">
|
||||
<RadioGroup name="doFullValidation" value={formValues.doFullValidation} onChange={doFullValidationRadioChangedHandler}>
|
||||
<ListItem sx={{pl: 2}}>
|
||||
<FormControlLabel
|
||||
value="true"
|
||||
control={<Radio />}
|
||||
label={(
|
||||
<ListItemText primaryTypographyProps={{fontSize: 16}}>
|
||||
Perform Validation on all records before processing.
|
||||
<CustomWidthTooltip
|
||||
title={(
|
||||
{buildDoFullValidationRadioListItem(
|
||||
"true",
|
||||
"Perform Validation on all records before processing", (
|
||||
<div>
|
||||
If you choose this option, a Validation step will run on all of the records that you selected.
|
||||
If you choose this option, a Validation step will run on all of the input records.
|
||||
You will then be told how many can process successfully, and how many have issues.
|
||||
<br />
|
||||
<br />
|
||||
@ -129,38 +153,21 @@ function QValidationReview({
|
||||
<br />
|
||||
Choose this option if you want more information about what will happen, and you are willing to wait for that information.
|
||||
</div>
|
||||
),
|
||||
)}
|
||||
>
|
||||
<IconButton sx={{py: 0}}><Icon fontSize="small">info_outlined</Icon></IconButton>
|
||||
</CustomWidthTooltip>
|
||||
</ListItemText>
|
||||
)}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem sx={{pl: 2}}>
|
||||
<FormControlLabel
|
||||
value="false"
|
||||
control={<Radio />}
|
||||
label={(
|
||||
<ListItemText primaryTypographyProps={{fontSize: 16}}>
|
||||
Skip Validation. Submit the records for immediate processing.
|
||||
<CustomWidthTooltip
|
||||
title={(
|
||||
|
||||
{buildDoFullValidationRadioListItem(
|
||||
"false",
|
||||
"Skip Validation. Submit the records for immediate processing", (
|
||||
<div>
|
||||
If you choose this option, the records you selected will immediately be processed.
|
||||
If you choose this option, the records input records will immediately be processed.
|
||||
You will be told how many records were successfully processed, and which ones had issues after the processing is completed.
|
||||
<br />
|
||||
<br />
|
||||
Choose this option if you feel that you do not need this information, or are not willing to wait for it.
|
||||
</div>
|
||||
),
|
||||
)}
|
||||
>
|
||||
<IconButton sx={{py: 0}}><Icon fontSize="small">info_outlined</Icon></IconButton>
|
||||
</CustomWidthTooltip>
|
||||
</ListItemText>
|
||||
)}
|
||||
/>
|
||||
</ListItem>
|
||||
</RadioGroup>
|
||||
</List>
|
||||
</>
|
||||
@ -172,11 +179,11 @@ function QValidationReview({
|
||||
const postValidationList = (
|
||||
<List sx={{mt: 2}}>
|
||||
{
|
||||
processValues?.recordCount && table && (
|
||||
processValues?.recordCount !== undefined && sourceTable && (
|
||||
<ListItem sx={{my: 2}}>
|
||||
<ListItemText primaryTypographyProps={{fontSize: 16}}>
|
||||
Validation complete on
|
||||
{` ${processValues.recordCount.toLocaleString()} ${table?.label} `}
|
||||
{` ${processValues.recordCount.toLocaleString()} ${sourceTable?.label} `}
|
||||
records.
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
@ -184,7 +191,7 @@ function QValidationReview({
|
||||
}
|
||||
<List>
|
||||
{
|
||||
processValues.validationSummary && processValues.validationSummary.map((processSummaryLine: ProcessSummaryLine, i: number) => (new ProcessSummaryLine(processSummaryLine).getProcessSummaryListItem(i, table, qInstance)))
|
||||
processValues.validationSummary && processValues.validationSummary.map((processSummaryLine: ProcessSummaryLine, i: number) => (new ProcessSummaryLine(processSummaryLine).getProcessSummaryListItem(i, sourceTable, qInstance)))
|
||||
}
|
||||
</List>
|
||||
</List>
|
||||
@ -199,9 +206,9 @@ function QValidationReview({
|
||||
<MDTypography color="body" variant="body2" component="div" mb={2}>
|
||||
<MDBox display="flex">
|
||||
{
|
||||
previewRecords && previewRecords.length > 0 ? (
|
||||
processValues?.previewMessage && previewRecords && previewRecords.length > 0 ? (
|
||||
<>
|
||||
<i>This is a preview of the records that will be created.</i>
|
||||
<i>{processValues?.previewMessage}</i>
|
||||
<CustomWidthTooltip
|
||||
title={(
|
||||
<div>
|
||||
|
@ -21,7 +21,9 @@
|
||||
|
||||
import * as Yup from "yup";
|
||||
|
||||
import {CircularProgress, TablePagination} from "@mui/material";
|
||||
import {
|
||||
Button, CircularProgress, Icon, TablePagination,
|
||||
} from "@mui/material";
|
||||
import {DataGridPro, GridColDef} from "@mui/x-data-grid-pro";
|
||||
// formik components
|
||||
import {Form, Formik} from "formik";
|
||||
@ -96,8 +98,13 @@ function ProcessRun({process}: Props): JSX.Element
|
||||
const [lastProcessResponse, setLastProcessResponse] = useState(
|
||||
null as QJobStarted | QJobComplete | QJobError | QJobRunning,
|
||||
);
|
||||
const [showErrorDetail, setShowErrorDetail] = useState(false);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// the validation screen - it can change whether next is actually the final step or not... so, use this state field to track that. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
const [overrideOnLastStep, setOverrideOnLastStep] = useState(null as boolean);
|
||||
|
||||
const onLastStep = activeStepIndex === steps.length - 2;
|
||||
const noMoreSteps = activeStepIndex === steps.length - 1;
|
||||
|
||||
@ -199,6 +206,11 @@ function ProcessRun({process}: Props): JSX.Element
|
||||
return (<span>{value}</span>);
|
||||
};
|
||||
|
||||
const toggleShowErrorDetail = () =>
|
||||
{
|
||||
setShowErrorDetail(!showErrorDetail);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// generate the main form body content for a step //
|
||||
////////////////////////////////////////////////////
|
||||
@ -216,11 +228,24 @@ function ProcessRun({process}: Props): JSX.Element
|
||||
{
|
||||
return (
|
||||
<>
|
||||
<MDTypography color="error" variant="h5" component="div">
|
||||
<MDTypography color="error" variant="h3" component="div">
|
||||
Error
|
||||
</MDTypography>
|
||||
<MDTypography color="body" variant="button">
|
||||
An error occurred while running the process:
|
||||
{" "}
|
||||
{process.label}
|
||||
<MDBox mt={3} display="flex" justifyContent="center">
|
||||
<MDBox display="flex" flexDirection="column" alignItems="center">
|
||||
<Button onClick={toggleShowErrorDetail} startIcon={<Icon>{showErrorDetail ? "expand_less" : "expand_more"}</Icon>}>
|
||||
{showErrorDetail ? "Hide " : "Show "}
|
||||
detailed error message
|
||||
</Button>
|
||||
<MDBox mt={1} style={{display: showErrorDetail ? "block" : "none"}}>
|
||||
{processError}
|
||||
</MDBox>
|
||||
</MDBox>
|
||||
</MDBox>
|
||||
</MDTypography>
|
||||
</>
|
||||
);
|
||||
@ -326,8 +351,7 @@ function ProcessRun({process}: Props): JSX.Element
|
||||
//////////////////////////////////////////////////////////////
|
||||
setFieldValue("doFullValidation", value);
|
||||
|
||||
// eslint-disable-next-line no-unneeded-ternary
|
||||
setOverrideOnLastStep(value === "true" ? false : true);
|
||||
setOverrideOnLastStep(value !== "true");
|
||||
}}
|
||||
/>
|
||||
)
|
||||
@ -823,6 +847,7 @@ function ProcessRun({process}: Props): JSX.Element
|
||||
|
||||
setProcessValues({});
|
||||
setRecords([]);
|
||||
setOverrideOnLastStep(null);
|
||||
setLastProcessResponse(new QJobRunning({message: "Working..."}));
|
||||
|
||||
setTimeout(async () =>
|
||||
@ -848,7 +873,7 @@ function ProcessRun({process}: Props): JSX.Element
|
||||
|
||||
const mainCardStyles: any = {};
|
||||
mainCardStyles.minHeight = "calc(100vh - 400px)";
|
||||
if (qJobRunning || activeStep === null)
|
||||
if (!processError && (qJobRunning || activeStep === null))
|
||||
{
|
||||
mainCardStyles.background = "none";
|
||||
mainCardStyles.boxShadow = "none";
|
||||
|
@ -59,24 +59,37 @@ export class ProcessSummaryLine
|
||||
|
||||
getProcessSummaryListItem(i: number, table: QTableMetaData, qInstance: QInstance, isResultScreen: boolean = false): JSX.Element
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// split up the message into words - then we'll display the last word by itself with a non-breaking space, no-wrap-glued to the button. //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
const messageWords = this.message ? this.message.split(" ") : [];
|
||||
const lastWord = messageWords.length > 1 ? messageWords[messageWords.length - 1] : "";
|
||||
if (messageWords.length > 1)
|
||||
{
|
||||
messageWords.splice(messageWords.length - 1, 1);
|
||||
}
|
||||
|
||||
return (
|
||||
<ListItem key={i} sx={{pl: 4, my: 2}}>
|
||||
<MDBox display="flex" alignItems="top">
|
||||
<Icon fontSize="medium" sx={{mr: 1}} color={this.getColor()}>{this.getIcon(isResultScreen)}</Icon>
|
||||
<ListItemText primaryTypographyProps={{fontSize: 16}}>
|
||||
{this.count.toLocaleString()}
|
||||
{" "}
|
||||
{this.message}
|
||||
</ListItemText>
|
||||
{/* work hard to prevent the icon from falling down to the next line by itself... */}
|
||||
{`${this.count.toLocaleString()} ${messageWords.join(" ")} `}
|
||||
{
|
||||
table && this.primaryKeys && (
|
||||
<Link target="_blank" to={this.getLinkToRecords(table, qInstance)}>
|
||||
(table && this.primaryKeys) ? (
|
||||
<span style={{whiteSpace: "nowrap"}}>
|
||||
{/* eslint-disable-next-line react/jsx-one-expression-per-line */}
|
||||
{lastWord} <Link target="_blank" to={this.getLinkToRecords(table, qInstance)}>
|
||||
<Tooltip title="See these records in a new tab" sx={{py: 0}}>
|
||||
<IconButton sx={{py: 0}}><Icon fontSize="small">open_in_new</Icon></IconButton>
|
||||
</Tooltip>
|
||||
{/* eslint-disable-next-line react/jsx-closing-tag-location */}
|
||||
</Link>
|
||||
)
|
||||
</span>
|
||||
) : <span>{lastWord}</span>
|
||||
}
|
||||
</ListItemText>
|
||||
</MDBox>
|
||||
</ListItem>
|
||||
);
|
||||
@ -131,8 +144,6 @@ export class ProcessSummaryLine
|
||||
{
|
||||
const tablePath = qInstance.getTablePath(table);
|
||||
const filter = new QQueryFilter([new QFilterCriteria(table.primaryKeyField, QCriteriaOperator.IN, this.primaryKeys)]);
|
||||
console.log("Link to records:");
|
||||
console.log(filter);
|
||||
return (`${tablePath}?filter=${JSON.stringify(filter)}`);
|
||||
}
|
||||
}
|
||||
|
@ -110,3 +110,10 @@
|
||||
margin-left: 40px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* Help make the radio, text, and icon wrap in a good way */
|
||||
.doFullValidationRadios label
|
||||
{
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
Reference in New Issue
Block a user