@mychoice/mychoice-sdk-modules 2.2.26 → 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.
@@ -1,5 +1,5 @@
1
1
  import { SelectOptionGroupInterface } from '@mychoice/mychoice-sdk-components';
2
2
  /**
3
- * Filter out G1/G2 options (ALC, RB) from the "Criminal" section for trafficTicketsGroupOptions
3
+ * Filter out G1/G2 options from "Serious" section for trafficTicketsGroupOptions
4
4
  */
5
5
  export declare const getTrafficTicketsGroupOptionsWithoutG1G2: () => SelectOptionGroupInterface[];
@@ -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, getDifferenceInMonths, 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';
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, useCallback, useEffect, useRef, useMemo } from 'react';
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) => (jsx(ButtonForm, { className: setClassNames(item.value === activeId), onClick: handleClick(item), selected: item.value === activeId, label: item.name }, `${item.name}_${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
- dispatchDiscountState({
208
- type: StoreFormCarDiscountActionTypes.FormCarDiscountVehlinkSelect,
209
- payload: {
210
- driverIndex: value,
211
- vehicleIndex,
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
- 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." }), vehicles.map((vehicle, vehicleIndex) => {
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 allDriverOptions = drivers
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 filteredOptions = allDriverOptions.filter(({ value }) => {
237
- const assignedVehicle = vehlinks.find((l) => l.driverIndex === value && l.priority === DriverPriorityTypes.Prn);
238
- return !assignedVehicle || assignedVehicle.vehicleIndex === vehicleIndex;
239
- });
240
- const availablePrimaryOptions = filteredOptions.length > 0 ? filteredOptions : allDriverOptions;
241
- return (jsx(SelectFormBox, { title: `Who drives the ${year} ${make} ${model} the most?`, onChange: handlePrimaryDriverChange(vehicleIndex), options: availablePrimaryOptions, defaultValue: selectedPrimary?.driverIndex ?? 0, name: `vehlink-${vehicleIndex}` }, `vehlink-${vehicleIndex}`));
242
- }), drivers.length > vehicles.length && unassignedDrivers.map(({ driver, index: driverIndex }) => {
243
- const selectedVehicle = occVehlinks.find((l) => l.driverIndex === driverIndex);
244
- const vehicleOptions = vehicles.map((vehicle, vehicleIndex) => ({
245
- name: `${vehicle.year} ${vehicle.make} ${vehicle.model}`,
246
- value: vehicleIndex,
247
- }));
248
- return (jsx(SelectFormBox, { title: `Which vehicle does ${driver.firstName} drive the most?`, onChange: handleOccDriverChange(driverIndex), options: vehicleOptions, defaultValue: selectedVehicle?.vehicleIndex ?? 0, name: `occ-vehlink-${driverIndex}` }, `occ-vehlink-${driverIndex}`));
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 : 1)}`;
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 : 1))} 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 }))] }));
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 = () => {
@@ -918,6 +957,7 @@ const VehicleSectionMain = () => {
918
957
  const AccidentBenefitsModal = ({ selectedCoverages, onApply, onCancel, selectAllByDefault = false, }) => {
919
958
  const { appConfigState: { appType } } = useStoreAppConfig();
920
959
  const isMyChoice = isMyChoiceLike(appType);
960
+ const isMyChoiceOnly = appType === AppTypes.MyChoice;
921
961
  const allValues = accidentBenefitsOptionalOptions.map((o) => o.value);
922
962
  const [localSelected, setLocalSelected] = useState(selectedCoverages.length > 0
923
963
  ? [...selectedCoverages]
@@ -936,7 +976,7 @@ const AccidentBenefitsModal = ({ selectedCoverages, onApply, onCancel, selectAll
936
976
  };
937
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
938
978
  ? 'Maximum coverage with flexibility to remove additional benefits.'
939
- : '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: "Apply" })] })] }) }));
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' })] })] }) }));
940
980
  };
941
981
 
942
982
  const allAccidentBenefitsCoverageValues = accidentBenefitsOptionalOptions.map((option) => option.value);
@@ -1242,6 +1282,7 @@ const SectionDriverLicence = () => {
1242
1282
  const [driverEducation, setDriverEducation] = useState(false);
1243
1283
  const { configState } = useStoreFormCarConfig();
1244
1284
  const { driverState, dispatchDriverLicenceState } = useStoreFormCarDriverLicence();
1285
+ const { discountState } = useStoreFormCarDiscount();
1245
1286
  const { isAlbertaProvince, isOntarioProvince } = useProvince();
1246
1287
  const mychoiceCls = isMyChoiceLike(appType) ? 'mychoice' : '';
1247
1288
  const isTheBig = appType === AppTypes.TheBig;
@@ -1253,14 +1294,9 @@ const SectionDriverLicence = () => {
1253
1294
  const driverNameDefault = `Driver ${driverState.activeIndex + 1}`;
1254
1295
  const birthDate = birthYear && birthMonth && birthDay ? `${birthYear}-${birthMonth}-${birthDay}` : '';
1255
1296
  const defaultMinDate = birthDate && firstLicenceAge ? addYearsToDate(birthDate, +firstLicenceAge) : '';
1256
- const getOntarioQ1YearMonth = () => {
1257
- if (licenceType === DriverLicenceTypes.G2)
1258
- return { year: g2LicenceYear, month: g2LicenceMonth };
1259
- if (licenceType === DriverLicenceTypes.G)
1260
- return { year: gLicenceYear, month: gLicenceMonth };
1261
- return { year: g1LicenceYear, month: g1LicenceMonth };
1262
- };
1263
- 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;
1264
1300
  const ontarioQ1Date = ontarioQ1Year && ontarioQ1Month ? `${ontarioQ1Year}-${ontarioQ1Month}-01` : '';
1265
1301
  const isOnlyG = useOntarioFlow
1266
1302
  ? (!!ontarioQ1Date && checkDateIsSpecial(ontarioQ1Date, configState.minDates.g.specialDate))
@@ -1314,7 +1350,7 @@ const SectionDriverLicence = () => {
1314
1350
  const birthYearNum = parseInt(birthYear || '0', 10);
1315
1351
  if (parseInt(ontarioQ1Year, 10) !== birthYearNum + 16 || ontarioQ1Month !== birthMonth)
1316
1352
  return;
1317
- const q1SlotForCorrection = licenceType || DriverLicenceTypes.G1;
1353
+ const q1SlotForCorrection = DriverLicenceTypes.G1;
1318
1354
  let correctedMonth = parseInt(birthMonth || '1', 10) + 1;
1319
1355
  let correctedYear = birthYearNum + 16;
1320
1356
  if (correctedMonth > 12) {
@@ -1471,8 +1507,9 @@ const SectionDriverLicence = () => {
1471
1507
  if (useOntarioFlow) {
1472
1508
  // When isOnlyG (before Apr 1994), treat as G Full regardless of what licenceType is in the store
1473
1509
  const firstClass = isOnlyG ? DriverLicenceTypes.G : licenceType;
1474
- // Q1 date lives in whichever slot the store wrote to (always ${licenceType}LicenceYear/Month).
1475
- const q1SlotType = licenceType || DriverLicenceTypes.G1;
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;
1476
1513
  const q1Year = getLicenceYear(q1SlotType);
1477
1514
  const q1Month = getLicenceMonth(q1SlotType);
1478
1515
  const q1Date = toDate(q1Year, q1Month);
@@ -1485,19 +1522,21 @@ const SectionDriverLicence = () => {
1485
1522
  const showGDateAfterG2 = firstClass === DriverLicenceTypes.G2 && receivedG === true;
1486
1523
  const g2Date = toDate(g2LicenceYear, g2LicenceMonth);
1487
1524
  const gDate = toDate(gLicenceYear, gLicenceMonth);
1488
- // G1 branch: ask once, based on the first licence date. Show/hide uses 5-year gate; question text says "3 years".
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).
1489
1529
  const showDtcG1Only = firstClass === DriverLicenceTypes.G1
1490
1530
  && !!q1Date
1491
- && getDifferenceInYears('', q1Date) <= 5
1492
- && !showG2Date;
1493
- // 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).
1494
1533
  const showDtcG2Only = firstClass === DriverLicenceTypes.G2
1495
1534
  && !!q1Date
1496
- && getDifferenceInYears('', q1Date) <= 5;
1497
- // Branch C G Full: within 5 years of Q1 (G date)
1535
+ && q1MonthsFromEffective < 36;
1536
+ // Branch C G Full: G first class (< 3 years / 36 months).
1498
1537
  const showDtcGFull = firstClass === DriverLicenceTypes.G
1499
1538
  && !!q1Date
1500
- && getDifferenceInYears('', q1Date) <= 5;
1539
+ && q1MonthsFromEffective < 36;
1501
1540
  // ── Validation error messages ──
1502
1541
  const q1MinDate = getG1MinDate();
1503
1542
  const q1ChronoError = driverState.inValidation
@@ -1533,7 +1572,7 @@ const SectionDriverLicence = () => {
1533
1572
  };
1534
1573
  const g2MaxDate = getMax60Date(q1Date);
1535
1574
  const gMaxDate = getMax60Date(q1Date);
1536
- 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, licenceType), title: "What class of license was it?", placeholder: "Select from the list", disabled: isOnlyG, 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)." })] }));
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)." })] }));
1537
1576
  }
1538
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
1539
1578
  ? `${driverName} was licenced in ${Number(birthYear) + Number(firstLicenceAge)} - ${Number(birthYear) + Number(firstLicenceAge) + 1}`
@@ -1560,17 +1599,17 @@ const SectionDriverInsurancePolicy = () => {
1560
1599
  const effectiveMonth = policyStartMonth || String(todayDate.getMonth() + 1).padStart(2, '0');
1561
1600
  const effectiveDay = policyStartDay || String(todayDate.getDate()).padStart(2, '0');
1562
1601
  const yearsInPast = Math.min(todayDate.getFullYear() - parseInt(year, 10), 10);
1563
- options.push({
1564
- value: subMonthsFromDate('', 6),
1565
- name: 'Less than 1 year',
1566
- });
1567
- for (let i = 1; i <= yearsInPast; i += 1) {
1602
+ for (let i = yearsInPast; i >= 1; i -= 1) {
1568
1603
  const pastDate = `${todayDate.getFullYear() - i}-${effectiveMonth}-${effectiveDay}`;
1569
1604
  options.push({
1570
1605
  value: pastDate,
1571
1606
  name: i === 10 ? '10+ Years' : `${i} ${i === 1 ? 'Year' : 'Years'}`,
1572
1607
  });
1573
1608
  }
1609
+ options.push({
1610
+ value: subMonthsFromDate('', 6),
1611
+ name: 'Less than 1 year',
1612
+ });
1574
1613
  }
1575
1614
  return options;
1576
1615
  };
@@ -1856,19 +1895,34 @@ const SectionDriverCancellation = () => {
1856
1895
  }), insuranceCancellationList?.length < 3 && (jsx(ButtonBase, { category: CategoryTypes.Filled, onClick: handleAddButtonClick, size: SizeTypes.Medium, color: ColorTypes.Primary, label: "Add another" }))] }))] }));
