QQQ-37 checkpoint - cleanup of various kinds on process validation & result

This commit is contained in:
2022-08-30 13:47:26 -05:00
parent 8b2364b596
commit 3eb1cd73a7
5 changed files with 138 additions and 86 deletions

View File

@ -48,15 +48,17 @@ function QProcessSummaryResults({
qInstance, process, table = null, processValues, step, qInstance, process, table = null, processValues, step,
}: Props): JSX.Element }: Props): JSX.Element
{ {
const sourceTable = qInstance.tables.get(processValues.sourceTable);
const resultValidationList = ( const resultValidationList = (
<List sx={{mt: 2}}> <List sx={{mt: 2}}>
{ {
processValues?.recordCount && table && ( processValues?.recordCount !== undefined && sourceTable && (
<ListItem sx={{my: 2}}> <ListItem sx={{my: 2}}>
<ListItemText primaryTypographyProps={{fontSize: 16}}> <ListItemText primaryTypographyProps={{fontSize: 16}}>
{processValues.recordCount.toLocaleString()} {processValues.recordCount.toLocaleString()}
{" "} {" "}
{table.label} {sourceTable.label}
{" "} {" "}
records were processed. records were processed.
</ListItemText> </ListItemText>
@ -65,7 +67,7 @@ function QProcessSummaryResults({
} }
<List> <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>
</List> </List>
@ -79,8 +81,8 @@ function QProcessSummaryResults({
<MDBox border="1px solid rgb(70%, 70%, 70%)" borderRadius="lg" p={2} mt={2}> <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 mt={-5} p={1} sx={{width: "fit-content"}} bgColor="success" borderRadius=".25em" width="initial" color="white">
<MDBox display="flex" alignItems="center" color="white"> <MDBox display="flex" alignItems="center" color="white">
<Icon fontSize="medium" sx={{mr: 1}}>{process.iconName}</Icon> {process.iconName && <Icon fontSize="medium" sx={{mr: 1}}>{process.iconName}</Icon>}
{`${process.label} : ${step.label}`} Process Summary
</MDBox> </MDBox>
</MDBox> </MDBox>
{resultValidationList} {resultValidationList}

View File

@ -64,6 +64,7 @@ function QValidationReview({
}: Props): JSX.Element }: Props): JSX.Element
{ {
const [previewRecordIndex, setPreviewRecordIndex] = useState(0); const [previewRecordIndex, setPreviewRecordIndex] = useState(0);
const sourceTable = qInstance.tables.get(processValues.sourceTable);
const updatePreviewRecordIndex = (offset: number) => 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}.&nbsp;<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 = ( const preValidationList = (
<List sx={{mt: 2}}> <List sx={{mt: 2}}>
{ {
processValues?.recordCount && table && ( processValues?.recordCount !== undefined && sourceTable && (
<ListItem sx={{my: 2}}> <ListItem sx={{my: 2}}>
<ListItemText primaryTypographyProps={{fontSize: 16}}> <ListItemText primaryTypographyProps={{fontSize: 16}}>
You selected {`Input: ${processValues.recordCount.toLocaleString()} ${sourceTable?.label} record${processValues.recordCount === 1 ? "" : "s"}.`}
{` ${processValues.recordCount.toLocaleString()} ${table?.label} `}
records.
</ListItemText> </ListItemText>
</ListItem> </ListItem>
) )
@ -108,59 +138,36 @@ function QValidationReview({
<ListItem sx={{mb: 1, mt: 6}}> <ListItem sx={{mb: 1, mt: 6}}>
<ListItemText primaryTypographyProps={{fontSize: 16}}>How would you like to proceed?</ListItemText> <ListItemText primaryTypographyProps={{fontSize: 16}}>How would you like to proceed?</ListItemText>
</ListItem> </ListItem>
<List> <List className="doFullValidationRadios">
<RadioGroup name="doFullValidation" value={formValues.doFullValidation} onChange={doFullValidationRadioChangedHandler}> <RadioGroup name="doFullValidation" value={formValues.doFullValidation} onChange={doFullValidationRadioChangedHandler}>
<ListItem sx={{pl: 2}}> {buildDoFullValidationRadioListItem(
<FormControlLabel "true",
value="true" "Perform Validation on all records before processing", (
control={<Radio />} <div>
label={( If you choose this option, a Validation step will run on all of the input records.
<ListItemText primaryTypographyProps={{fontSize: 16}}> You will then be told how many can process successfully, and how many have issues.
Perform Validation on all records before processing. <br />
<CustomWidthTooltip <br />
title={( Running this validation may take several minutes, depending on the complexity of the work, and the number of records.
<div> <br />
If you choose this option, a Validation step will run on all of the records that you selected. <br />
You will then be told how many can process successfully, and how many have issues. Choose this option if you want more information about what will happen, and you are willing to wait for that information.
<br /> </div>
<br /> ),
Running this validation may take several minutes, depending on the complexity of the work, and the number of records. )}
<br />
<br /> {buildDoFullValidationRadioListItem(
Choose this option if you want more information about what will happen, and you are willing to wait for that information. "false",
</div> "Skip Validation. Submit the records for immediate processing", (
)} <div>
> If you choose this option, the records input records will immediately be processed.
<IconButton sx={{py: 0}}><Icon fontSize="small">info_outlined</Icon></IconButton> You will be told how many records were successfully processed, and which ones had issues after the processing is completed.
</CustomWidthTooltip> <br />
</ListItemText> <br />
)} Choose this option if you feel that you do not need this information, or are not willing to wait for it.
/> </div>
</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={(
<div>
If you choose this option, the records you selected 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> </RadioGroup>
</List> </List>
</> </>
@ -172,11 +179,11 @@ function QValidationReview({
const postValidationList = ( const postValidationList = (
<List sx={{mt: 2}}> <List sx={{mt: 2}}>
{ {
processValues?.recordCount && table && ( processValues?.recordCount !== undefined && sourceTable && (
<ListItem sx={{my: 2}}> <ListItem sx={{my: 2}}>
<ListItemText primaryTypographyProps={{fontSize: 16}}> <ListItemText primaryTypographyProps={{fontSize: 16}}>
Validation complete on Validation complete on
{` ${processValues.recordCount.toLocaleString()} ${table?.label} `} {` ${processValues.recordCount.toLocaleString()} ${sourceTable?.label} `}
records. records.
</ListItemText> </ListItemText>
</ListItem> </ListItem>
@ -184,7 +191,7 @@ function QValidationReview({
} }
<List> <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>
</List> </List>
@ -199,9 +206,9 @@ function QValidationReview({
<MDTypography color="body" variant="body2" component="div" mb={2}> <MDTypography color="body" variant="body2" component="div" mb={2}>
<MDBox display="flex"> <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 <CustomWidthTooltip
title={( title={(
<div> <div>

View File

@ -21,7 +21,9 @@
import * as Yup from "yup"; 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"; import {DataGridPro, GridColDef} from "@mui/x-data-grid-pro";
// formik components // formik components
import {Form, Formik} from "formik"; import {Form, Formik} from "formik";
@ -96,8 +98,13 @@ function ProcessRun({process}: Props): JSX.Element
const [lastProcessResponse, setLastProcessResponse] = useState( const [lastProcessResponse, setLastProcessResponse] = useState(
null as QJobStarted | QJobComplete | QJobError | QJobRunning, 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 [overrideOnLastStep, setOverrideOnLastStep] = useState(null as boolean);
const onLastStep = activeStepIndex === steps.length - 2; const onLastStep = activeStepIndex === steps.length - 2;
const noMoreSteps = activeStepIndex === steps.length - 1; const noMoreSteps = activeStepIndex === steps.length - 1;
@ -199,6 +206,11 @@ function ProcessRun({process}: Props): JSX.Element
return (<span>{value}</span>); return (<span>{value}</span>);
}; };
const toggleShowErrorDetail = () =>
{
setShowErrorDetail(!showErrorDetail);
};
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
// generate the main form body content for a step // // generate the main form body content for a step //
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
@ -216,11 +228,24 @@ function ProcessRun({process}: Props): JSX.Element
{ {
return ( return (
<> <>
<MDTypography color="error" variant="h5" component="div"> <MDTypography color="error" variant="h3" component="div">
Error Error
</MDTypography> </MDTypography>
<MDTypography color="body" variant="button"> <MDTypography color="body" variant="button">
{processError} 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> </MDTypography>
</> </>
); );
@ -326,8 +351,7 @@ function ProcessRun({process}: Props): JSX.Element
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////
setFieldValue("doFullValidation", value); setFieldValue("doFullValidation", value);
// eslint-disable-next-line no-unneeded-ternary setOverrideOnLastStep(value !== "true");
setOverrideOnLastStep(value === "true" ? false : true);
}} }}
/> />
) )
@ -823,6 +847,7 @@ function ProcessRun({process}: Props): JSX.Element
setProcessValues({}); setProcessValues({});
setRecords([]); setRecords([]);
setOverrideOnLastStep(null);
setLastProcessResponse(new QJobRunning({message: "Working..."})); setLastProcessResponse(new QJobRunning({message: "Working..."}));
setTimeout(async () => setTimeout(async () =>
@ -848,7 +873,7 @@ function ProcessRun({process}: Props): JSX.Element
const mainCardStyles: any = {}; const mainCardStyles: any = {};
mainCardStyles.minHeight = "calc(100vh - 400px)"; mainCardStyles.minHeight = "calc(100vh - 400px)";
if (qJobRunning || activeStep === null) if (!processError && (qJobRunning || activeStep === null))
{ {
mainCardStyles.background = "none"; mainCardStyles.background = "none";
mainCardStyles.boxShadow = "none"; mainCardStyles.boxShadow = "none";

View File

@ -59,24 +59,37 @@ export class ProcessSummaryLine
getProcessSummaryListItem(i: number, table: QTableMetaData, qInstance: QInstance, isResultScreen: boolean = false): JSX.Element 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 ( return (
<ListItem key={i} sx={{pl: 4, my: 2}}> <ListItem key={i} sx={{pl: 4, my: 2}}>
<MDBox display="flex" alignItems="top"> <MDBox display="flex" alignItems="top">
<Icon fontSize="medium" sx={{mr: 1}} color={this.getColor()}>{this.getIcon(isResultScreen)}</Icon> <Icon fontSize="medium" sx={{mr: 1}} color={this.getColor()}>{this.getIcon(isResultScreen)}</Icon>
<ListItemText primaryTypographyProps={{fontSize: 16}}> <ListItemText primaryTypographyProps={{fontSize: 16}}>
{this.count.toLocaleString()} {/* work hard to prevent the icon from falling down to the next line by itself... */}
{" "} {`${this.count.toLocaleString()} ${messageWords.join(" ")} `}
{this.message} {
(table && this.primaryKeys) ? (
<span style={{whiteSpace: "nowrap"}}>
{/* eslint-disable-next-line react/jsx-one-expression-per-line */}
{lastWord}&nbsp;<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> </ListItemText>
{
table && this.primaryKeys && (
<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>
</Link>
)
}
</MDBox> </MDBox>
</ListItem> </ListItem>
); );
@ -131,8 +144,6 @@ export class ProcessSummaryLine
{ {
const tablePath = qInstance.getTablePath(table); const tablePath = qInstance.getTablePath(table);
const filter = new QQueryFilter([new QFilterCriteria(table.primaryKeyField, QCriteriaOperator.IN, this.primaryKeys)]); 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)}`); return (`${tablePath}?filter=${JSON.stringify(filter)}`);
} }
} }

View File

@ -110,3 +110,10 @@
margin-left: 40px; margin-left: 40px;
font-size: 14px; font-size: 14px;
} }
/* Help make the radio, text, and icon wrap in a good way */
.doFullValidationRadios label
{
display: flex;
align-items: flex-start;
}