Skip to content

Commit

Permalink
ORV2-2338 - Applying and amending permits using LOAs (#1637)
Browse files Browse the repository at this point in the history
Co-authored-by: gchauhan-aot <[email protected]>
  • Loading branch information
zgong-gov and gchauhan-aot authored Oct 16, 2024
1 parent 8872a39 commit c43068a
Show file tree
Hide file tree
Showing 70 changed files with 2,400 additions and 565 deletions.
2 changes: 1 addition & 1 deletion database/mssql/test/versions/v_43_1_test.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
SET NOCOUNT ON

SELECT COUNT(*) FROM $(DB_NAME).[dops].[ORBC_DOCUMENT_TEMPLATE]
WHERE TEMPLATE_NAME IN ('PERMIT','PERMIT_STOS_VOID','PERMIT_STOS_REVOKED')
WHERE TEMPLATE_NAME IN ('PERMIT','PERMIT_STOS_VOID','PERMIT_STOS_REVOKED')
2 changes: 1 addition & 1 deletion database/mssql/test/versions/v_43_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ if [[ $TEST_43_2_RESULT -eq 3 ]]; then
echo "Test 43.2 passed: STOS templates setup successfully in ORBC_DOCUMENT"
else
echo "******** Test 43.2 failed: Failed to setup STOS permit templates"
fi
fi
2 changes: 1 addition & 1 deletion database/mssql/test/versions/v_45_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ if [[ $TEST_45_1_RESULT -eq 7 ]]; then
echo "Test 45.1 passed: GL_PROJ_CODE column created in ORBC_PAYMENT_METHOD_TYPE"
else
echo "******** Test 45.1 failed: GL_PROJ_CODE column missing in ORBC_PAYMENT_METHOD_TYPE"
fi
fi
9 changes: 9 additions & 0 deletions frontend/src/common/components/form/CountryAndProvince.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ interface CountryAndProvinceProps {
isProvinceRequired?: boolean;
countryClassName?: string;
provinceClassName?: string;
readOnly?: boolean;
disabled?: boolean;
}

/**
Expand All @@ -62,6 +64,8 @@ export const CountryAndProvince = <T extends ORBC_FormTypes>({
isProvinceRequired = true,
countryClassName,
provinceClassName,
disabled,
readOnly,
}: CountryAndProvinceProps): JSX.Element => {
const { resetField, watch, setValue } = useFormContext();

Expand Down Expand Up @@ -175,7 +179,10 @@ export const CountryAndProvince = <T extends ORBC_FormTypes>({
</MenuItem>
))}
className={countryClassName}
disabled={disabled}
readOnly={readOnly}
/>

{shouldDisplayProvince && (
<CustomFormComponent
type="select"
Expand All @@ -192,6 +199,8 @@ export const CountryAndProvince = <T extends ORBC_FormTypes>({
</MenuItem>
))}
className={provinceClassName}
disabled={disabled}
readOnly={readOnly}
/>
)}
</>
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/common/constants/bannerMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ export const BANNER_MESSAGES = {
"Only vehicles in the Vehicle Inventory can be designated to LOA(s).",
SELECT_VEHICLES_LOA_INFO:
"If you do not see the vehicle(s) you wish to designate here, please make sure you add them to the client's Vehicle Inventory first and come back to this page.",
FIND_LOA_DETAILS:
"To find details about the LOA go to the Special Authorizations page.",
LOA_VEHICLE_CANNOT_BE_EDITED_IN_PERMIT:
"Vehicle details cannot be edited in the permit application if you are using an LOA.",
REJECTED_APPLICATIONS:
"Rejected applications appear in Applications in Progress.",
};
24 changes: 24 additions & 0 deletions frontend/src/common/helpers/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,3 +274,27 @@ export const setRedirectInSession = (redirectUri: string) => {
}
}
};

/**
* Determine whether or not two arrays have the same items.
* @param arr1 First array
* @param arr2 Second array
* @returns Whether or not the two arrays contain the same items
*/
export const areArraysEqual = <T extends (number | string)>(
arr1: T[],
arr2: T[],
) => {
const set1 = new Set(arr1);
const set2 = new Set(arr2);

for (const val of set1) {
if (!set2.has(val)) return false;
}

for (const val of set2) {
if (!set1.has(val)) return false;
}

return true;
};
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ export const getDefaultApplication = () => {
vehicleDetails,
commodities: conditions,
mailingAddress,
loas: [],
},
};
};
82 changes: 82 additions & 0 deletions frontend/src/features/permits/context/ApplicationFormContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { createContext } from "react";
import { Dayjs } from "dayjs";