1857
1896
  };
1858
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
+ };
1859
1903
  const BlockDriverSuspension = () => {
1860
1904
  const { driverState, dispatchDriverSuspensionState } = useStoreFormCarDriverSuspension();
1861
1905
  const { licenceSuspension, licenceSuspensionList = [] } = driverState.items[driverState.activeIndex];
1862
1906
  const { dispatchDriverBaseState } = useStoreFormCarDriverBase();
1863
1907
  const { discountState } = useStoreFormCarDiscount();
1864
1908
  const { appConfigState: { appType } } = useStoreAppConfig();
1909
+ const { isOntarioProvince } = useProvince();
1865
1910
  const isTheBig = appType === AppTypes.TheBig;
1866
- 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];
1867
1912
  const birthDate = birthYear && birthMonth && birthDay ? `${birthYear}-${birthMonth}-${birthDay}` : '';
1868
- const earliestLicenceDate = birthDate && firstLicenceAge ? addYearsToDate(birthDate, +firstLicenceAge) : '';
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) : '');
1869
1919
  const policyStart = discountState.policyStart || getFormattedDate('', 'yyyy-MM-dd');
1870
- const yearsElapsed = earliestLicenceDate ? Math.min(getDifferenceInYears(policyStart, earliestLicenceDate), 6) : 6;
1920
+ const yearsElapsed = earliestLicenceDate
1921
+ ? Math.min(getDifferenceInYears(policyStart, earliestLicenceDate), SUSPENSION_YEAR_CAP)
1922
+ : SUSPENSION_YEAR_CAP;
1923
+ const suspensionLookbackYears = Math.max(yearsElapsed, 1);
1871
1924
  const suspensionYearLabel = yearsElapsed <= 1 ? 'past year' : `past ${yearsElapsed} years`;
