Skip to content

Commit

Permalink
ORV2-2759 - FE: Staff Approve Application in the Queue (#1652)
Browse files Browse the repository at this point in the history
Co-authored-by: GlenAOT <[email protected]>
  • Loading branch information
glen-aot and glen-aot authored Oct 31, 2024
1 parent f54b0cc commit 99e2712
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 37 deletions.
5 changes: 5 additions & 0 deletions frontend/src/common/constants/validation_messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@
"beforeStart": {
"defaultMessage": "Expiry cannot be before Start Date"
}
},
"startOrExpiry": {
"past": {
"defaultMessage": "Start Date and/or Permit Expiry Date is in the past."
}
}
},
"email": {
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/common/helpers/validationMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ export const expiryMustBeAfterStart = () => {
return validationMessages.date.expiry.beforeStart.defaultMessage;
};

export const pastStartOrExpiryDate = () =>
validationMessages.date.startOrExpiry.past.defaultMessage;

export const invalidEmail = () => validationMessages.email.defaultMessage;

export const invalidPhoneLength = (min: number, max: number) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { useContext, useEffect, useState } from "react";
import { SnackBarContext } from "../../../../App";
import { OnRouteBCTableRowActions } from "../../../../common/components/table/OnRouteBCTableRowActions";
import { ApplicationInReviewModal } from "./ApplicationInReviewModal";
import { useNavigate } from "react-router-dom";
import { ERROR_ROUTES } from "../../../../routes/constants";
import {
useInvalidateApplicationsInQueue,
useUpdateApplicationInQueueStatus,
} from "../../../queue/hooks/hooks";
import { CASE_ACTIVITY_TYPES } from "../../../queue/types/CaseActivityType";
import { SnackBarContext } from "../../../../App";
import { ApplicationInReviewModal } from "./ApplicationInReviewModal";

const PERMIT_ACTION_OPTION_TYPES = {
WITHDRAW_APPLICATION: "withdrawApplication",
Expand Down Expand Up @@ -52,7 +50,6 @@ export const ApplicationsInReviewRowOptions = ({
isInReview: boolean;
permitId: string;
}) => {
const navigate = useNavigate();
const { invalidate } = useInvalidateApplicationsInQueue();

const [isAIRModalOpen, setIsAIRModalOpen] = useState<boolean>(false);
Expand All @@ -65,20 +62,17 @@ export const ApplicationsInReviewRowOptions = ({
const {
mutateAsync: updateApplication,
data: updateApplicationResponse,
isError: isUpdateApplicationError,
error: updateApplicationError,
} = useUpdateApplicationInQueueStatus();

const updateApplicationErrorStatus = updateApplicationError?.response?.status;

useEffect(() => {
if (isUpdateApplicationError) {
if (updateApplicationErrorStatus === 422) {
// if the application has already been withdrawn by another user
if (updateApplicationError.response?.status === 422) {
return setIsAIRModalOpen(true);
}
// handle all other errors
navigate(ERROR_ROUTES.UNEXPECTED);
return setIsAIRModalOpen(true);
}
}, [isUpdateApplicationError, updateApplicationError]);
}, [updateApplicationErrorStatus]);

const isSuccess = (status?: number) => status === 201;
const { setSnackBar } = useContext(SnackBarContext);
Expand Down
34 changes: 28 additions & 6 deletions frontend/src/features/permits/helpers/dateSelection.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { Dayjs } from "dayjs";
import dayjs, { Dayjs } from "dayjs";

import { BASE_DAYS_IN_YEAR, TERM_DURATION_INTERVAL_DAYS } from "../constants/constants";
import {
BASE_DAYS_IN_YEAR,
TERM_DURATION_INTERVAL_DAYS,
} from "../constants/constants";
import { PERMIT_TYPES, PermitType } from "../types/PermitType";
import { getExpiryDate } from "./permitState";
import { getMostRecentExpiryFromLOAs } from "./permitLOA";
Expand Down Expand Up @@ -57,7 +60,7 @@ export const maxDurationForPermitType = (permitType: PermitType) => {
* @param permitType Permit type to get duration interval for
* @returns Number of days as duration interval for the permit type.
*/
export const getDurationIntervalDays = (permitType: PermitType) => {
export const getDurationIntervalDays = (permitType: PermitType) => {
switch (permitType) {
case PERMIT_TYPES.TROW:
return TROW_DURATION_INTERVAL_DAYS;
Expand Down Expand Up @@ -100,8 +103,10 @@ export const getAvailableDurationOptions = (
const mostRecentLOAExpiry = getMostRecentExpiryFromLOAs(selectedLOAs);
if (!mostRecentLOAExpiry) return fullDurationOptions;

return fullDurationOptions
.filter(({ value: durationDays }) => !mostRecentLOAExpiry.isBefore(getExpiryDate(startDate, durationDays)));
return fullDurationOptions.filter(
({ value: durationDays }) =>
!mostRecentLOAExpiry.isBefore(getExpiryDate(startDate, durationDays)),
);
};

/**
Expand All @@ -121,7 +126,9 @@ export const handleUpdateDurationIfNeeded = (
}[],
) => {
const minAllowableDuration = minDurationForPermitType(permitType);
const maxDurationInOptions = Math.max(...durationOptions.map(durationOption => durationOption.value));
const maxDurationInOptions = Math.max(
...durationOptions.map((durationOption) => durationOption.value),
);

if (currentDuration > maxDurationInOptions) {
if (maxDurationInOptions < minAllowableDuration) {
Expand All @@ -132,3 +139,18 @@ export const handleUpdateDurationIfNeeded = (

return currentDuration;
};

/**
* Determine if start date or expiry date of permit applicationare in the past
* @param startDate Start date of the permit
* @param expiryDate Expiry date of the permit
* @returns True if either startDate or expiryDate are in the past
*/
export const isPermitStartOrExpiryDateInPast = (
startDate: Dayjs,
expiryDate: Dayjs,
) => {
return (
dayjs().isAfter(startDate, "day") || dayjs().isAfter(expiryDate, "day")
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { ReviewFeeSummary } from "./ReviewFeeSummary";
import { ReviewPermitDetails } from "./ReviewPermitDetails";
import { ReviewPermitLOAs } from "./ReviewPermitLOAs";
import { ReviewVehicleInfo } from "./ReviewVehicleInfo";
import { isPermitStartOrExpiryDateInPast } from "../../../../helpers/dateSelection";

interface PermitReviewProps {
reviewContext: PermitReviewContext;
Expand Down Expand Up @@ -60,6 +61,14 @@ interface PermitReviewProps {
}

export const PermitReview = (props: PermitReviewProps) => {
const invalidPermitDates =
props.permitStartDate && props.permitExpiryDate
? isPermitStartOrExpiryDateInPast(
props.permitStartDate,
props.permitExpiryDate,
)
: false;

return (
<Box className="permit-review layout-box">
<Box className="permit-review__container">
Expand Down Expand Up @@ -94,6 +103,7 @@ export const PermitReview = (props: PermitReviewProps) => {
showChangedFields={props.showChangedFields}
oldStartDate={props.oldFields?.permitData?.startDate}
oldDuration={props.oldFields?.permitData?.permitDuration}
showDateErrorBanner={invalidPermitDates}
/>

<ReviewVehicleInfo
Expand Down Expand Up @@ -123,11 +133,12 @@ export const PermitReview = (props: PermitReviewProps) => {
onContinue={props.onContinue}
hasToCartButton={props.reviewContext === PERMIT_REVIEW_CONTEXTS.APPLY}
onAddToCart={props.onAddToCart}
disableApproveAndRejectButtons={
props.updateApplicationMutationPending
}
handleApproveButton={props.handleApproveButton}
handleRejectButton={props.handleRejectButton}
disableApproveButton={
props.updateApplicationMutationPending || invalidPermitDates
}
disableRejectButton={props.updateApplicationMutationPending}
/>
</Box>
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,21 @@ export const ReviewActions = ({
onContinue,
hasToCartButton,
onAddToCart,
disableApproveAndRejectButtons,
handleApproveButton,
handleRejectButton,
disableApproveButton,
disableRejectButton,
}: {
reviewContext: PermitReviewContext;
onEdit: () => void;
continueBtnText?: string;
onContinue?: () => Promise<void>;
hasToCartButton: boolean;
onAddToCart?: () => Promise<void>;
disableApproveAndRejectButtons?: boolean;
handleApproveButton?: () => Promise<void>;
handleRejectButton?: () => void;
disableApproveButton?: boolean;
disableRejectButton?: boolean;
}) => {
return (
<Box className="review-actions">
Expand Down Expand Up @@ -88,7 +90,7 @@ export const ReviewActions = ({
color="error"
data-testid="reject-btn"
onClick={handleRejectButton}
disabled={disableApproveAndRejectButtons}
disabled={disableRejectButton}
>
Reject
</Button>
Expand All @@ -100,7 +102,7 @@ export const ReviewActions = ({
color="primary"
data-testid="approve-btn"
onClick={handleApproveButton}
disabled={disableApproveAndRejectButtons}
disabled={disableApproveButton}
>
Approve
</Button>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@use "../../../../../../themes/orbcStyles";
@import "../../../../../../themes/orbcStyles";

@include orbcStyles.permit-main-box-style(".review-permit-details");
@include orbcStyles.permit-left-box-style(".review-permit-details__header");
Expand All @@ -18,6 +19,16 @@
.permit-expiry-banner {
width: 90%;
}

.permit-error-banner {
padding-top: 1.5rem;

.bc-gov-alertbanner {
background-color: $bc-messages-red-background;
color: $bc-messages-red-text;
margin-bottom: 0;
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import { Box, Typography } from "@mui/material";
import { Dayjs } from "dayjs";

import "./ReviewPermitDetails.scss";
import { ErrorBcGovBanner } from "../../../../../../common/components/banners/ErrorBcGovBanner";
import { PermitExpiryDateBanner } from "../../../../../../common/components/banners/PermitExpiryDateBanner";
import { ReviewConditionsTable } from "./ReviewConditionsTable";
import { DiffChip } from "./DiffChip";
import { Nullable } from "../../../../../../common/types/common";
import { PermitCondition } from "../../../../types/PermitCondition";
import { BASE_DAYS_IN_YEAR } from "../../../../constants/constants";
import { applyWhenNotNullable } from "../../../../../../common/helpers/util";
import { areValuesDifferent } from "../../../../../../common/helpers/equality";
import {
DATE_FORMATS,
dayjsToLocalStr,
} from "../../../../../../common/helpers/formatDate";
import { applyWhenNotNullable } from "../../../../../../common/helpers/util";
import { Nullable } from "../../../../../../common/types/common";
import { BASE_DAYS_IN_YEAR } from "../../../../constants/constants";
import { PermitCondition } from "../../../../types/PermitCondition";
import { DiffChip } from "./DiffChip";
import { ReviewConditionsTable } from "./ReviewConditionsTable";
import "./ReviewPermitDetails.scss";
import { pastStartOrExpiryDate } from "../../../../../../common/helpers/validationMessages";

export const ReviewPermitDetails = ({
startDate,
Expand All @@ -23,6 +24,7 @@ export const ReviewPermitDetails = ({
showChangedFields = false,
oldStartDate,
oldDuration,
showDateErrorBanner,
}: {
startDate?: Nullable<Dayjs>;
permitDuration?: Nullable<number>;
Expand All @@ -31,6 +33,7 @@ export const ReviewPermitDetails = ({
showChangedFields?: boolean;
oldStartDate?: Nullable<Dayjs>;
oldDuration?: Nullable<number>;
showDateErrorBanner?: Nullable<boolean>;
}) => {
const changedFields = showChangedFields
? {
Expand Down Expand Up @@ -87,7 +90,8 @@ export const ReviewPermitDetails = ({
data-testid="permit-duration"
>
{applyWhenNotNullable(
(duration) => duration === BASE_DAYS_IN_YEAR ? "1 Year" : `${duration} Days`,
(duration) =>
duration === BASE_DAYS_IN_YEAR ? "1 Year" : `${duration} Days`,
permitDuration,
"",
)}
Expand All @@ -102,6 +106,12 @@ export const ReviewPermitDetails = ({
)}
/>
</Box>

{showDateErrorBanner && (
<Box className="permit-error-banner">
<ErrorBcGovBanner msg={pastStartOrExpiryDate()} />
</Box>
)}
<Box className="permit-conditions">
<Typography variant="h3">
Selected commodities and their respective CVSE forms.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ import { Application } from "../../permits/types/application";
import { PERMIT_REVIEW_CONTEXTS } from "../../permits/types/PermitReviewContext";
import { DEFAULT_PERMIT_TYPE } from "../../permits/types/PermitType";
import { useFetchSpecialAuthorizations } from "../../settings/hooks/specialAuthorizations";
import { useUpdateApplicationInQueueStatus } from "../hooks/hooks";
import { CASE_ACTIVITY_TYPES } from "../types/CaseActivityType";
import "./ApplicationInQueueReview.scss";
import { QueueBreadcrumb } from "./QueueBreadcrumb";
import { RejectApplicationModal } from "./RejectApplicationModal";
import { useUpdateApplicationInQueueStatus } from "../hooks/hooks";

export const ApplicationInQueueReview = ({
applicationData,
Expand Down Expand Up @@ -150,6 +150,14 @@ export const ApplicationInQueueReview = ({
isPending={updateApplicationMutationPending}
/>
)}
{showRejectApplicationModal && (
<RejectApplicationModal
showModal={showRejectApplicationModal}
onCancel={() => setShowRejectApplicationModal(false)}
onConfirm={handleReject}
isPending={updateApplicationMutationPending}
/>
)}
</div>
);
};
11 changes: 10 additions & 1 deletion frontend/src/features/queue/hooks/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import {
} from "../apiManager/queueAPI";
import { canViewApplicationQueue } from "../helpers/canViewApplicationQueue";
import { CaseActivityType } from "../types/CaseActivityType";
import { useNavigate } from "react-router-dom";
import { ERROR_ROUTES } from "../../../routes/constants";

const QUEUE_QUERY_KEYS_BASE = "queue";

Expand Down Expand Up @@ -160,6 +162,7 @@ export const useClaimApplicationInQueueMutation = () => {

export const useUpdateApplicationInQueueStatus = () => {
const { invalidate } = useInvalidateApplicationsInQueue();
const navigate = useNavigate();

return useMutation({
mutationFn: (data: {
Expand All @@ -173,7 +176,13 @@ export const useUpdateApplicationInQueueStatus = () => {
onSuccess: () => {
invalidate();
},
onError: (err: AxiosError) => err,
onError: (err: AxiosError) => {
if (err.response?.status === 422) {
return err;
} else {
navigate(ERROR_ROUTES.UNEXPECTED);
}
},
});
};

Expand Down

0 comments on commit 99e2712

Please sign in to comment.