import { PermitVehicleDetails } from "../types/PermitVehicleDetails";
import { LOADetail } from "../../settings/types/SpecialAuthorization";
import { ApplicationFormData } from "../types/application";
import { getDefaultValues } from "../helpers/getDefaultApplicationFormData";
import { DEFAULT_PERMIT_TYPE } from "../types/PermitType";
import { PermitCondition } from "../types/PermitCondition";
import { PowerUnit, Trailer, VehicleSubType } from "../../manageVehicles/types/Vehicle";
import { Nullable } from "../../../common/types/common";
import { CompanyProfile } from "../../manageProfile/types/manageProfile.d";
import {
PAST_START_DATE_STATUSES,
PastStartDateStatus,
} from "../../../common/components/form/subFormComponents/CustomDatePicker";

interface ApplicationFormContextType {
initialFormData: ApplicationFormData;
formData: ApplicationFormData;
durationOptions: {
value: number;
label: string;
}[];
vehicleOptions: (PowerUnit | Trailer)[];
powerUnitSubtypes: VehicleSubType[];
trailerSubtypes: VehicleSubType[];
isLcvDesignated: boolean;
feature: string;
companyInfo?: Nullable<CompanyProfile>;
isAmendAction: boolean;
createdDateTime?: Nullable<Dayjs>;
updatedDateTime?: Nullable<Dayjs>;
pastStartDateStatus: PastStartDateStatus;
companyLOAs: LOADetail[];
revisionHistory: {
permitId: number;
name: string;
revisionDateTime: string;
comment: string;
}[];
onLeave?: () => void;
onSave?: () => Promise<void>;
onCancel?: () => void;
onContinue: () => Promise<void>;
onSetDuration: (duration: number) => void;
onSetExpiryDate: (expiry: Dayjs) => void;
onSetConditions: (conditions: PermitCondition[]) => void;
onToggleSaveVehicle: (saveVehicle: boolean) => void;
onSetVehicle: (vehicleDetails: PermitVehicleDetails) => void;
onClearVehicle: (saveVehicle: boolean) => void;
onUpdateLOAs: (updatedLOAs: LOADetail[]) => void;
}

export const ApplicationFormContext = createContext<ApplicationFormContextType>({
initialFormData: getDefaultValues(DEFAULT_PERMIT_TYPE, undefined),
formData: getDefaultValues(DEFAULT_PERMIT_TYPE, undefined),
durationOptions: [],
vehicleOptions: [],
powerUnitSubtypes: [],
trailerSubtypes: [],
isLcvDesignated: false,
feature: "",
companyInfo: undefined,
isAmendAction: false,
createdDateTime: undefined,
updatedDateTime: undefined,
pastStartDateStatus: PAST_START_DATE_STATUSES.ALLOWED,
companyLOAs: [],
revisionHistory: [],
onLeave: undefined,
onSave: undefined,
onCancel: undefined,
onContinue: async () => undefined,
onSetDuration: () => undefined,
onSetExpiryDate: () => undefined,
onSetConditions: () => undefined,
onToggleSaveVehicle: () => undefined,
onSetVehicle: () => undefined,
onClearVehicle: () => undefined,
onUpdateLOAs: () => undefined,
});
70 changes: 70 additions & 0 deletions frontend/src/features/permits/helpers/dateSelection.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Dayjs } from "dayjs";