1925
+ const suspensionMinFloor = earliestLicenceDate || getMinDate(birthDate, firstLicenceAge);
1872
1926
  const handleSuspensionChange = ({ value }) => {
1873
1927
  dispatchDriverSuspensionState({
1874
1928
  type: StoreFormCarDriverSuspensionActionTypes.FormCarDriverLicenceSuspensionSelect,
@@ -1941,7 +1995,7 @@ const BlockDriverSuspension = () => {
1941
1995
  });
1942
1996
  }
1943
1997
  };
1944
- 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: "There are two main types of licence suspensions: 1. Non-administrated suspension for cause: A driver's licence is suspended or cancelled because of traffic and criminal code driving offences or collecting too many demerit points. 2. Administrative suspension/cancellation/lapse: A driver's licence is suspended, cancelled, or allowed to lapse for reasons other than driving offences or demerit points such as a roadside licence suspension, medical conditions or failure to comply with court\u2011ordered obligations." }), licenceSuspension && (jsxs(Fragment, { children: [licenceSuspensionList?.map(({ suspensionReason, suspensionYear, suspensionMonth, reinstatementYear, reinstatementMonth, }, index) => {
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) => {
1945
1999
  const defaultSuspensionDate = {
1946
2000
  year: suspensionYear,
1947
2001
  month: suspensionMonth,
@@ -1953,7 +2007,7 @@ const BlockDriverSuspension = () => {
1953
2007
  const now = getFormattedDate('', 'yyyy-MM-dd');
1954
2008
  const days = birthDay ? (+birthDay + 1) : 1;
1955
2009
  const endDate = reinstatementYear && reinstatementMonth ? addDayToDate(`${reinstatementYear}-${reinstatementMonth}`, days) : now;
1956
- const currentMinDate = getMinDateByYears(getMinDate(birthDate, firstLicenceAge), 6);
2010
+ const currentMinDate = getMinDateByYears(suspensionMinFloor, suspensionLookbackYears);
1957
2011
  const currentEndDate = compareDates(endDate, currentMinDate) < 0 ? currentMinDate : endDate;
1958
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
1959
2013
  ? addDayToDate(`${suspensionYear}-${suspensionMonth}`, days) : currentMinDate })] })] }, `suspension-${index}`));
