From db95e2c023e378a5f3d7ef228c06702142e34145 Mon Sep 17 00:00:00 2001 From: dbarkowsky Date: Wed, 12 Jul 2023 14:00:06 -0700 Subject: [PATCH] Warning message if request is incomplete --- api/controllers/requests-api-controller.ts | 2 +- api/helpers/checkForCompleteRequest.ts | 8 ++- .../components/custom/forms/RequestForm.tsx | 62 +++++++++++++++-- .../components/custom/modals/DeletePrompt.tsx | 2 +- .../custom/modals/GeneralPrompt.tsx | 68 +++++++++++++++++++ app/src/pages/IndividualRequest.tsx | 2 +- 6 files changed, 135 insertions(+), 9 deletions(-) create mode 100644 app/src/components/custom/modals/GeneralPrompt.tsx diff --git a/api/controllers/requests-api-controller.ts b/api/controllers/requests-api-controller.ts index 6874a25..8fcbd6e 100644 --- a/api/controllers/requests-api-controller.ts +++ b/api/controllers/requests-api-controller.ts @@ -259,7 +259,7 @@ export const updateRequestState = async (req: Request, res: Response) => { if (refinedState === RequestStates.INCOMPLETE || refinedState === RequestStates.COMPLETE) { // Get user with matching IDIR const users: IDIRUser[] = await getIDIRUser(existingRequest.idir); - if (users) { + if (users.length > 0) { sendChangeNotification(users.at(0).email, `${FRONTEND_URL}/request/${id}`); } } diff --git a/api/helpers/checkForCompleteRequest.ts b/api/helpers/checkForCompleteRequest.ts index e975067..097747d 100644 --- a/api/helpers/checkForCompleteRequest.ts +++ b/api/helpers/checkForCompleteRequest.ts @@ -24,14 +24,18 @@ export const checkForCompleteRequest = (requestData: RequestUpdateData) => { if (!requestData.employeeId) return false; // Do all purchases have a file? - const purchasesHaveFiles = requestData.purchases.every((purchase: Purchase) => purchase.fileObj); + const purchasesHaveFiles = requestData.purchases.every( + (purchase: Purchase) => purchase.fileObj && !purchase.fileObj.removed, + ); if (!purchasesHaveFiles) return false; // Does at least one approval exist? if (requestData.approvals.length === 0) return false; // Does each approval have a file? - const approvalsHaveFiles = requestData.approvals.every((approval: Approval) => approval.fileObj); + const approvalsHaveFiles = requestData.approvals.every( + (approval: Approval) => approval.fileObj && !approval.fileObj.removed, + ); if (!approvalsHaveFiles) return false; return true; diff --git a/app/src/components/custom/forms/RequestForm.tsx b/app/src/components/custom/forms/RequestForm.tsx index 6f65d7d..2391947 100644 --- a/app/src/components/custom/forms/RequestForm.tsx +++ b/app/src/components/custom/forms/RequestForm.tsx @@ -33,6 +33,7 @@ import BackButton from '../../bcgov/BackButton'; import DeletePrompt from '../modals/DeletePrompt'; import { ErrorContext, errorStyles } from '../notifications/ErrorWrapper'; import { getAllFiles } from '../../../helpers/fileDownloadAll'; +import GeneralPrompt from '../modals/GeneralPrompt'; /** * @interface @@ -137,11 +138,45 @@ const RequestForm = (props: RequestFormProps) => { } }; - const openDialog = () => { - const dialog: HTMLDialogElement = document.querySelector('#deletePrompt')!; + // Opens dialog elements based on id + const openDialog = (id: string) => { + const dialog: HTMLDialogElement = document.querySelector(`#${id}`)!; dialog.showModal(); }; + // Checks if few required fields are populated + const allRequiredInfoIsHere = () => + reimbursementRequest?.employeeId && approvals && approvals.length !== 0; + + // Checks if all elements have files. + const allFilesAdded = () => { + // Are both types of lists here? + if (!approvals || !purchases) return false; + + // Remove any temporary or removed files + const actualApprovalFiles = approvalFiles.filter( + (file) => file.source !== 'temp' && !file.removed, + ); + const actualPurchaseFiles = purchaseFiles.filter( + (file) => file.source !== 'temp' && !file.removed, + ); + + // Do lists have all file objects? + const allApprovalsHaveFiles = approvals.length === actualApprovalFiles.length; + const allPurchasesHaveFiles = purchases.length === actualPurchaseFiles.length; + + return allApprovalsHaveFiles && allPurchasesHaveFiles; + }; + + // When the update button is selected. + const onUpdate = () => { + if (!isAdmin && !(allRequiredInfoIsHere() && allFilesAdded())) { + openDialog('incompletePrompt'); + } else { + handleUpdate(); + } + }; + const theme = useTheme(); const matches = useMediaQuery(theme.breakpoints.up('sm')); const navigate = useNavigate(); @@ -155,6 +190,7 @@ const RequestForm = (props: RequestFormProps) => { return ( <> + {/* Modals Exist Here */} { ]} id='deletePrompt' /> + + {/* Start Actual Form */} { Update @@ -263,7 +311,13 @@ const RequestForm = (props: RequestFormProps) => { > View User's Requests - Delete Request + { + openDialog('deletePrompt'); + }} + > + Delete Request + ) : ( diff --git a/app/src/components/custom/modals/DeletePrompt.tsx b/app/src/components/custom/modals/DeletePrompt.tsx index aa1155d..b46127a 100644 --- a/app/src/components/custom/modals/DeletePrompt.tsx +++ b/app/src/components/custom/modals/DeletePrompt.tsx @@ -7,7 +7,7 @@ import { modalStyles } from './modalStyles'; * @description Properties passed to the DeletePrompt component. * @property {() => void} deleteHander - The action taken when delete is confirmed. * @property {string} title - The title of the modal popup. - * @property {string} blurb - The text in the body of the modal. If multiple paragraphs are required, separate the paragraphs with ";;". + * @property {string[]} blurb - The text in the body of the modal. * @property {string} id - The id assigned to this modal. */ interface DeletePromptProps { diff --git a/app/src/components/custom/modals/GeneralPrompt.tsx b/app/src/components/custom/modals/GeneralPrompt.tsx new file mode 100644 index 0000000..a7ec5ff --- /dev/null +++ b/app/src/components/custom/modals/GeneralPrompt.tsx @@ -0,0 +1,68 @@ +import ActionButton from '../../bcgov/ActionButton'; +import { buttonStyles } from '../../bcgov/ButtonStyles'; +import { modalStyles } from './modalStyles'; + +/** + * @interface + * @description Properties passed to the GeneralPrompt component. + * @property {() => void} handler - The action taken when prompt is confirmed. + * @property {string} title - The title of the modal popup. + * @property {string[]} blurb - The text in the body of the modal. + * @property {string} id - The id assigned to this modal. + */ +interface GeneralPromptProps { + handler: () => void; + title: string; + blurb: string[]; + id: string; +} + +/** + * @description A modal element that can be used to confirm actions. + * @param {GeneralPromptProps} props The properties passed to the component. + * @returns A React component. + */ +const GeneralPrompt = (props: GeneralPromptProps) => { + const { handler, title, blurb, id } = props; + return ( + +

+ {title} +

+ {blurb.map((paragraph, index) => { + const key = `blurb${title}${index}`; + return ( +

+ {paragraph} +

+ ); + })} +
+ { + const thisDialog: HTMLDialogElement = document.querySelector(`#${id}`)!; + thisDialog.close(); + }} + > + Cancel + + + Confirm + +
+
+ ); +}; + +export default GeneralPrompt; diff --git a/app/src/pages/IndividualRequest.tsx b/app/src/pages/IndividualRequest.tsx index 62b0805..9fdd618 100644 --- a/app/src/pages/IndividualRequest.tsx +++ b/app/src/pages/IndividualRequest.tsx @@ -51,7 +51,7 @@ const IndividualRequest = () => { deleted: false, downloaded: false, removed: false, - source: '', + source: 'temp', }; // Retrieves a single request's info