import { BASE_DAYS_IN_YEAR, TERM_DURATION_INTERVAL_DAYS } from "../constants/constants";
import { PERMIT_TYPES, PermitType } from "../types/PermitType";
import {
Expand All @@ -13,6 +15,9 @@ import {
TROW_DURATION_INTERVAL_DAYS,
TROW_DURATION_OPTIONS,
} from "../constants/trow";
import { getExpiryDate } from "./permitState";
import { LOADetail } from "../../settings/types/SpecialAuthorization";
import { getMostRecentExpiryFromLOAs } from "./permitLOA";

/**
* Get list of selectable duration options for a given permit type.
Expand Down Expand Up @@ -62,3 +67,68 @@ export const getDurationIntervalDays = (permitType: PermitType) => {
return TERM_DURATION_INTERVAL_DAYS; // This needs to be updated once more permit types are added
}
};

/**
* Get the minimum permit expiry date.
* @param permitType Permit type
* @param startDate Expected start date of the permit
* @returns The earliest date that the permit will expire on
*/
export const getMinPermitExpiryDate = (
permitType: PermitType,
startDate: Dayjs,
) => {
const minDuration = minDurationForPermitType(permitType);
return getExpiryDate(startDate, minDuration);
};

/**
* Get available duration options for a permit based on selected LOAs and start date.
* @param fullDurationOptions Full duration select options for a permit
* @param selectedLOAs Selected LOAs for a permit
* @param startDate Start date for a permit
* @returns Updated available duration select options
*/
export const getAvailableDurationOptions = (
fullDurationOptions: {
value: number;
label: string;
}[],
selectedLOAs: LOADetail[],
startDate: Dayjs,
) => {
const mostRecentLOAExpiry = getMostRecentExpiryFromLOAs(selectedLOAs);
if (!mostRecentLOAExpiry) return fullDurationOptions;

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

/**
* Update permit duration if durations options change.
* Selected duration must be between min allowable permit duration and max available duration in the options.
* @param permitType Permit type
* @param currentDuration Currently selected duration for the permit
* @param durationOptions Available list of selectable duration options for the permit
* @returns Current permit duration if valid, or updated duration if no longer valid
*/
export const handleUpdateDurationIfNeeded = (
permitType: PermitType,
currentDuration: number,
durationOptions: {
value: number;
label: string;
}[],
) => {
const minAllowableDuration = minDurationForPermitType(permitType);
const maxDurationInOptions = Math.max(...durationOptions.map(durationOption => durationOption.value));

if (currentDuration > maxDurationInOptions) {
if (maxDurationInOptions < minAllowableDuration) {
return minAllowableDuration;
}
return maxDurationInOptions;
}

return currentDuration;
};
41 changes: 41 additions & 0 deletions frontend/src/features/permits/helpers/equality.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { PermitMailingAddress } from "../types/PermitMailingAddress";
import { PermitContactDetails } from "../types/PermitContactDetails";
import { PermitVehicleDetails } from "../types/PermitVehicleDetails";
import { PermitData } from "../types/PermitData";
import { areLOADetailsEqual, LOADetail } from "../../settings/types/SpecialAuthorization";
import { PermitCondition } from "../types/PermitCondition";
import {
DATE_FORMATS,
Expand Down Expand Up @@ -113,6 +114,45 @@ const areVehicleDetailsEqual = (
.reduce((prevIsEqual, currIsEqual) => prevIsEqual && currIsEqual, true);
};

/**
* Compare whether or not the LOAs for two permits are equal.
* @param loas1 LOAs for first permit
* @param loas2 LOAs for second permit
* @returns true when the selected LOAs are the same, false otherwise
*/
export const arePermitLOAsEqual = (
loas1: Nullable<LOADetail[]>,
loas2: Nullable<LOADetail[]>,
) => {
const isLoas1Empty = !loas1 || loas1.length === 0;
const isLoas2Empty = !loas2 || loas2.length === 0;

if (isLoas1Empty && isLoas2Empty) return true;
if ((isLoas1Empty && !isLoas2Empty) || (!isLoas1Empty && isLoas2Empty))
return false;

const loaMap1 = new Map(
(loas1 as LOADetail[]).map((loa) => [loa.loaNumber, loa]),
);
const loaMap2 = new Map(
(loas2 as LOADetail[]).map((loa) => [loa.loaNumber, loa]),
);

for (const [loaNumber, loa] of loaMap1) {
if (!areLOADetailsEqual(loa, loaMap2.get(loaNumber))) {
return false;
}
}

for (const [loaNumber, loa] of loaMap2) {
if (!areLOADetailsEqual(loa, loaMap1.get(loaNumber))) {
return false;
}
}

return true;
};

/**
* Compare whether or not two application data info are equal.
* @param data1 first application data info
Expand All @@ -133,6 +173,7 @@ export const areApplicationDataEqual = (
areVehicleDetailsEqual(data1.vehicleDetails, data2.vehicleDetails) &&
areConditionsEqual(data1.commodities, data2.commodities) &&
areMailingAddressesEqual(data1.mailingAddress, data2.mailingAddress) &&
arePermitLOAsEqual(data1.loas, data2.loas) &&
((!data1.companyName && !data2.companyName) ||
data1.companyName === data2.companyName) &&
((!data1.doingBusinessAs && !data2.doingBusinessAs) ||
Expand Down
Loading

0 comments on commit c43068a

Please sign in to comment.