@@ -2037,17 +2091,16 @@ const BlockDriverAccident = () => {
2037
2091
  };
2038
2092
 
2039
2093
  /**
2040
- * Filter out G1/G2 options (ALC, RB) from the "Criminal" section for trafficTicketsGroupOptions
2094
+ * Filter out G1/G2 options from "Serious" section for trafficTicketsGroupOptions
2041
2095
  */
2042
2096
  const getTrafficTicketsGroupOptionsWithoutG1G2 = () => {
2043
2097
  const excludeOptions = ['ALC', 'RB'];
2044
- return trafficTicketsGroupOptions.map((group) => {
2045
- if (group.label !== 'Criminal')
2046
- return group;
2047
- return {
2048
- ...group,
2049
- options: group.options.filter((option) => !excludeOptions.includes(option.value)),
2050
- };
2098
+ const seriousOptions = trafficTicketsGroupOptions.find((entry) => entry.label === 'Serious');
2099
+ const filteredSeriousOptions = seriousOptions.options.filter((option) => !excludeOptions.includes(option.value));
2100
+ return trafficTicketsGroupOptions.map((option) => {
2101
+ if (option.label === 'Serious')
2102
+ return { ...option, options: filteredSeriousOptions };
2103
+ return option;
2051
2104
  });
2052
2105
  };
2053
2106
 
@@ -4399,7 +4452,9 @@ const NavigationBottom = ({ createItem, validateForm, formSteps, className, item
4399
4452
  setSteps(filteredSteps);
4400
4453
  }, [isCompleted]);
4401
4454
  const onForwardClick = () => {
4402
- const isValid = !formIsValid ? validateForm && validateForm() : formIsValid;
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
- const isValid = !formIsValid ? validateForm && validateForm() : formIsValid;
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
  }