@mychoice/mychoice-sdk-modules 2.2.27 → 2.2.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.js +118 -61
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/shared/boxes/SwitchButtonBox/interface.d.ts +1 -1
- package/dist/esm/index.js +120 -63
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/shared/boxes/SwitchButtonBox/interface.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/package.json +4 -4
|
@@ -2,7 +2,7 @@ import { SelectOptionInterface, OnChangeEventInterface } from '@mychoice/mychoic
|
|
|
2
2
|
import { LabelFormBoxProps } from '../LabelFormBox/interface';
|
|
3
3
|
export type SwitchButtonBoxProps = LabelFormBoxProps & {
|
|
4
4
|
name: string;
|
|
5
|
-
defaultValue: string;
|
|
5
|
+
defaultValue: string | boolean;
|
|
6
6
|
items: SelectOptionInterface[];
|
|
7
7
|
onChange: (option: OnChangeEventInterface) => void;
|
|
8
8
|
errorMessage?: string;
|
package/dist/esm/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import { ErrorBoundary } from 'react-error-boundary';
|
|
3
|
-
import { AppTypes, isMyChoiceLike, TitleForm, TooltipForm, IconDelete, ColorVariablesTypes, InputTypes, InputForm, InputFormPhone, InputFormEmail, InputFormLicence, SelectForm, getFormattedDate, DateTypes, getDateOptions, ButtonForm, defaultBrokerPhone, DriverPriorityTypes, SizeTypes, ButtonBase, CategoryTypes, ColorTypes, defaultLocalIndex, liabilityOptions, accidentBenefitsOptionalOptions, coverageOptions, lossOfUseOptions, liabilityForDamageOptions, limitedWaiverOfDepreciationOptions, OfferDetail, CompanyRoleTypes, OperationHoursInfo, OfferHeader, OfferPrice, ButtonTypes, ButtonVariantTypes, IconDropdownArrow, InsuranceTypes, HeaderNavGroup, ButtonMenu, DeviceTypes, IconLoaderPrimary, getInsuranceType, RequestStatusTypes, formatPostalCode, IconLoaderSecondary, postalCodeFormat, useEffectOnce, carCondition, carStatus, carWinterTiresCheck, carKeepPlace, carUsagePurpose, firstDriveDistanceList, yearlyDriveDistanceList, numberWithCommas, VehiclePrimaryUseTypes, businessUsePercentOptions, addDaysToDate, comprehensiveCoverage, collisionCoverage, maritalStatusOptions, subYearsFromDate, occupationOptions, genderOptions, applicantRelationshipOptions, addYearsToDate, checkDateIsSpecial, DriverLicenceTypes, getDifferenceInYears, compareDates, yesNoOptions, getLicenceTypeOptions, getDisabledLicenceTypes,
|
|
3
|
+
import { AppTypes, isMyChoiceLike, TitleForm, TooltipForm, IconDelete, ColorVariablesTypes, InputTypes, InputForm, InputFormPhone, InputFormEmail, InputFormLicence, SelectForm, getFormattedDate, DateTypes, getDateOptions, ButtonForm, defaultBrokerPhone, DriverPriorityTypes, SizeTypes, ButtonBase, CategoryTypes, ColorTypes, defaultLocalIndex, liabilityOptions, accidentBenefitsOptionalOptions, coverageOptions, lossOfUseOptions, liabilityForDamageOptions, limitedWaiverOfDepreciationOptions, OfferDetail, CompanyRoleTypes, OperationHoursInfo, OfferHeader, OfferPrice, ButtonTypes, ButtonVariantTypes, IconDropdownArrow, InsuranceTypes, HeaderNavGroup, ButtonMenu, DeviceTypes, IconLoaderPrimary, getInsuranceType, RequestStatusTypes, formatPostalCode, IconLoaderSecondary, postalCodeFormat, useEffectOnce, carCondition, carStatus, carWinterTiresCheck, carKeepPlace, carUsagePurpose, firstDriveDistanceList, yearlyDriveDistanceList, numberWithCommas, VehiclePrimaryUseTypes, businessUsePercentOptions, addDaysToDate, comprehensiveCoverage, collisionCoverage, maritalStatusOptions, subYearsFromDate, occupationOptions, genderOptions, applicantRelationshipOptions, addYearsToDate, checkDateIsSpecial, DriverLicenceTypes, getDifferenceInYears, getDifferenceInMonths, compareDates, yesNoOptions, getLicenceTypeOptions, getDisabledLicenceTypes, addMonthsToDate, getMinDate, getMinDateByYears, CheckboxForm, insuranceCancellationReasonOptions, subMonthsFromDate, DriverListTypes, licenceSuspensionsReasonOptions, trafficTicketsGroupOptions, ValidationStatusTypes, getPlaceholderEmail, QuoteEdit, homeOwnerTypeOptions, tenantBuildingStructureOptions, condoBuildingStructureOptions, homeBuildingStructureOptions, residentsOptions, insuredYearsOptions, getInsuranceYearsOptions, ApplicantListTypes, claimTypeOptions, exteriorFinishOptions, constructionTypeOptions, garageTypeOptions, getNumericOptions, fireHallDistanceOptions, fireHydrantDistanceOptions, primaryHeatingTypeOptions, auxHeatingTypeOptions, InfoMessage, ppOptions, homeCoverageOptions, homeLiabilityOptions, lifeInsuranceTypeOptions, lifeInsuranceOntarioTypeOptions, lifeCoverageOptions, smokerOptions, MychoiceStep, Step, IconClock, ButtonArrow, DirectionTypes, ButtonTabArrow, ButtonTab, ButtonTabMobile, NavigationCar, NavigationPerson, NavigationDiscount, NavigationQuotes, NavigationAddress, NavigationProperty, ModalTypes } from '@mychoice/mychoice-sdk-components';
|
|
4
4
|
import { useStoreAppConfig, useStoreFormCarVehicle, useStoreFormCarDriverBase, useStoreFormCarDiscount, StoreFormCarDiscountActionTypes, useStoreFormCarQuote, CarQuoteDataHandler, useValidationCarDiscount, StoreFormCarVehicleActionTypes, useStoreFormHomeDiscount, useStorePartner, useStoreDeviceType, useStoreAppLoader, useStoreAppModal, StoreConfigAppModalActionTypes, useHandlerPostal, useHandlerCarMake, useHandlerCarModel, useProvince, useAbReform, useValidationVehicle, useAutofillCarForm, StoreFormCarDriverBaseActionTypes, useStoreFormCarConfig, useStoreFormCarDriverInfo, StoreFormCarDriverInfoActionTypes, useStoreFormCarDriverLicence, addDayToDate, StoreFormCarDriverLicenceActionTypes, useStoreFormCarDriverInsurance, StoreFormCarDriverInsuranceActionTypes, useStoreFormCarDriverCancellation, StoreFormCarDriverCancellationActionTypes, useStoreFormCarDriverSuspension, StoreFormCarDriverSuspensionActionTypes, useStoreFormCarDriverAccident, StoreFormCarDriverAccidentActionTypes, useStoreFormCarDriverTicket, StoreFormCarDriverTicketActionTypes, useValidationDriver, useHandlerCarQuoterEmail, useHandlerCarQuoterPhone, useStoreFormHomeDwelling, useStoreFormHomePostal, useStoreFormHomeApplicantBase, StoreFormHomePostalActionTypes, StoreFormHomeDwellingActionTypes, StoreConfigAppConfigActionTypes, StoreFormHomeApplicantBaseActionTypes, useValidationAddress, useStoreFormHomeApplicantInfo, StoreFormHomeDiscountActionTypes, StoreFormHomeApplicantInfoActionTypes, useStoreFormHomeApplicantInsurance, StoreFormHomeApplicantInsuranceActionTypes, useStoreFormHomeApplicantCancellation, StoreFormHomeApplicantCancellationActionTypes, useStoreFormHomeApplicantClaim, StoreFormHomeApplicantClaimActionTypes, useValidationApplicant, useValidationDwelling, useStoreFormHomeQuote, HomeQuoteDataHandler, useValidationHomeDiscount, useHandlerHomeQuoterEmail, useHandlerHomeQuoterPhone, useStoreFormLifeCoverage, StoreFormLifeCoverageActionTypes, useValidationCoverage, useAutofillLifeForm, useStoreFormLifeQuote, useStoreFormLifeApplicant, LifeQuoteDataHandler, useValidationLifeApplicant, useHandlerLifeQuoterEmail, useHandlerLifeQuoterPhone, StoreFormLifeApplicantActionTypes, useStoreFormCarPostal, useStoreFormLifePostal, ClearFormDataHandler, useStoreAppDevice, useStoreClient, useHandlerAuth, useHandlerPartner, StoreConfigAppLoaderActionTypes, StoreFormCarConfigActionTypes, StoreFormCarPostalActionTypes, StoreFormCarQuoteActionTypes, StoreFormHomeQuoteActionTypes, StoreFormLifePostalActionTypes, StoreFormLifeQuoteActionTypes, StoreClientActionTypes, StoreConfigAppDeviceActionTypes, StoreProvider, initHttpResponse } from '@mychoice/mychoice-sdk-store';
|
|
5
5
|
import { useNavigate, useLocation, BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
|
|
6
|
-
import React, { useState,
|
|
6
|
+
import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react';
|
|
7
7
|
import { random } from 'lodash';
|
|
8
8
|
import GooglePlacesAutocomplete, { geocodeByPlaceId } from 'react-google-places-autocomplete';
|
|
9
9
|
import { components } from 'react-select';
|
|
@@ -108,10 +108,14 @@ DateSelectFormBox.defaultProps = {
|
|
|
108
108
|
|
|
109
109
|
const SwitchButtonBox = (props) => {
|
|
110
110
|
const { name, title, description, hintMessage, isRemovable, errorMessage, className, defaultValue, items, onChange } = props;
|
|
111
|
+
// Allow boolean true/false answers; empty string means unanswered (neither Yes nor No).
|
|
111
112
|
const [activeId, setActiveId] = useState(defaultValue);
|
|
112
113
|
const { appConfigState: { appType }, } = useStoreAppConfig();
|
|
113
114
|
const isTheBig = appType === AppTypes.TheBig;
|
|
114
115
|
const isMychoice = isMyChoiceLike(appType);
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
setActiveId(defaultValue);
|
|
118
|
+
}, [defaultValue]);
|
|
115
119
|
const setClassNames = (isActive) => {
|
|
116
120
|
if (isTheBig) {
|
|
117
121
|
return isActive ? 'thebig-bold' : 'thebig-regular';
|
|
@@ -121,6 +125,7 @@ const SwitchButtonBox = (props) => {
|
|
|
121
125
|
}
|
|
122
126
|
return '';
|
|
123
127
|
};
|
|
128
|
+
const isAnswered = activeId === true || activeId === false;
|
|
124
129
|
const handleClick = useCallback((selectedItem) => () => {
|
|
125
130
|
setActiveId(selectedItem.value);
|
|
126
131
|
if (onChange) {
|
|
@@ -129,8 +134,11 @@ const SwitchButtonBox = (props) => {
|
|
|
129
134
|
value: selectedItem.value,
|
|
130
135
|
});
|
|
131
136
|
}
|
|
132
|
-
}, []);
|
|
133
|
-
return (jsx(Container, { name: name, className: className, title: title, description: description, errorMessage: errorMessage, isRemovable: isRemovable, hintMessage: hintMessage, children: jsx("div", { className: "flex-items-container", children: items.map((item, index) =>
|
|
137
|
+
}, [name, onChange]);
|
|
138
|
+
return (jsx(Container, { name: name, className: className, title: title, description: description, errorMessage: errorMessage, isRemovable: isRemovable, hintMessage: hintMessage, children: jsx("div", { className: "flex-items-container", children: items.map((item, index) => {
|
|
139
|
+
const isSelected = isAnswered && item.value === activeId;
|
|
140
|
+
return (jsx(ButtonForm, { className: setClassNames(isSelected), onClick: handleClick(item), selected: isSelected, label: item.name }, `${item.name}_${index}`));
|
|
141
|
+
}) }) }));
|
|
134
142
|
};
|
|
135
143
|
SwitchButtonBox.defaultProps = {
|
|
136
144
|
defaultValue: '',
|
|
@@ -199,18 +207,41 @@ const capitalize = (text) => text.charAt(0).toUpperCase() + text.slice(1);
|
|
|
199
207
|
const BlockVehLinks = () => {
|
|
200
208
|
const { vehicleState: { items: vehicles } } = useStoreFormCarVehicle();
|
|
201
209
|
const { driverState: { items: drivers } } = useStoreFormCarDriverBase();
|
|
202
|
-
const { discountState: { vehlinks, occVehlinks }, dispatchDiscountState } = useStoreFormCarDiscount();
|
|
210
|
+
const { discountState: { vehlinks, occVehlinks, inValidation }, dispatchDiscountState } = useStoreFormCarDiscount();
|
|
211
|
+
// Spec: 2 drivers / 2 vehicles — ask only for Vehicle1; system assigns the other driver as PO on Vehicle2.
|
|
212
|
+
const isTwoByTwo = drivers.length === 2 && vehicles.length === 2;
|
|
213
|
+
const assignPrimary = (driverIndex, vehicleIndex) => {
|
|
214
|
+
dispatchDiscountState({
|
|
215
|
+
type: StoreFormCarDiscountActionTypes.FormCarDiscountVehlinkSelect,
|
|
216
|
+
payload: { driverIndex, vehicleIndex },
|
|
217
|
+
});
|
|
218
|
+
};
|
|
219
|
+
useEffect(() => {
|
|
220
|
+
if (!isTwoByTwo)
|
|
221
|
+
return;
|
|
222
|
+
const v1 = vehlinks.find((l) => l.vehicleIndex === 0 && l.priority === DriverPriorityTypes.Prn);
|
|
223
|
+
if (!v1 || v1.driverIndex < 0)
|
|
224
|
+
return;
|
|
225
|
+
const otherDriverIndex = drivers.findIndex((_, index) => index !== v1.driverIndex);
|
|
226
|
+
if (otherDriverIndex < 0)
|
|
227
|
+
return;
|
|
228
|
+
const v2 = vehlinks.find((l) => l.vehicleIndex === 1 && l.priority === DriverPriorityTypes.Prn);
|
|
229
|
+
if (v2?.driverIndex === otherDriverIndex)
|
|
230
|
+
return;
|
|
231
|
+
assignPrimary(otherDriverIndex, 1);
|
|
232
|
+
}, [isTwoByTwo, vehlinks, drivers]);
|
|
203
233
|
if (drivers.length <= 1) {
|
|
204
234
|
return null;
|
|
205
235
|
}
|
|
206
236
|
const handlePrimaryDriverChange = (vehicleIndex) => ({ value }) => {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
237
|
+
const driverIndex = Number(value);
|
|
238
|
+
assignPrimary(driverIndex, vehicleIndex);
|
|
239
|
+
if (isTwoByTwo && vehicleIndex === 0) {
|
|
240
|
+
const otherDriverIndex = drivers.findIndex((_, index) => index !== driverIndex);
|
|
241
|
+
if (otherDriverIndex >= 0) {
|
|
242
|
+
assignPrimary(otherDriverIndex, 1);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
214
245
|
};
|
|
215
246
|
const handleOccDriverChange = (driverIndex) => ({ value }) => {
|
|
216
247
|
dispatchDiscountState({
|
|
@@ -223,30 +254,38 @@ const BlockVehLinks = () => {
|
|
|
223
254
|
};
|
|
224
255
|
// Drivers that are NOT assigned as primary for any vehicle
|
|
225
256
|
const primaryDriverIndices = vehlinks
|
|
226
|
-
.filter((l) => l.priority === DriverPriorityTypes.Prn)
|
|
257
|
+
.filter((l) => l.priority === DriverPriorityTypes.Prn && l.driverIndex >= 0)
|
|
227
258
|
.map((l) => l.driverIndex);
|
|
228
259
|
const unassignedDrivers = drivers
|
|
229
260
|
.map((driver, index) => ({ driver, index }))
|
|
230
261
|
.filter(({ index }) => !primaryDriverIndices.includes(index));
|
|
231
|
-
|
|
262
|
+
const vehiclesToAsk = isTwoByTwo ? vehicles.slice(0, 1) : vehicles;
|
|
263
|
+
return (jsxs(Fragment, { children: [jsx(LabelFormBox, { title: "Since there are multiple drivers in this quote, please tell us who drives which vehicle the most. Please provide an answer that most accurately describes your situation." }), vehiclesToAsk.map((vehicle, vehicleIndex) => {
|
|
232
264
|
const { year, make, model } = vehicle;
|
|
233
265
|
const selectedPrimary = vehlinks.find((l) => l.vehicleIndex === vehicleIndex && l.priority === DriverPriorityTypes.Prn);
|
|
234
|
-
const
|
|
266
|
+
const isPrimaryAnswered = selectedPrimary !== undefined && selectedPrimary.driverIndex >= 0;
|
|
267
|
+
// Show all drivers so the user can freely reselect / re-order primaries.
|
|
268
|
+
const primaryOptions = drivers
|
|
235
269
|
.map((driver, driverIndex) => ({ name: driver.firstName, value: driverIndex }));
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
270
|
+
const isDuplicatePrimary = !isTwoByTwo
|
|
271
|
+
&& isPrimaryAnswered
|
|
272
|
+
&& drivers.length >= vehicles.length
|
|
273
|
+
&& vehlinks.some((l) => l.vehicleIndex !== vehicleIndex
|
|
274
|
+
&& l.priority === DriverPriorityTypes.Prn
|
|
275
|
+
&& l.driverIndex === selectedPrimary?.driverIndex);
|
|
276
|
+
return (jsx(SelectFormBox, { title: `Who is the principal driver of the ${year} ${make} ${model}?`, onChange: handlePrimaryDriverChange(vehicleIndex), options: primaryOptions, defaultValue: isPrimaryAnswered ? selectedPrimary.driverIndex : '', name: `vehlink-${vehicleIndex}`, placeholder: "Select from the list", autoSelectIfValueIsOutOfOptions: false, error: (!isPrimaryAnswered && inValidation) || isDuplicatePrimary, errorMessage: isDuplicatePrimary
|
|
277
|
+
? 'This driver is already the primary driver of another vehicle. Each vehicle should have a different primary driver.'
|
|
278
|
+
: '' }, `vehlink-${vehicleIndex}`));
|
|
279
|
+
}), drivers.length > vehicles.length && vehicles.length > 1
|
|
280
|
+
&& unassignedDrivers.map(({ driver, index: driverIndex }) => {
|
|
281
|
+
const selectedVehicle = occVehlinks.find((l) => l.driverIndex === driverIndex);
|
|
282
|
+
const vehicleOptions = vehicles.map((vehicle, vehicleIndex) => ({
|
|
283
|
+
name: `${vehicle.year} ${vehicle.make} ${vehicle.model}`,
|
|
284
|
+
value: vehicleIndex,
|
|
285
|
+
}));
|
|
286
|
+
const isOccAnswered = selectedVehicle !== undefined && selectedVehicle.vehicleIndex >= 0;
|
|
287
|
+
return (jsx(SelectFormBox, { title: `Which vehicle does ${driver.firstName} drive the most?`, onChange: handleOccDriverChange(driverIndex), options: vehicleOptions, defaultValue: isOccAnswered ? selectedVehicle.vehicleIndex : '', name: `occ-vehlink-${driverIndex}`, placeholder: "Select from the list", autoSelectIfValueIsOutOfOptions: false, error: !isOccAnswered && inValidation }, `occ-vehlink-${driverIndex}`));
|
|
288
|
+
})] }));
|
|
250
289
|
};
|
|
251
290
|
|
|
252
291
|
const BlockNextPageInfo$2 = () => {
|
|
@@ -883,7 +922,7 @@ const BlockCarConditionInfo = () => {
|
|
|
883
922
|
type: StoreFormCarVehicleActionTypes.FormCarDailyDistanceSelect,
|
|
884
923
|
payload: { distanceDaily: value },
|
|
885
924
|
});
|
|
886
|
-
const yearlyDefault = `${getRecommendedRange(value, primaryUse === VehiclePrimaryUseTypes.Business ? distanceBusiness :
|
|
925
|
+
const yearlyDefault = `${getRecommendedRange(value, primaryUse === VehiclePrimaryUseTypes.Business ? distanceBusiness : undefined)}`;
|
|
887
926
|
dispatchVehicleState({
|
|
888
927
|
type: StoreFormCarVehicleActionTypes.FormCarYearlyDistanceSelect,
|
|
889
928
|
payload: { distanceYearly: yearlyDefault },
|
|
@@ -906,7 +945,7 @@ const BlockCarConditionInfo = () => {
|
|
|
906
945
|
purchaseMonth || '',
|
|
907
946
|
purchaseYear || '',
|
|
908
947
|
], vehicleState.inValidation), error: vehicleState.inValidation, minDate: year ? `${Number(year) - 1}-01-01` : getFormattedDate('', 'yyyy-MM-dd'), maxDate: getFormattedDate('', 'yyyy-MM-dd') }), !(isAlbertaProvince && isTheBig) && (jsx(SwitchButtonBox, { items: carWinterTiresCheck, onChange: handleWinterTiresChange, name: "winterTires", defaultValue: getSelectedOption(carWinterTiresCheck, winterTires), description: "Snow. Ice. Freezing temperatures. Winter tires protect you from all these winter weather conditions, and more. It also protects your wallet. Investing in winter tires can save you $50 to $100 on your insurance.", title: "Does your car have winter tires?" })), jsx(SelectFormBox, { options: carKeepPlace, name: "parkingLocation", onChange: handleCarParkingLocationChange, defaultValue: getSelectedOption(carKeepPlace, parkingLocation), title: "Where do you keep your car overnight?", placeholder: "Select from the list", description: "For many of us, the answer is \u201Cat home.\u201D If you park your car in various locations throughout the year, then select the most frequently parking spot. Be honest\u2014some insurers will use your overnight parking location to calculate a quote, which could affect your claims in the future.", autoSelectIfValueIsOutOfOptions: false, error: !parkingLocation && vehicleState.inValidation, errorMessage: getErrorMessage(parkingLocation, vehicleState.inValidation) }), jsx(SwitchButtonBox, { items: carUsagePurpose, onChange: handleCarUsageChange, name: "primaryUse", defaultValue: getSelectedOption(carUsagePurpose, primaryUse) || carUsagePurpose[0].value, hintMessage: primaryUseHintMessage, description: "The way you use your vehicle is a primary factor that insurers use in your policy terms and fees, whether it be personal or business.\n \u2022 Personal is driving from work or school to home.\n \u2022 Business considers other uses like sales calls, pick-ups or deliveries, or other business errands.", title: "What do you use your car for?" }), jsx(SelectFormBox, { options: firstDriveDistanceList, name: "distanceDaily", onChange: handleCarDailyDistanceChange, defaultValue: getSelectedOption(firstDriveDistanceList, distanceDaily), title: "How far is your 1-way commute to work or school?", placeholder: "Select", description: "The distance you drive to work or school would be an example of your daily commute. It is one of the most important factors when determining coverage options since it exposes you to traffic each day. The shorter your commute, the less risk you carry of an accident.", autoSelectIfValueIsOutOfOptions: false, error: distanceDaily === undefined && vehicleState.inValidation, errorMessage: getErrorMessage(distanceDaily, vehicleState.inValidation) }), jsx(SelectFormBox, { options: yearlyDriveDistanceList, name: "distanceYearly", onChange: handleCarYearlyDistanceChange, defaultValue: getSelectedOption(yearlyDriveDistanceList, distanceYearly), title: "How many total kilometers are driven each year?", placeholder: "Select", hintMessage: distanceDaily !== undefined ? `Based on your daily driven kilometers, we recommend
|
|
909
|
-
${numberWithCommas(getRecommendedRange(distanceDaily, primaryUse === VehiclePrimaryUseTypes.Business ? distanceBusiness :
|
|
948
|
+
${numberWithCommas(getRecommendedRange(distanceDaily, primaryUse === VehiclePrimaryUseTypes.Business ? distanceBusiness : undefined))} for your yearly driven kilometers` : '', autoSelectIfValueIsOutOfOptions: false, error: !distanceYearly && vehicleState.inValidation, errorMessage: getErrorMessage(distanceYearly, vehicleState.inValidation) }), primaryUse === 'business' && (jsx(SelectFormBox, { options: businessUsePercentOptions, name: "businessUsePercent", onChange: handleBusinessUsePercentChange, defaultValue: getSelectedOption(businessUsePercentOptions, businessUsePercent), title: "What percentage of your total kilometers are driven for business purposes?", placeholder: "Select", autoSelectIfValueIsOutOfOptions: false, error: !businessUsePercent && vehicleState.inValidation, errorMessage: getErrorMessage(businessUsePercent, vehicleState.inValidation) })), showPolicyStartOnVehiclePage && (jsx(DateSelectFormBox, { name: "policyStart", dateNames: ['policyStartYear', 'policyStartMonth', 'policyStartDay'], onDateChange: handlePolicyStartDateChange, defaultValue: defaultPolicyStartDate, title: "What is the ideal start date for your new insurance policy?", description: "Select your preferred date for the beginning of your new insurance policy. For instance, you may set the start date for the day that your current insurance expires to ensure that you're continuously covered. Alternatively, select today's date for a quote or new policy.", minDate: addDaysToDate('', 1), maxDate: addDaysToDate('', 90), isDay: true }))] }));
|
|
910
949
|
};
|
|
911
950
|
|
|
912
951
|
const VehicleSectionMain = () => {
|
|
@@ -937,7 +976,7 @@ const AccidentBenefitsModal = ({ selectedCoverages, onApply, onCancel, selectAll
|
|
|
937
976
|
};
|
|
938
977
|
return (jsx("div", { className: "ab-modal-overlay", onClick: onCancel, children: jsxs("div", { className: "ab-modal", onClick: (e) => e.stopPropagation(), children: [jsxs("div", { className: "ab-modal-header", children: [jsx("h3", { children: "Accident Benefits" }), jsx("p", { className: "ab-modal-subtitle", children: isMyChoice ? 'Standard Coverage + Optional Enhancements' : 'Standard Coverage + Self-Selected Optional Enhancements' }), jsx("p", { className: "ab-modal-body-text", children: isMyChoice
|
|
939
978
|
? 'Maximum coverage with flexibility to remove additional benefits.'
|
|
940
|
-
: 'Base coverage with flexibility to select additional benefits' })] }), jsxs("div", { className: "ab-modal-content", children: [jsxs("div", { className: "ab-modal-standard-block ab-modal-standard-block--included", children: [jsxs("div", { className: "ab-modal-standard-included-header", children: [jsx("input", { type: "checkbox", checked: true, disabled: true, readOnly: true, className: "ab-modal-standard-locked-checkbox" }), jsx("h4", { children: isMyChoice ? 'All Coverages Included by Default' : 'Standard Benefits — Included' })] }), !isMyChoice && (jsx("p", { className: "ab-modal-standard-subtitle", children: "Medical, Rehabilitation, and Attendant Care" })), jsx("p", { className: "ab-modal-standard-description", children: isMyChoice ? (jsxs(Fragment, { children: ["Our standard quote automatically includes all optional accident benefits at their minimum coverage limits. We strongly recommend keeping all coverages selected to ensure the most complete protection \u2014 opting out could leave you without critical support when you need it most. For example, deselecting Income Replacement means no weekly payments if an injury prevents you from working, and removing Caregiver Replacement leaves no financial support for dependents in your care.", jsx("br", {}), jsx("br", {}), "Please speak with a broker partner to discuss higher limits or learn more about how each benefit protects you."] })) : ('Covers medical expenses, therapy and personal care for injuries from an accident, including doctor visits and physiotherapy.') })] }), jsxs("div", { className: "ab-modal-optional-block", children: [jsxs("label", { className: "ab-modal-select-all", children: [jsx("input", { type: "checkbox", checked: allSelected, onChange: handleSelectAll }), jsx("span", { children: "Select All Additional Benefits" })] }), jsx("div", { className: "ab-modal-options", children: accidentBenefitsOptionalOptions.map((option) => (jsxs("label", { className: "ab-modal-option", children: [jsx("input", { type: "checkbox", checked: localSelected.includes(option.value), onChange: () => handleToggle(option.value) }), jsxs("div", { className: "ab-modal-option-text", children: [jsx("strong", { children: option.name }), jsx("span", { children: option.description })] })] }, option.value))) }), jsxs("div", { className: "ab-modal-minimum-notice", children: [jsx("strong", { children: "Minimum Limits Apply" }), jsx("p", { children: "All optional benefits selected below will be quoted at their minimum coverage limits. Please speak with your broker if you wish to discuss higher limits." })] })] })] }), jsxs("div", { className: "ab-modal-actions", children: [jsx("button", { type: "button", className: "ab-modal-btn ab-modal-btn-cancel", onClick: onCancel, children: "Cancel" }), jsx("button", { type: "button", className: "ab-modal-btn ab-modal-btn-apply", onClick: () => onApply(localSelected), children: isMyChoiceOnly ? '
|
|
979
|
+
: 'Base coverage with flexibility to select additional benefits' })] }), jsxs("div", { className: "ab-modal-content", children: [jsxs("div", { className: "ab-modal-standard-block ab-modal-standard-block--included", children: [jsxs("div", { className: "ab-modal-standard-included-header", children: [jsx("input", { type: "checkbox", checked: true, disabled: true, readOnly: true, className: "ab-modal-standard-locked-checkbox" }), jsx("h4", { children: isMyChoice ? 'All Coverages Included by Default' : 'Standard Benefits — Included' })] }), !isMyChoice && (jsx("p", { className: "ab-modal-standard-subtitle", children: "Medical, Rehabilitation, and Attendant Care" })), jsx("p", { className: "ab-modal-standard-description", children: isMyChoice ? (jsxs(Fragment, { children: ["Our standard quote automatically includes all optional accident benefits at their minimum coverage limits. We strongly recommend keeping all coverages selected to ensure the most complete protection \u2014 opting out could leave you without critical support when you need it most. For example, deselecting Income Replacement means no weekly payments if an injury prevents you from working, and removing Caregiver Replacement leaves no financial support for dependents in your care.", jsx("br", {}), jsx("br", {}), "Please speak with a broker partner to discuss higher limits or learn more about how each benefit protects you."] })) : ('Covers medical expenses, therapy and personal care for injuries from an accident, including doctor visits and physiotherapy.') })] }), jsxs("div", { className: "ab-modal-optional-block", children: [jsxs("label", { className: "ab-modal-select-all", children: [jsx("input", { type: "checkbox", checked: allSelected, onChange: handleSelectAll }), jsx("span", { children: "Select All Additional Benefits" })] }), jsx("div", { className: "ab-modal-options", children: accidentBenefitsOptionalOptions.map((option) => (jsxs("label", { className: "ab-modal-option", children: [jsx("input", { type: "checkbox", checked: localSelected.includes(option.value), onChange: () => handleToggle(option.value) }), jsxs("div", { className: "ab-modal-option-text", children: [jsx("strong", { children: option.name }), jsx("span", { children: option.description })] })] }, option.value))) }), jsxs("div", { className: "ab-modal-minimum-notice", children: [jsx("strong", { children: "Minimum Limits Apply" }), jsx("p", { children: "All optional benefits selected below will be quoted at their minimum coverage limits. Please speak with your broker if you wish to discuss higher limits." })] })] })] }), jsxs("div", { className: "ab-modal-actions", children: [jsx("button", { type: "button", className: "ab-modal-btn ab-modal-btn-cancel", onClick: onCancel, children: "Cancel" }), jsx("button", { type: "button", className: "ab-modal-btn ab-modal-btn-apply", onClick: () => onApply(localSelected), children: isMyChoiceOnly ? 'Ok' : 'Apply' })] })] }) }));
|
|
941
980
|
};
|
|
942
981
|
|
|
943
982
|
const allAccidentBenefitsCoverageValues = accidentBenefitsOptionalOptions.map((option) => option.value);
|
|
@@ -1243,6 +1282,7 @@ const SectionDriverLicence = () => {
|
|
|
1243
1282
|
const [driverEducation, setDriverEducation] = useState(false);
|
|
1244
1283
|
const { configState } = useStoreFormCarConfig();
|
|
1245
1284
|
const { driverState, dispatchDriverLicenceState } = useStoreFormCarDriverLicence();
|
|
1285
|
+
const { discountState } = useStoreFormCarDiscount();
|
|
1246
1286
|
const { isAlbertaProvince, isOntarioProvince } = useProvince();
|
|
1247
1287
|
const mychoiceCls = isMyChoiceLike(appType) ? 'mychoice' : '';
|
|
1248
1288
|
const isTheBig = appType === AppTypes.TheBig;
|
|
@@ -1254,14 +1294,9 @@ const SectionDriverLicence = () => {
|
|
|
1254
1294
|
const driverNameDefault = `Driver ${driverState.activeIndex + 1}`;
|
|
1255
1295
|
const birthDate = birthYear && birthMonth && birthDay ? `${birthYear}-${birthMonth}-${birthDay}` : '';
|
|
1256
1296
|
const defaultMinDate = birthDate && firstLicenceAge ? addYearsToDate(birthDate, +firstLicenceAge) : '';
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
if (licenceType === DriverLicenceTypes.G)
|
|
1261
|
-
return { year: gLicenceYear, month: gLicenceMonth };
|
|
1262
|
-
return { year: g1LicenceYear, month: g1LicenceMonth };
|
|
1263
|
-
};
|
|
1264
|
-
const { year: ontarioQ1Year, month: ontarioQ1Month } = getOntarioQ1YearMonth();
|
|
1297
|
+
// Ontario Q1 (first-licence date) always lives in the G1 slot, independent of the chosen class.
|
|
1298
|
+
const ontarioQ1Year = g1LicenceYear;
|
|
1299
|
+
const ontarioQ1Month = g1LicenceMonth;
|
|
1265
1300
|
const ontarioQ1Date = ontarioQ1Year && ontarioQ1Month ? `${ontarioQ1Year}-${ontarioQ1Month}-01` : '';
|
|
1266
1301
|
const isOnlyG = useOntarioFlow
|
|
1267
1302
|
? (!!ontarioQ1Date && checkDateIsSpecial(ontarioQ1Date, configState.minDates.g.specialDate))
|
|
@@ -1315,7 +1350,7 @@ const SectionDriverLicence = () => {
|
|
|
1315
1350
|
const birthYearNum = parseInt(birthYear || '0', 10);
|
|
1316
1351
|
if (parseInt(ontarioQ1Year, 10) !== birthYearNum + 16 || ontarioQ1Month !== birthMonth)
|
|
1317
1352
|
return;
|
|
1318
|
-
const q1SlotForCorrection =
|
|
1353
|
+
const q1SlotForCorrection = DriverLicenceTypes.G1;
|
|
1319
1354
|
let correctedMonth = parseInt(birthMonth || '1', 10) + 1;
|
|
1320
1355
|
let correctedYear = birthYearNum + 16;
|
|
1321
1356
|
if (correctedMonth > 12) {
|
|
@@ -1472,8 +1507,9 @@ const SectionDriverLicence = () => {
|
|
|
1472
1507
|
if (useOntarioFlow) {
|
|
1473
1508
|
// When isOnlyG (before Apr 1994), treat as G Full regardless of what licenceType is in the store
|
|
1474
1509
|
const firstClass = isOnlyG ? DriverLicenceTypes.G : licenceType;
|
|
1475
|
-
// Q1 date
|
|
1476
|
-
|
|
1510
|
+
// Q1 (first-licence date) is always stored in the G1 slot, independent of the chosen class,
|
|
1511
|
+
// so switching the class never moves it and the DTC/isOnlyG checks stay consistent.
|
|
1512
|
+
const q1SlotType = DriverLicenceTypes.G1;
|
|
1477
1513
|
const q1Year = getLicenceYear(q1SlotType);
|
|
1478
1514
|
const q1Month = getLicenceMonth(q1SlotType);
|
|
1479
1515
|
const q1Date = toDate(q1Year, q1Month);
|
|
@@ -1486,19 +1522,21 @@ const SectionDriverLicence = () => {
|
|
|
1486
1522
|
const showGDateAfterG2 = firstClass === DriverLicenceTypes.G2 && receivedG === true;
|
|
1487
1523
|
const g2Date = toDate(g2LicenceYear, g2LicenceMonth);
|
|
1488
1524
|
const gDate = toDate(gLicenceYear, gLicenceMonth);
|
|
1489
|
-
//
|
|
1525
|
+
// Thresholds per the licensing flow: G1 < 5 years (4y11m = 59 months); G2/G < 3 years (2y11m = 35 months).
|
|
1526
|
+
const policyStart = discountState.policyStart || today;
|
|
1527
|
+
const q1MonthsFromEffective = q1Date ? getDifferenceInMonths(policyStart, q1Date) : Infinity;
|
|
1528
|
+
// G1 branch: ask once, based on the first licence date (< 5 years / 60 months).
|
|
1490
1529
|
const showDtcG1Only = firstClass === DriverLicenceTypes.G1
|
|
1491
1530
|
&& !!q1Date
|
|
1492
|
-
&&
|
|
1493
|
-
|
|
1494
|
-
// Branch B: DTC shown as soon as class=G2 and Q1 is within 5 years (before G question)
|
|
1531
|
+
&& q1MonthsFromEffective < 60;
|
|
1532
|
+
// Branch B: G2 first class (< 3 years / 36 months).
|
|
1495
1533
|
const showDtcG2Only = firstClass === DriverLicenceTypes.G2
|
|
1496
1534
|
&& !!q1Date
|
|
1497
|
-
&&
|
|
1498
|
-
// Branch C G Full:
|
|
1535
|
+
&& q1MonthsFromEffective < 36;
|
|
1536
|
+
// Branch C G Full: G first class (< 3 years / 36 months).
|
|
1499
1537
|
const showDtcGFull = firstClass === DriverLicenceTypes.G
|
|
1500
1538
|
&& !!q1Date
|
|
1501
|
-
&&
|
|
1539
|
+
&& q1MonthsFromEffective < 36;
|
|
1502
1540
|
// ── Validation error messages ──
|
|
1503
1541
|
const q1MinDate = getG1MinDate();
|
|
1504
1542
|
const q1ChronoError = driverState.inValidation
|
|
@@ -1534,7 +1572,7 @@ const SectionDriverLicence = () => {
|
|
|
1534
1572
|
};
|
|
1535
1573
|
const g2MaxDate = getMax60Date(q1Date);
|
|
1536
1574
|
const gMaxDate = getMax60Date(q1Date);
|
|
1537
|
-
return (jsxs("div", { className: `form-section ${mychoiceCls}`, children: [jsx(DateSelectFormBox, { name: "firstLicenceDate", dateNames: ['firstLicenceYear', 'firstLicenceMonth'], onDateChange: handleLicenceDateChange(q1SlotType), defaultValue: getDefaultLicenceDate(q1SlotType), title: `When did ${driverName} first receive their drivers license in Canada?`, description: "Select the year and month in which you received your first license to drive an automobile in Canada. This would include a beginners, intermediate or full license.", errorMessage: q1Error, error: driverState.inValidation, minDate: q1MinDate, maxDate: today, disabled: !birthDate }), q1Date && (jsx(SelectFormBox, { options: isOnlyG ? ontarioOnlyGOptions : ontarioFirstClassOptions, name: "firstLicenceClass", onChange: handleLicenceTypeChange, defaultValue: getSelectedOption(isOnlyG ? ontarioOnlyGOptions : ontarioFirstClassOptions,
|
|
1575
|
+
return (jsxs("div", { className: `form-section ${mychoiceCls}`, children: [jsx(DateSelectFormBox, { name: "firstLicenceDate", dateNames: ['firstLicenceYear', 'firstLicenceMonth'], onDateChange: handleLicenceDateChange(q1SlotType), defaultValue: getDefaultLicenceDate(q1SlotType), title: `When did ${driverName} first receive their drivers license in Canada?`, description: "Select the year and month in which you received your first license to drive an automobile in Canada. This would include a beginners, intermediate or full license.", errorMessage: q1Error, error: driverState.inValidation, minDate: q1MinDate, maxDate: today, disabled: !birthDate }), q1Date && (jsx(SelectFormBox, { options: isOnlyG ? ontarioOnlyGOptions : ontarioFirstClassOptions, name: "firstLicenceClass", onChange: handleLicenceTypeChange, defaultValue: getSelectedOption(isOnlyG ? ontarioOnlyGOptions : ontarioFirstClassOptions, firstClass), title: "What class of license was it?", placeholder: "Select from the list", description: "Drivers licensed in Ontario under the graduated licensing program would select G1 Beginner. If the driver was licensed outside Ontario or granted an exception for having driving experience from another jurisdiction then select the license class they first received. Select class G Full or equivalent if the driver was licensed in Ontario before 1994 or was licensed in another province. G equivalent classes in Canada are class 5.", autoSelectIfValueIsOutOfOptions: false })), firstClass === DriverLicenceTypes.G1 && (jsxs(Fragment, { children: [showDtcG1Only && (jsx(SwitchButtonBox, { items: yesNoOptions, onChange: handleTrainingChange, name: "passedDriverTraining", defaultValue: getSelectedOption(yesNoOptions, passedDriverTraining), title: `Has ${driverName} completed a gov't approved drivers education course within the past 3 years?` })), jsx(SwitchButtonBox, { items: yesNoOptions, onChange: handleReceivedG2Change, name: "receivedG2", defaultValue: getSelectedOption(yesNoOptions, receivedG2), title: `Did ${driverName} receive a G2 license?` }), showG2Date && (jsx(DateSelectFormBox, { name: "g2LicenceDate", dateNames: ['g2LicenceYear', 'g2LicenceMonth'], onDateChange: handleLicenceDateChange(DriverLicenceTypes.G2), defaultValue: getDefaultLicenceDate(DriverLicenceTypes.G2), title: `When did ${driverName} receive this G2 license?`, errorMessage: g2Error, error: driverState.inValidation, minDate: g2MinDate, maxDate: g2MaxDate })), showGQuestionAfterG1 && (jsx(SwitchButtonBox, { items: yesNoOptions, onChange: handleReceivedGChange, name: "receivedG", defaultValue: getSelectedOption(yesNoOptions, receivedG), title: `Did ${driverName} receive a G license?` })), showGDateAfterG1 && (jsx(DateSelectFormBox, { name: "gLicenceDate", dateNames: ['gLicenceYear', 'gLicenceMonth'], onDateChange: handleLicenceDateChange(DriverLicenceTypes.G), defaultValue: getDefaultLicenceDate(DriverLicenceTypes.G), title: `When did ${driverName} receive this G license?`, errorMessage: gError, error: driverState.inValidation, minDate: gMinDate, maxDate: gMaxDate }))] })), firstClass === DriverLicenceTypes.G2 && (jsxs(Fragment, { children: [showDtcG2Only && (jsx(SwitchButtonBox, { items: yesNoOptions, onChange: handleTrainingChange, name: "passedDriverTraining", defaultValue: getSelectedOption(yesNoOptions, passedDriverTraining), title: `Has ${driverName} completed a gov't approved drivers education course within the past 3 years?` })), jsx(SwitchButtonBox, { items: yesNoOptions, onChange: handleReceivedGChange, name: "receivedG", defaultValue: getSelectedOption(yesNoOptions, receivedG), title: `Did ${driverName} receive a G license?` }), showGDateAfterG2 && (jsx(DateSelectFormBox, { name: "gLicenceDate", dateNames: ['gLicenceYear', 'gLicenceMonth'], onDateChange: handleLicenceDateChange(DriverLicenceTypes.G), defaultValue: getDefaultLicenceDate(DriverLicenceTypes.G), title: `When did ${driverName} receive this G license?`, errorMessage: gError, error: driverState.inValidation, minDate: gMinDate, maxDate: gMaxDate }))] })), firstClass === DriverLicenceTypes.G && !isOnlyG && showDtcGFull && (jsx(SwitchButtonBox, { items: yesNoOptions, onChange: handleTrainingChange, name: "passedDriverTraining", defaultValue: getSelectedOption(yesNoOptions, passedDriverTraining), title: `Has ${driverName} completed a gov't approved drivers education course within the past 3 years?` })), isOnlyG && showDtcGFull && (jsx(SwitchButtonBox, { items: yesNoOptions, onChange: handleTrainingChange, name: "passedDriverTraining", defaultValue: getSelectedOption(yesNoOptions, passedDriverTraining), title: `Has ${driverName} completed a gov't approved drivers education course within the past 3 years?` })), jsx(SwitchButtonBox, { items: yesNoOptions, onChange: handlePreviousLicenceChange, name: "previousLicence", defaultValue: getSelectedOption(yesNoOptions, previousLicence), title: `Has ${driverName} ever had a full license anywhere else in Canada or USA?`, description: "If you have driving experience outside Canada or the United States, it may lower your premium. Your insurer may require proof of insurance in these other locations or some proof of driving experience from the country you indicate (like a copy of a previous driver's licence)." })] }));
|
|
1538
1576
|
}
|
|
1539
1577
|
return (jsxs("div", { className: `form-section ${mychoiceCls}`, children: [jsx(InputFormBox, { name: "firstLicenceAge", title: getLicenceAgeTitle(), onChange: handleLicenceAgeChange, type: InputTypes.Number, defaultValue: firstLicenceAge, description: configState.toolTip.licenceAge, hintMessage: birthDate
|
|
1540
1578
|
? `${driverName} was licenced in ${Number(birthYear) + Number(firstLicenceAge)} - ${Number(birthYear) + Number(firstLicenceAge) + 1}`
|
|
@@ -1561,17 +1599,17 @@ const SectionDriverInsurancePolicy = () => {
|
|
|
1561
1599
|
const effectiveMonth = policyStartMonth || String(todayDate.getMonth() + 1).padStart(2, '0');
|
|
1562
1600
|
const effectiveDay = policyStartDay || String(todayDate.getDate()).padStart(2, '0');
|
|
1563
1601
|
const yearsInPast = Math.min(todayDate.getFullYear() - parseInt(year, 10), 10);
|
|
1564
|
-
|
|
1565
|
-
value: subMonthsFromDate('', 6),
|
|
1566
|
-
name: 'Less than 1 year',
|
|
1567
|
-
});
|
|
1568
|
-
for (let i = 1; i <= yearsInPast; i += 1) {
|
|
1602
|
+
for (let i = yearsInPast; i >= 1; i -= 1) {
|
|
1569
1603
|
const pastDate = `${todayDate.getFullYear() - i}-${effectiveMonth}-${effectiveDay}`;
|
|
1570
1604
|
options.push({
|
|
1571
1605
|
value: pastDate,
|
|
1572
1606
|
name: i === 10 ? '10+ Years' : `${i} ${i === 1 ? 'Year' : 'Years'}`,
|
|
1573
1607
|
});
|
|
1574
1608
|
}
|
|
1609
|
+
options.push({
|
|
1610
|
+
value: subMonthsFromDate('', 6),
|
|
1611
|
+
name: 'Less than 1 year',
|
|
1612
|
+
});
|
|
1575
1613
|
}
|
|
1576
1614
|
return options;
|
|
1577
1615
|
};
|
|
@@ -1857,19 +1895,34 @@ const SectionDriverCancellation = () => {
|
|
|
1857
1895
|
}), insuranceCancellationList?.length < 3 && (jsx(ButtonBase, { category: CategoryTypes.Filled, onClick: handleAddButtonClick, size: SizeTypes.Medium, color: ColorTypes.Primary, label: "Add another" }))] }))] }));
|
|
1858
1896
|
};
|
|
1859
1897
|
|
|
1898
|
+
const SUSPENSION_YEAR_CAP = 6;
|
|
1899
|
+
const SuspensionDescription = () => {
|
|
1900
|
+
const listStyle = { margin: 0, paddingLeft: '1.25rem', whiteSpace: 'normal' };
|
|
1901
|
+
return (jsxs("div", { children: ["There are two main types of licence suspensions:", jsx("br", {}), "1. Suspension for Cause (Non-administrative)", jsx("br", {}), "A driver's licence is suspended or cancelled because of:", jsxs("ul", { style: listStyle, children: [jsx("li", { children: "Traffic offences." }), jsx("li", { children: "Criminal Code driving offences." }), jsx("li", { children: "Accumulating too many demerit points." })] }), "2. Administrative Suspension / Cancellation / Lapse", jsx("br", {}), "A driver's licence is suspended, cancelled, or allowed to lapse for reasons not related to driving offences or demerit points, such as:", jsxs("ul", { style: listStyle, children: [jsx("li", { children: "Roadside licence suspensions." }), jsx("li", { children: "Medical conditions." }), jsx("li", { children: "Failure to comply with court-ordered obligations." })] })] }));
|
|
1902
|
+
};
|
|
1860
1903
|
const BlockDriverSuspension = () => {
|
|
1861
1904
|
const { driverState, dispatchDriverSuspensionState } = useStoreFormCarDriverSuspension();
|
|
1862
1905
|
const { licenceSuspension, licenceSuspensionList = [] } = driverState.items[driverState.activeIndex];
|
|
1863
1906
|
const { dispatchDriverBaseState } = useStoreFormCarDriverBase();
|
|
1864
1907
|
const { discountState } = useStoreFormCarDiscount();
|
|
1865
1908
|
const { appConfigState: { appType } } = useStoreAppConfig();
|
|
1909
|
+
const { isOntarioProvince } = useProvince();
|
|
1866
1910
|
const isTheBig = appType === AppTypes.TheBig;
|
|
1867
|
-
const { firstName, birthYear, birthMonth, birthDay, licenceInfo: { firstLicenceAge }, } = driverState.items[driverState.activeIndex];
|
|
1911
|
+
const { firstName, birthYear, birthMonth, birthDay, licenceInfo: { firstLicenceAge, g1LicenceYear, g1LicenceMonth, }, } = driverState.items[driverState.activeIndex];
|
|
1868
1912
|
const birthDate = birthYear && birthMonth && birthDay ? `${birthYear}-${birthMonth}-${birthDay}` : '';
|
|
1869
|
-
|
|
1913
|
+
// Ontario flow stores the first-licence (Q1) date in the G1 slot; firstLicenceAge is not collected.
|
|
1914
|
+
const ontarioQ1Date = isOntarioProvince && isMyChoiceLike(appType) && g1LicenceYear && g1LicenceMonth
|
|
1915
|
+
? `${g1LicenceYear}-${g1LicenceMonth}-01`
|
|
1916
|
+
: '';
|
|
1917
|
+
const earliestLicenceDate = ontarioQ1Date
|
|
1918
|
+
|| (birthDate && firstLicenceAge ? addYearsToDate(birthDate, +firstLicenceAge) : '');
|
|
1870
1919
|
const policyStart = discountState.policyStart || getFormattedDate('', 'yyyy-MM-dd');
|
|
1871
|
-
const yearsElapsed = earliestLicenceDate
|
|
1920
|
+
const yearsElapsed = earliestLicenceDate
|
|
1921
|
+
? Math.min(getDifferenceInYears(policyStart, earliestLicenceDate), SUSPENSION_YEAR_CAP)
|
|
1922
|
+
: SUSPENSION_YEAR_CAP;
|
|
1923
|
+
const suspensionLookbackYears = Math.max(yearsElapsed, 1);
|
|
1872
1924
|
const suspensionYearLabel = yearsElapsed <= 1 ? 'past year' : `past ${yearsElapsed} years`;
|
|
1925
|
+
const suspensionMinFloor = earliestLicenceDate || getMinDate(birthDate, firstLicenceAge);
|
|
1873
1926
|
const handleSuspensionChange = ({ value }) => {
|
|
1874
1927
|
dispatchDriverSuspensionState({
|
|
1875
1928
|
type: StoreFormCarDriverSuspensionActionTypes.FormCarDriverLicenceSuspensionSelect,
|
|
@@ -1942,7 +1995,7 @@ const BlockDriverSuspension = () => {
|
|
|
1942
1995
|
});
|
|
1943
1996
|
}
|
|
1944
1997
|
};
|
|
1945
|
-
return (jsxs("div", { className: "form-block-container", children: [jsx("h2", { className: isTheBig ? 'thebig-bold' : '', children: "Licence Suspensions" }), jsx(SwitchButtonBox, { items: yesNoOptions, onChange: handleSuspensionChange, name: "licenceSuspension", defaultValue: getSelectedOption(yesNoOptions, licenceSuspension), title: `Has ${firstName} had a licence suspension within the ${suspensionYearLabel}?`, description:
|
|
1998
|
+
return (jsxs("div", { className: "form-block-container", children: [jsx("h2", { className: isTheBig ? 'thebig-bold' : '', children: "Licence Suspensions" }), jsx(SwitchButtonBox, { items: yesNoOptions, onChange: handleSuspensionChange, name: "licenceSuspension", defaultValue: getSelectedOption(yesNoOptions, licenceSuspension), title: `Has ${firstName} had a licence suspension within the ${suspensionYearLabel}?`, description: jsx(SuspensionDescription, {}) }), licenceSuspension && (jsxs(Fragment, { children: [licenceSuspensionList?.map(({ suspensionReason, suspensionYear, suspensionMonth, reinstatementYear, reinstatementMonth, }, index) => {
|
|
1946
1999
|
const defaultSuspensionDate = {
|
|
1947
2000
|
year: suspensionYear,
|
|
1948
2001
|
month: suspensionMonth,
|
|
@@ -1954,7 +2007,7 @@ const BlockDriverSuspension = () => {
|
|
|
1954
2007
|
const now = getFormattedDate('', 'yyyy-MM-dd');
|
|
1955
2008
|
const days = birthDay ? (+birthDay + 1) : 1;
|
|
1956
2009
|
const endDate = reinstatementYear && reinstatementMonth ? addDayToDate(`${reinstatementYear}-${reinstatementMonth}`, days) : now;
|
|
1957
|
-
const currentMinDate = getMinDateByYears(
|
|
2010
|
+
const currentMinDate = getMinDateByYears(suspensionMinFloor, suspensionLookbackYears);
|
|
1958
2011
|
const currentEndDate = compareDates(endDate, currentMinDate) < 0 ? currentMinDate : endDate;
|
|
1959
2012
|
return (jsxs("div", { className: "list-block", children: [jsx("hr", {}), jsxs("div", { className: "list-item", children: [jsx(SelectFormBox, { options: licenceSuspensionsReasonOptions, name: `suspensionReason-${index}`, onChange: handleReasonChange(index), defaultValue: suspensionReason, title: "Reason for licence suspension", placeholder: "Select from the list", isRemovable: licenceSuspensionList.length > 1, onIconClick: handleIconClick(index), autoSelectIfValueIsOutOfOptions: false, error: !suspensionReason && driverState.inValidation, errorMessage: getErrorMessage(suspensionReason, driverState.inValidation) }), jsx(DateSelectFormBox, { name: `suspensionDate-${index}`, dateNames: [`suspensionYear-${index}`, `suspensionMonth-${index}`], onDateChange: handleSuspensionDateChange(index), defaultValue: defaultSuspensionDate, title: "Start date", errorMessage: getDateErrorMessage(['01', suspensionMonth, suspensionYear], driverState.inValidation), error: driverState.inValidation, minDate: currentMinDate, maxDate: currentEndDate }), jsx(DateSelectFormBox, { name: `reinstatementDate-${index}`, dateNames: [`reinstatementYear-${index}`, `reinstatementMonth-${index}`], onDateChange: handleReinstatementDateChange(index), defaultValue: defaultReinstatementDate, title: "End Date", errorMessage: getDateErrorMessage(['01', reinstatementMonth, reinstatementYear], driverState.inValidation), error: driverState.inValidation, minDate: suspensionYear && suspensionMonth
|
|
1960
2013
|
? addDayToDate(`${suspensionYear}-${suspensionMonth}`, days) : currentMinDate })] })] }, `suspension-${index}`));
|
|
@@ -4399,7 +4452,9 @@ const NavigationBottom = ({ createItem, validateForm, formSteps, className, item
|
|
|
4399
4452
|
setSteps(filteredSteps);
|
|
4400
4453
|
}, [isCompleted]);
|
|
4401
4454
|
const onForwardClick = () => {
|
|
4402
|
-
|
|
4455
|
+
// Always re-run validation so newly shown required fields (e.g. G2 Yes/No) cannot be skipped
|
|
4456
|
+
// after an earlier successful validate left formIsValid=true.
|
|
4457
|
+
const isValid = validateForm ? validateForm() : !!formIsValid;
|
|
4403
4458
|
if (activeIndex < steps.length && isValid) {
|
|
4404
4459
|
navigate(`/${appConfigState.localIndex}/${appConfigState.insuranceType}${steps[activeIndex + 1].path}`);
|
|
4405
4460
|
}
|
|
@@ -4433,7 +4488,9 @@ const NavigationBottomTheBig = ({ createItem, validateForm, formSteps, className
|
|
|
4433
4488
|
setSteps(filteredSteps);
|
|
4434
4489
|
}, [isCompleted]);
|
|
4435
4490
|
const onForwardClick = () => {
|
|
4436
|
-
|
|
4491
|
+
// Always re-run validation so newly shown required fields cannot be skipped after
|
|
4492
|
+
// an earlier successful validate left formIsValid=true.
|
|
4493
|
+
const isValid = validateForm ? validateForm() : !!formIsValid;
|
|
4437
4494
|
if (activeIndex < steps.length && isValid) {
|
|
4438
4495
|
navigate(`/${appConfigState.localIndex}/${appConfigState.insuranceType}${steps[activeIndex + 1].path}`);
|
|
4439
4496
|
}
|