@axos-web-dev/shared-components 2.0.0-dev.13-stepForm.4 → 2.0.0-dev.13-stepForm.5

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,11 +1,11 @@
1
1
  "use client";
2
- import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
3
3
  import { useState, useRef, useMemo, useEffect } from "react";
4
4
  import { AnimatePresence, motion } from "framer-motion";
5
- import { Button, Field, Label, Input, RadioGroup, Radio } from "@headlessui/react";
6
- import { section, formShell, successWrap, successTitle, successText, disclosure, disclosureTitle, disclosureText, mainTitle, mainDescription, progressWrap, progressRow, progressLabel, progressTrack, progressFill, stepPanel, title, description, actions, secondaryButton, primaryButton, disclosureTextSm, overlayOpacity, inputGrid, inputRowTwo, fieldBlock, fieldLabel, input, errorText, optionalText, radioOption, radioOptionChecked, radioText, autoAdvanceHint, sliderWrap, sliderHeader, sliderValue, slider, helperText, amountPreview, amountPreviewLabel, amountPreviewValue, radioGrid, autoAdvanceStatus, autoAdvanceText, autoAdvanceTrack, autoAdvanceBar } from "./MortgageRateStep.css.js";
7
- import { fixedLoanTermOptions, armLoanTermOptions, fixedLoanTypeOptions, amortizationTypeOptions, creditScoreOptions, propertyUseOptions, propertyTypeOptions, mortgageTypeOptions } from "./mortgageLeadForm.config.js";
8
- import { parseCurrencyInput, formatCurrency, formatPhoneInput, formatPercentInput, formatCurrencyInput, calculatePercentAmount, validateZipCode, validateEmail, validatePhone } from "./mortgageLeadForm.utils.js";
5
+ import { Button, Field, Label, Input, Listbox, ListboxButton, ListboxOptions, ListboxOption, Fieldset, Legend, RadioGroup, Radio } from "@headlessui/react";
6
+ import { section, formShell, successWrap, successTitle, successText, disclosure, disclosureTitle, disclosureText, mainTitle, mainDescription, progressWrap, progressRow, progressLabel, progressTrack, progressFill, stepPanel, title, description, actions, secondaryButton, primaryButton, disclosureTextSm, overlayOpacity, inputGrid, fieldBlock, fieldLabel, input, errorText, selectWrap, selectButton, selectButtonPlaceholder, selectChevron, selectOptions, selectOption, autoAdvanceHint, fieldSet, subStepLabel, inputRowTwo, radioOption, radioOptionChecked, radioText, conditionalField, conditionalFieldWrap, selectLabel, sliderWrap, sliderHeader, sliderValue, slider, helperText, amountPreview, amountPreviewLabel, amountPreviewValue, optionalText, radioGrid, autoAdvanceStatus, autoAdvanceText, autoAdvanceTrack, autoAdvanceBar } from "./MortgageRateStep.css.js";
7
+ import { updateFrequencyOptions, fixedLoanTermOptions, armLoanTermOptions, amortizationTypeOptions, creditScoreOptions, propertyTypeOptions, unitCountOptions, propertyUseOptions, mortgageTypeOptions } from "./mortgageLeadForm.config.js";
8
+ import { parseCurrencyInput, formatCurrency, formatPercentInput, formatCurrencyInput, formatPhoneInput, calculatePercentAmount, validateEmail, validatePhone, validateZipCode } from "./mortgageLeadForm.utils.js";
9
9
  import "../../icons/ArrowIcon/ArrowIcon.css.js";
10
10
  import "../../icons/CheckIcon/CheckIcon.css.js";
11
11
  import '../../assets/icons/FollowIcon/FollowIcon.css';import '../../assets/icons/DownloadIcon/DownloadIcon.css';import '../../assets/themes/victorie.css';import '../../assets/themes/ufb.css';import '../../assets/themes/premier.css';import '../../assets/themes/axos.css';/* empty css */
@@ -21,6 +21,7 @@ const AUTO_ADVANCE_DELAY = 1e3;
21
21
  const initialValues = {
22
22
  mortgageType: "",
23
23
  propertyType: "",
24
+ unitCount: "",
24
25
  propertyUse: "",
25
26
  creditScore: "",
26
27
  purchasePrice: "",
@@ -31,14 +32,15 @@ const initialValues = {
31
32
  cashOutAmount: "",
32
33
  zipCode: "",
33
34
  amortizationType: "",
34
- fixedLoanType: "",
35
+ fixedLoanType: "conforming",
35
36
  loanTerm: "",
36
37
  desiredRate: "",
37
- discountPoints: "",
38
+ discountPoints: "0",
38
39
  firstName: "",
39
40
  lastName: "",
40
41
  email: "",
41
- phoneNumber: ""
42
+ phoneNumber: "",
43
+ updateFrequency: ""
42
44
  };
43
45
  function MortgageRateStep() {
44
46
  const [currentStepIndex, setCurrentStepIndex] = useState(0);
@@ -51,22 +53,18 @@ function MortgageRateStep() {
51
53
  const autoAdvanceTimeout = useRef(null);
52
54
  const isPurchase = values.mortgageType === "purchase-rates";
53
55
  const isRefinance = values.mortgageType === "refinance-rates";
54
- const showFixedLoanTypeStep = values.amortizationType === "fixed";
56
+ const isMultiUnit = values.propertyType === "2-4-unit";
55
57
  const steps = useMemo(() => {
56
- const base = [
58
+ return [
59
+ "contact",
57
60
  "mortgageType",
58
- "propertyType",
59
- "propertyUse",
61
+ "propertyTypeUse",
60
62
  "creditScore",
61
63
  "propertyDetails",
62
- "amortizationType"
64
+ "amortizationLoanTerm",
65
+ "finalDetails"
63
66
  ];
64
- if (showFixedLoanTypeStep) {
65
- base.push("fixedLoanType");
66
- }
67
- base.push("loanTerm", "finalDetails", "contact");
68
- return base;
69
- }, [showFixedLoanTypeStep]);
67
+ }, []);
70
68
  const totalSteps = steps.length;
71
69
  const stepKey = steps[currentStepIndex];
72
70
  const progress = (currentStepIndex + 1) / totalSteps * 100;
@@ -99,8 +97,18 @@ function MortgageRateStep() {
99
97
  Number(value)
100
98
  );
101
99
  }
100
+ if (key === "mortgageType") {
101
+ next.purchasePrice = "";
102
+ next.downPaymentAmount = "";
103
+ next.downPaymentPercent = 10;
104
+ next.estimatedHomeValue = "";
105
+ next.currentLoanBalance = "";
106
+ next.cashOutAmount = "";
107
+ }
108
+ if (key === "propertyType" && value !== "2-4-unit") {
109
+ next.unitCount = "";
110
+ }
102
111
  if (key === "amortizationType") {
103
- next.fixedLoanType = "";
104
112
  next.loanTerm = "";
105
113
  }
106
114
  return next;
@@ -113,14 +121,37 @@ function MortgageRateStep() {
113
121
  };
114
122
  const validateStep = (step) => {
115
123
  const nextErrors = {};
124
+ if (step === "contact") {
125
+ if (!values.firstName.trim()) {
126
+ nextErrors.firstName = "Please enter your first name.";
127
+ }
128
+ if (!values.lastName.trim()) {
129
+ nextErrors.lastName = "Please enter your last name.";
130
+ }
131
+ if (!values.email.trim()) {
132
+ nextErrors.email = "Please enter your email address.";
133
+ } else if (!validateEmail(values.email)) {
134
+ nextErrors.email = "Please enter a valid email address.";
135
+ }
136
+ if (!values.phoneNumber.trim()) {
137
+ nextErrors.phoneNumber = "Please enter your phone number.";
138
+ } else if (!validatePhone(values.phoneNumber)) {
139
+ nextErrors.phoneNumber = "Please enter a valid 10-digit phone number.";
140
+ }
141
+ }
116
142
  if (step === "mortgageType" && !values.mortgageType) {
117
143
  nextErrors.mortgageType = "Please select a mortgage type.";
118
144
  }
119
- if (step === "propertyType" && !values.propertyType) {
120
- nextErrors.propertyType = "Please select a property type.";
121
- }
122
- if (step === "propertyUse" && !values.propertyUse) {
123
- nextErrors.propertyUse = "Please select how the property will be used.";
145
+ if (step === "propertyTypeUse") {
146
+ if (!values.propertyType) {
147
+ nextErrors.propertyType = "Please select a property type.";
148
+ }
149
+ if (values.propertyType === "2-4-unit" && !values.unitCount) {
150
+ nextErrors.unitCount = "Please select the number of units.";
151
+ }
152
+ if (!values.propertyUse) {
153
+ nextErrors.propertyUse = "Please select how the property will be used.";
154
+ }
124
155
  }
125
156
  if (step === "creditScore" && !values.creditScore) {
126
157
  nextErrors.creditScore = "Please select your estimated credit score.";
@@ -150,36 +181,22 @@ function MortgageRateStep() {
150
181
  }
151
182
  }
152
183
  }
153
- if (step === "amortizationType" && !values.amortizationType) {
154
- nextErrors.amortizationType = "Please select an amortization type.";
155
- }
156
- if (step === "fixedLoanType" && !values.fixedLoanType) {
157
- nextErrors.fixedLoanType = "Please select a loan type.";
158
- }
159
- if (step === "loanTerm" && !values.loanTerm) {
160
- nextErrors.loanTerm = "Please select a desired loan term.";
184
+ if (step === "amortizationLoanTerm") {
185
+ if (!values.amortizationType) {
186
+ nextErrors.amortizationType = "Please select an amortization type.";
187
+ }
188
+ if (!values.loanTerm) {
189
+ nextErrors.loanTerm = "Please select a desired loan term.";
190
+ }
161
191
  }
162
192
  if (step === "finalDetails") {
163
193
  if (!values.desiredRate.trim()) {
164
194
  nextErrors.desiredRate = "Please enter your desired rate.";
195
+ } else if (Number(values.desiredRate) < 2 || Number(values.desiredRate) > 10) {
196
+ nextErrors.desiredRate = "Please enter a rate between 2% and 10%.";
165
197
  }
166
- }
167
- if (step === "contact") {
168
- if (!values.firstName.trim()) {
169
- nextErrors.firstName = "Please enter your first name.";
170
- }
171
- if (!values.lastName.trim()) {
172
- nextErrors.lastName = "Please enter your last name.";
173
- }
174
- if (!values.email.trim()) {
175
- nextErrors.email = "Please enter your email address.";
176
- } else if (!validateEmail(values.email)) {
177
- nextErrors.email = "Please enter a valid email address.";
178
- }
179
- if (!values.phoneNumber.trim()) {
180
- nextErrors.phoneNumber = "Please enter your phone number.";
181
- } else if (!validatePhone(values.phoneNumber)) {
182
- nextErrors.phoneNumber = "Please enter a valid 10-digit phone number.";
198
+ if (!values.updateFrequency) {
199
+ nextErrors.updateFrequency = "Please select how often you'd like to receive rate updates.";
183
200
  }
184
201
  }
185
202
  setErrors(nextErrors);
@@ -203,37 +220,31 @@ function MortgageRateStep() {
203
220
  setLastInteractedRadioStep(null);
204
221
  setCurrentStepIndex((prev) => Math.max(0, prev - 1));
205
222
  };
206
- const getAutoAdvanceValue = () => {
207
- switch (stepKey) {
208
- case "mortgageType":
209
- return values.mortgageType;
210
- case "propertyType":
211
- return values.propertyType;
212
- case "propertyUse":
213
- return values.propertyUse;
214
- case "creditScore":
215
- return values.creditScore;
216
- case "amortizationType":
217
- return values.amortizationType;
218
- case "fixedLoanType":
219
- return values.fixedLoanType;
220
- case "loanTerm":
221
- return values.loanTerm;
222
- default:
223
- return "";
224
- }
225
- };
226
223
  const isAutoAdvanceStep = (key) => {
227
224
  return [
228
225
  "mortgageType",
229
- "propertyType",
230
- "propertyUse",
226
+ "propertyTypeUse",
231
227
  "creditScore",
232
- "amortizationType",
233
- "fixedLoanType",
234
- "loanTerm"
228
+ "amortizationLoanTerm"
235
229
  ].includes(key);
236
230
  };
231
+ const isAutoAdvanceReady = (key) => {
232
+ switch (key) {
233
+ case "mortgageType":
234
+ return !!values.mortgageType;
235
+ case "propertyTypeUse":
236
+ if (!values.propertyType || !values.propertyUse) return false;
237
+ if (values.propertyType === "2-4-unit" && !values.unitCount)
238
+ return false;
239
+ return true;
240
+ case "creditScore":
241
+ return !!values.creditScore;
242
+ case "amortizationLoanTerm":
243
+ return !!values.amortizationType && !!values.loanTerm;
244
+ default:
245
+ return false;
246
+ }
247
+ };
237
248
  const registerRadioInteraction = (step) => {
238
249
  setLastInteractedRadioStep(step);
239
250
  setRadioSelectionVersion((prev) => prev + 1);
@@ -241,13 +252,16 @@ function MortgageRateStep() {
241
252
  useEffect(() => {
242
253
  clearAutoAdvance();
243
254
  if (!isAutoAdvanceStep(stepKey)) return;
244
- const selectedValue = getAutoAdvanceValue();
245
- if (!selectedValue) return;
255
+ if (!isAutoAdvanceReady(stepKey)) return;
246
256
  if (lastInteractedRadioStep !== stepKey) return;
247
257
  setIsAutoAdvancing(true);
258
+ const stepAtSchedule = currentStepIndex;
248
259
  autoAdvanceTimeout.current = setTimeout(() => {
249
- if (currentStepIndex === totalSteps - 1) return;
250
- setCurrentStepIndex((prev) => prev + 1);
260
+ setCurrentStepIndex((prev) => {
261
+ if (prev !== stepAtSchedule) return prev;
262
+ if (prev === totalSteps - 1) return prev;
263
+ return prev + 1;
264
+ });
251
265
  setIsAutoAdvancing(false);
252
266
  }, AUTO_ADVANCE_DELAY);
253
267
  return () => clearAutoAdvance();
@@ -259,52 +273,44 @@ function MortgageRateStep() {
259
273
  radioSelectionVersion,
260
274
  values.mortgageType,
261
275
  values.propertyType,
276
+ values.unitCount,
262
277
  values.propertyUse,
263
278
  values.creditScore,
264
279
  values.amortizationType,
265
- values.fixedLoanType,
266
280
  values.loanTerm
267
281
  ]);
268
282
  const getStepTitle = () => {
269
283
  switch (stepKey) {
284
+ case "contact":
285
+ return "Let's start with how we can reach you.";
270
286
  case "mortgageType":
271
287
  return "What type of mortgage are you interested in?";
272
- case "propertyType":
273
- return "What type of property are you interested in financing?";
274
- case "propertyUse":
275
- return "How do you plan to utilize this property?";
288
+ case "propertyTypeUse":
289
+ return "Tell us about the property.";
276
290
  case "creditScore":
277
291
  return "What is your estimated credit score?";
278
292
  case "propertyDetails":
279
293
  return "Please share more details about the property.";
280
- case "amortizationType":
281
- return "What type of amortization do you prefer for the loan?";
282
- case "fixedLoanType":
283
- return "Which type of loan are you interested in?";
284
- case "loanTerm":
285
- return "What is your desired loan term?";
294
+ case "amortizationLoanTerm":
295
+ return "Choose your loan structure.";
286
296
  case "finalDetails":
287
297
  return "Some final details...";
288
- case "contact":
289
- return "Tell us how we can get in touch with you.";
290
298
  default:
291
299
  return "";
292
300
  }
293
301
  };
294
302
  const getStepDescription = () => {
295
303
  switch (stepKey) {
304
+ case "contact":
305
+ return "Share your contact details so we can send you personalized rate alerts that match your goals.";
296
306
  case "mortgageType":
297
307
  return "Select the option that best matches your needs.";
298
- case "propertyUse":
299
- return "Select how you intend to utilize the property so we can assist you in discovering the most suitable mortgage options available for you.";
308
+ case "propertyTypeUse":
309
+ return "Tell us a bit about the property type and how you plan to use it so we can match you with the most suitable mortgage options.";
300
310
  case "propertyDetails":
301
311
  return "Kindly share these extra details so we can better grasp your mortgage requirements.";
302
- case "amortizationType":
303
- return "Choosing an amortization type allows us to customize loan options that align with your financial objectives.";
304
- case "fixedLoanType":
305
- return "Select the loan type that most accurately reflects the mortgage you are looking for.";
306
- case "loanTerm":
307
- return "Choose the duration for your loan term.";
312
+ case "amortizationLoanTerm":
313
+ return "Pick the amortization style that fits your goals, then select your preferred loan term.";
308
314
  case "finalDetails":
309
315
  return "You're almost done! Just share these last details so we can help you discover the best rates available.";
310
316
  default:
@@ -320,6 +326,105 @@ function MortgageRateStep() {
320
326
  };
321
327
  const renderStep = () => {
322
328
  switch (stepKey) {
329
+ case "contact":
330
+ return /* @__PURE__ */ jsxs(Fieldset, { className: `${inputGrid} ${fieldSet}`, children: [
331
+ /* @__PURE__ */ jsx(Legend, { className: fieldLabel, style: { fontSize: "1rem" }, children: "Contact Information" }),
332
+ /* @__PURE__ */ jsxs("div", { className: inputRowTwo, children: [
333
+ /* @__PURE__ */ jsxs(Field, { className: fieldBlock, children: [
334
+ /* @__PURE__ */ jsx(Label, { className: fieldLabel, children: "First Name" }),
335
+ /* @__PURE__ */ jsx(
336
+ Input,
337
+ {
338
+ className: input,
339
+ type: "text",
340
+ placeholder: "Enter your first name",
341
+ value: values.firstName,
342
+ onChange: (e) => updateValue("firstName", e.target.value),
343
+ "aria-describedby": errors.firstName ? "firstName-error" : void 0,
344
+ "aria-invalid": !!errors.firstName,
345
+ autoComplete: "given-name"
346
+ }
347
+ ),
348
+ errors.firstName ? /* @__PURE__ */ jsx(
349
+ "p",
350
+ {
351
+ id: "firstName-error",
352
+ className: errorText,
353
+ role: "alert",
354
+ children: errors.firstName
355
+ }
356
+ ) : null
357
+ ] }),
358
+ /* @__PURE__ */ jsxs(Field, { className: fieldBlock, children: [
359
+ /* @__PURE__ */ jsx(Label, { className: fieldLabel, children: "Last Name" }),
360
+ /* @__PURE__ */ jsx(
361
+ Input,
362
+ {
363
+ className: input,
364
+ type: "text",
365
+ placeholder: "Enter your last name",
366
+ value: values.lastName,
367
+ onChange: (e) => updateValue("lastName", e.target.value),
368
+ "aria-describedby": errors.lastName ? "lastName-error" : void 0,
369
+ "aria-invalid": !!errors.lastName,
370
+ autoComplete: "family-name"
371
+ }
372
+ ),
373
+ errors.lastName ? /* @__PURE__ */ jsx(
374
+ "p",
375
+ {
376
+ id: "lastName-error",
377
+ className: errorText,
378
+ role: "alert",
379
+ children: errors.lastName
380
+ }
381
+ ) : null
382
+ ] })
383
+ ] }),
384
+ /* @__PURE__ */ jsxs(Field, { className: fieldBlock, children: [
385
+ /* @__PURE__ */ jsx(Label, { className: fieldLabel, children: "Email" }),
386
+ /* @__PURE__ */ jsx(
387
+ Input,
388
+ {
389
+ className: input,
390
+ type: "email",
391
+ placeholder: "Enter your email",
392
+ value: values.email,
393
+ onChange: (e) => updateValue("email", e.target.value),
394
+ "aria-describedby": errors.email ? "email-error" : void 0,
395
+ "aria-invalid": !!errors.email,
396
+ autoComplete: "email"
397
+ }
398
+ ),
399
+ errors.email ? /* @__PURE__ */ jsx("p", { id: "email-error", className: errorText, role: "alert", children: errors.email }) : null
400
+ ] }),
401
+ /* @__PURE__ */ jsxs(Field, { className: fieldBlock, children: [
402
+ /* @__PURE__ */ jsx(Label, { className: fieldLabel, children: "Phone Number" }),
403
+ /* @__PURE__ */ jsx(
404
+ Input,
405
+ {
406
+ className: input,
407
+ type: "tel",
408
+ inputMode: "tel",
409
+ placeholder: "(XXX) XXX-XXXX",
410
+ value: values.phoneNumber,
411
+ onChange: (e) => updateValue("phoneNumber", formatPhoneInput(e.target.value)),
412
+ "aria-describedby": errors.phoneNumber ? "phoneNumber-error" : void 0,
413
+ "aria-invalid": !!errors.phoneNumber,
414
+ autoComplete: "tel"
415
+ }
416
+ ),
417
+ errors.phoneNumber ? /* @__PURE__ */ jsx(
418
+ "p",
419
+ {
420
+ id: "phoneNumber-error",
421
+ className: errorText,
422
+ role: "alert",
423
+ children: errors.phoneNumber
424
+ }
425
+ ) : null
426
+ ] })
427
+ ] });
323
428
  case "mortgageType":
324
429
  return /* @__PURE__ */ jsxs(Field, { className: fieldBlock, children: [
325
430
  /* @__PURE__ */ jsx(Label, { className: fieldLabel, children: "Choose one" }),
@@ -348,73 +453,191 @@ function MortgageRateStep() {
348
453
  ),
349
454
  /* @__PURE__ */ jsx("p", { className: autoAdvanceHint, children: "Your selection will continue automatically." }),
350
455
  renderAutoAdvanceStatus(),
351
- errors.mortgageType ? /* @__PURE__ */ jsx("p", { className: errorText, children: errors.mortgageType }) : null
352
- ] });
353
- case "propertyType":
354
- return /* @__PURE__ */ jsxs(Field, { className: fieldBlock, style: { marginTop: "24px" }, children: [
355
- /* @__PURE__ */ jsx(Label, { className: fieldLabel, children: "Choose one" }),
356
- /* @__PURE__ */ jsx(
357
- RadioGroup,
456
+ errors.mortgageType ? /* @__PURE__ */ jsx(
457
+ "p",
358
458
  {
359
- value: values.propertyType,
360
- onChange: (value) => {
361
- updateValue("propertyType", value);
362
- registerRadioInteraction("propertyType");
363
- },
364
- className: radioGrid,
365
- style: {
366
- gridTemplateColumns: "repeat(auto-fit,minmax(258px,1fr))"
367
- },
368
- children: propertyTypeOptions.map((option) => {
369
- const checked = values.propertyType === option.value;
370
- return /* @__PURE__ */ jsx(
371
- Radio,
372
- {
373
- value: option.value,
374
- className: `${radioOption} ${checked ? radioOptionChecked : ""}`,
375
- children: /* @__PURE__ */ jsx("span", { className: radioText, children: option.label })
376
- },
377
- option.value
378
- );
379
- })
459
+ id: "mortgageType-error",
460
+ className: errorText,
461
+ role: "alert",
462
+ children: errors.mortgageType
380
463
  }
381
- ),
382
- /* @__PURE__ */ jsx("p", { className: autoAdvanceHint, children: "Your selection will continue automatically." }),
383
- renderAutoAdvanceStatus(),
384
- errors.propertyType ? /* @__PURE__ */ jsx("p", { className: errorText, children: errors.propertyType }) : null
464
+ ) : null
385
465
  ] });
386
- case "propertyUse":
387
- return /* @__PURE__ */ jsxs(Field, { className: fieldBlock, children: [
388
- /* @__PURE__ */ jsx(Label, { className: fieldLabel, children: "Choose one" }),
389
- /* @__PURE__ */ jsx(
390
- RadioGroup,
391
- {
392
- value: values.propertyUse,
393
- onChange: (value) => {
394
- updateValue("propertyUse", value);
395
- registerRadioInteraction("propertyUse");
396
- },
397
- className: radioGrid,
398
- style: {
399
- gridTemplateColumns: "repeat(auto-fit,minmax(258px,1fr))"
466
+ case "propertyTypeUse":
467
+ return /* @__PURE__ */ jsxs(Fieldset, { className: fieldSet, children: [
468
+ /* @__PURE__ */ jsx(Legend, { className: fieldLabel, style: { fontSize: "1rem" }, children: "Property Details" }),
469
+ /* @__PURE__ */ jsxs(Field, { className: fieldBlock, style: { marginTop: "16px" }, children: [
470
+ /* @__PURE__ */ jsx(Label, { className: subStepLabel, children: "What type of property are you financing?" }),
471
+ /* @__PURE__ */ jsx(
472
+ RadioGroup,
473
+ {
474
+ value: values.propertyType,
475
+ onChange: (value) => {
476
+ updateValue("propertyType", value);
477
+ registerRadioInteraction("propertyTypeUse");
478
+ },
479
+ className: radioGrid,
480
+ style: {
481
+ gridTemplateColumns: "repeat(auto-fit,minmax(258px,1fr))"
482
+ },
483
+ children: propertyTypeOptions.map((option) => {
484
+ const checked = values.propertyType === option.value;
485
+ return /* @__PURE__ */ jsx(
486
+ Radio,
487
+ {
488
+ value: option.value,
489
+ className: `${radioOption} ${checked ? radioOptionChecked : ""}`,
490
+ children: /* @__PURE__ */ jsx("span", { className: radioText, children: option.label })
491
+ },
492
+ option.value
493
+ );
494
+ })
495
+ }
496
+ ),
497
+ errors.propertyType ? /* @__PURE__ */ jsx(
498
+ "p",
499
+ {
500
+ id: "propertyType-error",
501
+ className: errorText,
502
+ role: "alert",
503
+ children: errors.propertyType
504
+ }
505
+ ) : null,
506
+ /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: isMultiUnit && /* @__PURE__ */ jsx(
507
+ motion.div,
508
+ {
509
+ initial: { opacity: 0, height: 0, y: -8 },
510
+ animate: { opacity: 1, height: "auto", y: 0 },
511
+ exit: { opacity: 0, height: 0, y: -8 },
512
+ transition: { duration: 0.28, ease: "easeOut" },
513
+ className: conditionalField,
514
+ children: /* @__PURE__ */ jsxs("div", { className: conditionalFieldWrap, children: [
515
+ /* @__PURE__ */ jsxs(
516
+ Listbox,
517
+ {
518
+ value: values.unitCount,
519
+ onChange: (value) => {
520
+ updateValue("unitCount", value);
521
+ registerRadioInteraction("propertyTypeUse");
522
+ },
523
+ children: [
524
+ /* @__PURE__ */ jsx(Label, { className: selectLabel, children: "How many units?" }),
525
+ /* @__PURE__ */ jsxs("div", { className: selectWrap, children: [
526
+ /* @__PURE__ */ jsx(
527
+ ListboxButton,
528
+ {
529
+ className: selectButton,
530
+ "aria-describedby": errors.unitCount ? "unitCount-error" : void 0,
531
+ "aria-invalid": !!errors.unitCount,
532
+ children: ({ open }) => /* @__PURE__ */ jsxs(Fragment, { children: [
533
+ /* @__PURE__ */ jsx(
534
+ "span",
535
+ {
536
+ className: values.unitCount ? "" : selectButtonPlaceholder,
537
+ children: values.unitCount ? unitCountOptions.find(
538
+ (o) => o.value === values.unitCount
539
+ )?.label : "Select number of units"
540
+ }
541
+ ),
542
+ /* @__PURE__ */ jsx(
543
+ "svg",
544
+ {
545
+ className: selectChevron,
546
+ "data-open": open,
547
+ viewBox: "0 0 20 20",
548
+ fill: "none",
549
+ xmlns: "http://www.w3.org/2000/svg",
550
+ "aria-hidden": "true",
551
+ children: /* @__PURE__ */ jsx(
552
+ "path",
553
+ {
554
+ d: "M5 7.5L10 12.5L15 7.5",
555
+ stroke: "currentColor",
556
+ strokeWidth: "2",
557
+ strokeLinecap: "round",
558
+ strokeLinejoin: "round"
559
+ }
560
+ )
561
+ }
562
+ )
563
+ ] })
564
+ }
565
+ ),
566
+ /* @__PURE__ */ jsx(
567
+ ListboxOptions,
568
+ {
569
+ className: selectOptions,
570
+ modal: false,
571
+ children: unitCountOptions.map((option) => /* @__PURE__ */ jsx(
572
+ ListboxOption,
573
+ {
574
+ value: option.value,
575
+ className: selectOption,
576
+ children: option.label
577
+ },
578
+ option.value
579
+ ))
580
+ }
581
+ )
582
+ ] })
583
+ ]
584
+ }
585
+ ),
586
+ errors.unitCount ? /* @__PURE__ */ jsx(
587
+ "p",
588
+ {
589
+ id: "unitCount-error",
590
+ className: errorText,
591
+ role: "alert",
592
+ style: { marginTop: "8px" },
593
+ children: errors.unitCount
594
+ }
595
+ ) : null
596
+ ] })
400
597
  },
401
- children: propertyUseOptions.map((option) => {
402
- const checked = values.propertyUse === option.value;
403
- return /* @__PURE__ */ jsx(
404
- Radio,
405
- {
406
- value: option.value,
407
- className: `${radioOption} ${checked ? radioOptionChecked : ""}`,
408
- children: /* @__PURE__ */ jsx("span", { className: radioText, children: option.label })
409
- },
410
- option.value
411
- );
412
- })
413
- }
414
- ),
415
- /* @__PURE__ */ jsx("p", { className: autoAdvanceHint, children: "Your selection will continue automatically." }),
416
- renderAutoAdvanceStatus(),
417
- errors.propertyUse ? /* @__PURE__ */ jsx("p", { className: errorText, children: errors.propertyUse }) : null
598
+ "unit-count-wrap"
599
+ ) })
600
+ ] }),
601
+ /* @__PURE__ */ jsxs(Field, { className: fieldBlock, style: { marginTop: "12px" }, children: [
602
+ /* @__PURE__ */ jsx(Label, { className: subStepLabel, children: "How do you plan to use this property?" }),
603
+ /* @__PURE__ */ jsx(
604
+ RadioGroup,
605
+ {
606
+ value: values.propertyUse,
607
+ onChange: (value) => {
608
+ updateValue("propertyUse", value);
609
+ registerRadioInteraction("propertyTypeUse");
610
+ },
611
+ className: radioGrid,
612
+ style: {
613
+ gridTemplateColumns: "repeat(auto-fit,minmax(258px,1fr))"
614
+ },
615
+ children: propertyUseOptions.map((option) => {
616
+ const checked = values.propertyUse === option.value;
617
+ return /* @__PURE__ */ jsx(
618
+ Radio,
619
+ {
620
+ value: option.value,
621
+ className: `${radioOption} ${checked ? radioOptionChecked : ""}`,
622
+ children: /* @__PURE__ */ jsx("span", { className: radioText, children: option.label })
623
+ },
624
+ option.value
625
+ );
626
+ })
627
+ }
628
+ ),
629
+ errors.propertyUse ? /* @__PURE__ */ jsx(
630
+ "p",
631
+ {
632
+ id: "propertyUse-error",
633
+ className: errorText,
634
+ role: "alert",
635
+ children: errors.propertyUse
636
+ }
637
+ ) : null
638
+ ] }),
639
+ /* @__PURE__ */ jsx("p", { className: autoAdvanceHint, style: { marginTop: "12px" }, children: "Once all selections are made, you'll continue automatically." }),
640
+ renderAutoAdvanceStatus()
418
641
  ] });
419
642
  case "creditScore":
420
643
  return /* @__PURE__ */ jsxs(Field, { className: fieldBlock, style: { marginTop: "24px" }, children: [
@@ -444,7 +667,15 @@ function MortgageRateStep() {
444
667
  ),
445
668
  /* @__PURE__ */ jsx("p", { className: autoAdvanceHint, children: "Your selection will continue automatically." }),
446
669
  renderAutoAdvanceStatus(),
447
- errors.creditScore ? /* @__PURE__ */ jsx("p", { className: errorText, children: errors.creditScore }) : null
670
+ errors.creditScore ? /* @__PURE__ */ jsx(
671
+ "p",
672
+ {
673
+ id: "creditScore-error",
674
+ className: errorText,
675
+ role: "alert",
676
+ children: errors.creditScore
677
+ }
678
+ ) : null
448
679
  ] });
449
680
  case "propertyDetails":
450
681
  if (isPurchase) {
@@ -461,10 +692,20 @@ function MortgageRateStep() {
461
692
  onChange: (e) => updateValue(
462
693
  "purchasePrice",
463
694
  formatCurrencyInput(e.target.value)
464
- )
695
+ ),
696
+ "aria-describedby": errors.purchasePrice ? "purchasePrice-error" : void 0,
697
+ "aria-invalid": !!errors.purchasePrice
465
698
  }
466
699
  ),
467
- errors.purchasePrice ? /* @__PURE__ */ jsx("p", { className: errorText, children: errors.purchasePrice }) : null
700
+ errors.purchasePrice ? /* @__PURE__ */ jsx(
701
+ "p",
702
+ {
703
+ id: "purchasePrice-error",
704
+ className: errorText,
705
+ role: "alert",
706
+ children: errors.purchasePrice
707
+ }
708
+ ) : null
468
709
  ] }),
469
710
  /* @__PURE__ */ jsxs(Field, { className: sliderWrap, children: [
470
711
  /* @__PURE__ */ jsxs("div", { className: sliderHeader, children: [
@@ -484,7 +725,8 @@ function MortgageRateStep() {
484
725
  step: 1,
485
726
  value: values.downPaymentPercent,
486
727
  onChange: (e) => updateValue("downPaymentPercent", Number(e.target.value)),
487
- "aria-label": "Down payment percentage"
728
+ "aria-label": "Down payment percentage",
729
+ "aria-valuetext": `${values.downPaymentPercent}% down payment, estimated amount ${purchaseDownPaymentPreview}`
488
730
  }
489
731
  ),
490
732
  /* @__PURE__ */ jsx("p", { className: helperText, children: "Move the slider to estimate your down payment amount instantly." }),
@@ -505,10 +747,20 @@ function MortgageRateStep() {
505
747
  onChange: (e) => updateValue(
506
748
  "zipCode",
507
749
  e.target.value.replace(/[^\d]/g, "").slice(0, 5)
508
- )
750
+ ),
751
+ "aria-describedby": errors.zipCode ? "zipCode-error" : void 0,
752
+ "aria-invalid": !!errors.zipCode
509
753
  }
510
754
  ),
511
- errors.zipCode ? /* @__PURE__ */ jsx("p", { className: errorText, children: errors.zipCode }) : null
755
+ errors.zipCode ? /* @__PURE__ */ jsx(
756
+ "p",
757
+ {
758
+ id: "zipCode-error",
759
+ className: errorText,
760
+ role: "alert",
761
+ children: errors.zipCode
762
+ }
763
+ ) : null
512
764
  ] })
513
765
  ] });
514
766
  }
@@ -532,10 +784,20 @@ function MortgageRateStep() {
532
784
  onChange: (e) => updateValue(
533
785
  "estimatedHomeValue",
534
786
  formatCurrencyInput(e.target.value)
535
- )
787
+ ),
788
+ "aria-describedby": errors.estimatedHomeValue ? "estimatedHomeValue-error" : void 0,
789
+ "aria-invalid": !!errors.estimatedHomeValue
536
790
  }
537
791
  ),
538
- errors.estimatedHomeValue ? /* @__PURE__ */ jsx("p", { className: errorText, children: errors.estimatedHomeValue }) : null
792
+ errors.estimatedHomeValue ? /* @__PURE__ */ jsx(
793
+ "p",
794
+ {
795
+ id: "estimatedHomeValue-error",
796
+ className: errorText,
797
+ role: "alert",
798
+ children: errors.estimatedHomeValue
799
+ }
800
+ ) : null
539
801
  ] }),
540
802
  /* @__PURE__ */ jsxs(Field, { className: fieldBlock, children: [
541
803
  /* @__PURE__ */ jsx(Label, { className: fieldLabel, children: "Current Loan Balance" }),
@@ -549,10 +811,20 @@ function MortgageRateStep() {
549
811
  onChange: (e) => updateValue(
550
812
  "currentLoanBalance",
551
813
  formatCurrencyInput(e.target.value)
552
- )
814
+ ),
815
+ "aria-describedby": errors.currentLoanBalance ? "currentLoanBalance-error" : void 0,
816
+ "aria-invalid": !!errors.currentLoanBalance
553
817
  }
554
818
  ),
555
- errors.currentLoanBalance ? /* @__PURE__ */ jsx("p", { className: errorText, children: errors.currentLoanBalance }) : null
819
+ errors.currentLoanBalance ? /* @__PURE__ */ jsx(
820
+ "p",
821
+ {
822
+ id: "currentLoanBalance-error",
823
+ className: errorText,
824
+ role: "alert",
825
+ children: errors.currentLoanBalance
826
+ }
827
+ ) : null
556
828
  ] }),
557
829
  /* @__PURE__ */ jsxs(Field, { className: fieldBlock, children: [
558
830
  /* @__PURE__ */ jsxs(Label, { className: fieldLabel, children: [
@@ -570,9 +842,20 @@ function MortgageRateStep() {
570
842
  onChange: (e) => updateValue(
571
843
  "cashOutAmount",
572
844
  formatCurrencyInput(e.target.value)
573
- )
845
+ ),
846
+ "aria-describedby": errors.cashOutAmount ? "cashOutAmount-error" : void 0,
847
+ "aria-invalid": !!errors.cashOutAmount
574
848
  }
575
- )
849
+ ),
850
+ errors.cashOutAmount ? /* @__PURE__ */ jsx(
851
+ "p",
852
+ {
853
+ id: "cashOutAmount-error",
854
+ className: errorText,
855
+ role: "alert",
856
+ children: errors.cashOutAmount
857
+ }
858
+ ) : null
576
859
  ] }),
577
860
  /* @__PURE__ */ jsxs(Field, { className: fieldBlock, children: [
578
861
  /* @__PURE__ */ jsx(Label, { className: fieldLabel, children: "ZIP Code" }),
@@ -586,128 +869,197 @@ function MortgageRateStep() {
586
869
  onChange: (e) => updateValue(
587
870
  "zipCode",
588
871
  e.target.value.replace(/[^\d]/g, "").slice(0, 5)
589
- )
872
+ ),
873
+ "aria-describedby": errors.zipCode ? "zipCode-error" : void 0,
874
+ "aria-invalid": !!errors.zipCode
590
875
  }
591
876
  ),
592
- errors.zipCode ? /* @__PURE__ */ jsx("p", { className: errorText, children: errors.zipCode }) : null
877
+ errors.zipCode ? /* @__PURE__ */ jsx("p", { id: "zipCode-error", className: errorText, role: "alert", children: errors.zipCode }) : null
593
878
  ] })
594
879
  ]
595
880
  }
596
881
  );
597
- case "amortizationType":
598
- return /* @__PURE__ */ jsxs(Field, { className: fieldBlock, children: [
599
- /* @__PURE__ */ jsx(Label, { className: fieldLabel, children: "Choose one" }),
600
- /* @__PURE__ */ jsx(
601
- RadioGroup,
602
- {
603
- value: values.amortizationType,
604
- onChange: (value) => {
605
- updateValue("amortizationType", value);
606
- registerRadioInteraction("amortizationType");
607
- },
608
- className: inputRowTwo,
609
- children: amortizationTypeOptions.map((option) => {
610
- const checked = values.amortizationType === option.value;
611
- return /* @__PURE__ */ jsx(
612
- Radio,
613
- {
614
- value: option.value,
615
- className: `${radioOption} ${checked ? radioOptionChecked : ""}`,
616
- children: /* @__PURE__ */ jsx("span", { className: radioText, children: option.label })
617
- },
618
- option.value
619
- );
620
- })
621
- }
622
- ),
623
- /* @__PURE__ */ jsx("p", { className: autoAdvanceHint, children: "Your selection will continue automatically." }),
624
- renderAutoAdvanceStatus(),
625
- errors.amortizationType ? /* @__PURE__ */ jsx("p", { className: errorText, children: errors.amortizationType }) : null
626
- ] });
627
- case "fixedLoanType":
628
- return /* @__PURE__ */ jsxs(Field, { className: fieldBlock, children: [
629
- /* @__PURE__ */ jsx(Label, { className: fieldLabel, children: "Choose one" }),
630
- /* @__PURE__ */ jsx(
631
- RadioGroup,
632
- {
633
- value: values.fixedLoanType,
634
- onChange: (value) => {
635
- updateValue("fixedLoanType", value);
636
- registerRadioInteraction("fixedLoanType");
637
- },
638
- className: inputRowTwo,
639
- children: fixedLoanTypeOptions.map((option) => {
640
- const checked = values.fixedLoanType === option.value;
641
- return /* @__PURE__ */ jsx(
642
- Radio,
643
- {
644
- value: option.value,
645
- className: `${radioOption} ${checked ? radioOptionChecked : ""}`,
646
- children: /* @__PURE__ */ jsx("span", { className: radioText, children: option.label })
647
- },
648
- option.value
649
- );
650
- })
651
- }
652
- ),
653
- /* @__PURE__ */ jsx("p", { className: autoAdvanceHint, children: "Your selection will continue automatically." }),
654
- renderAutoAdvanceStatus(),
655
- errors.fixedLoanType ? /* @__PURE__ */ jsx("p", { className: errorText, children: errors.fixedLoanType }) : null
656
- ] });
657
- case "loanTerm":
658
- const loanTermOptions = values.amortizationType === "fixed" ? fixedLoanTermOptions : armLoanTermOptions;
659
- return /* @__PURE__ */ jsxs(Field, { className: fieldBlock, children: [
660
- /* @__PURE__ */ jsx(Label, { className: fieldLabel, children: "Choose one" }),
661
- /* @__PURE__ */ jsx(
662
- RadioGroup,
663
- {
664
- value: values.loanTerm,
665
- onChange: (value) => {
666
- updateValue("loanTerm", value);
667
- registerRadioInteraction("loanTerm");
882
+ case "amortizationLoanTerm": {
883
+ const loanTermOptions = values.amortizationType === "fixed" ? fixedLoanTermOptions : values.amortizationType === "arm" ? armLoanTermOptions : [];
884
+ const selectedTermLabel = loanTermOptions.find(
885
+ (o) => o.value === values.loanTerm
886
+ )?.label;
887
+ return /* @__PURE__ */ jsxs(Fieldset, { className: fieldSet, children: [
888
+ /* @__PURE__ */ jsx(Legend, { className: fieldLabel, style: { fontSize: "1rem" }, children: "Loan Structure" }),
889
+ /* @__PURE__ */ jsxs(Field, { className: fieldBlock, style: { marginTop: "16px" }, children: [
890
+ /* @__PURE__ */ jsx(Label, { className: subStepLabel, children: "Which amortization type do you prefer?" }),
891
+ /* @__PURE__ */ jsx(
892
+ RadioGroup,
893
+ {
894
+ value: values.amortizationType,
895
+ onChange: (value) => {
896
+ updateValue("amortizationType", value);
897
+ registerRadioInteraction("amortizationLoanTerm");
898
+ },
899
+ className: inputRowTwo,
900
+ children: amortizationTypeOptions.map((option) => {
901
+ const checked = values.amortizationType === option.value;
902
+ return /* @__PURE__ */ jsx(
903
+ Radio,
904
+ {
905
+ value: option.value,
906
+ className: `${radioOption} ${checked ? radioOptionChecked : ""}`,
907
+ title: option.value === "fixed" ? "Fixed Rate Mortgage" : "Adjustable Rate Mortgage",
908
+ children: /* @__PURE__ */ jsx("span", { className: radioText, children: option.label })
909
+ },
910
+ option.value
911
+ );
912
+ })
913
+ }
914
+ ),
915
+ errors.amortizationType ? /* @__PURE__ */ jsx(
916
+ "p",
917
+ {
918
+ id: "amortizationType-error",
919
+ className: errorText,
920
+ role: "alert",
921
+ children: errors.amortizationType
922
+ }
923
+ ) : null,
924
+ /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", initial: false, children: values.amortizationType && /* @__PURE__ */ jsxs(
925
+ motion.div,
926
+ {
927
+ initial: { opacity: 0, height: 0, y: -8 },
928
+ animate: { opacity: 1, height: "auto", y: 0 },
929
+ exit: { opacity: 0, height: 0, y: -8 },
930
+ transition: { duration: 0.32, ease: [0.22, 1, 0.36, 1] },
931
+ className: conditionalField,
932
+ style: { minHeight: "154px" },
933
+ children: [
934
+ /* @__PURE__ */ jsxs("div", { className: conditionalFieldWrap, children: [
935
+ /* @__PURE__ */ jsxs(
936
+ Listbox,
937
+ {
938
+ value: values.loanTerm,
939
+ onChange: (value) => {
940
+ updateValue("loanTerm", value);
941
+ registerRadioInteraction("amortizationLoanTerm");
942
+ },
943
+ children: [
944
+ /* @__PURE__ */ jsx(Label, { className: selectLabel, children: values.amortizationType === "fixed" ? "What is your desired loan term?" : "What is your desired ARM term?" }),
945
+ /* @__PURE__ */ jsxs("div", { className: selectWrap, children: [
946
+ /* @__PURE__ */ jsx(
947
+ ListboxButton,
948
+ {
949
+ className: selectButton,
950
+ "aria-describedby": errors.loanTerm ? "loanTerm-error" : void 0,
951
+ "aria-invalid": !!errors.loanTerm,
952
+ children: ({ open }) => /* @__PURE__ */ jsxs(Fragment, { children: [
953
+ /* @__PURE__ */ jsx(
954
+ "span",
955
+ {
956
+ className: values.loanTerm ? "" : selectButtonPlaceholder,
957
+ children: selectedTermLabel ?? "Select a loan term"
958
+ }
959
+ ),
960
+ /* @__PURE__ */ jsx(
961
+ "svg",
962
+ {
963
+ className: selectChevron,
964
+ "data-open": open,
965
+ viewBox: "0 0 20 20",
966
+ fill: "none",
967
+ xmlns: "http://www.w3.org/2000/svg",
968
+ "aria-hidden": "true",
969
+ children: /* @__PURE__ */ jsx(
970
+ "path",
971
+ {
972
+ d: "M5 7.5L10 12.5L15 7.5",
973
+ stroke: "currentColor",
974
+ strokeWidth: "2",
975
+ strokeLinecap: "round",
976
+ strokeLinejoin: "round"
977
+ }
978
+ )
979
+ }
980
+ )
981
+ ] })
982
+ }
983
+ ),
984
+ /* @__PURE__ */ jsx(
985
+ ListboxOptions,
986
+ {
987
+ className: selectOptions,
988
+ modal: false,
989
+ children: loanTermOptions.map((option) => /* @__PURE__ */ jsx(
990
+ ListboxOption,
991
+ {
992
+ value: option.value,
993
+ className: selectOption,
994
+ children: option.label
995
+ },
996
+ option.value
997
+ ))
998
+ }
999
+ )
1000
+ ] })
1001
+ ]
1002
+ }
1003
+ ),
1004
+ errors.loanTerm ? /* @__PURE__ */ jsx(
1005
+ "p",
1006
+ {
1007
+ id: "loanTerm-error",
1008
+ className: errorText,
1009
+ role: "alert",
1010
+ style: { marginTop: "8px" },
1011
+ children: errors.loanTerm
1012
+ }
1013
+ ) : null
1014
+ ] }),
1015
+ /* @__PURE__ */ jsx(
1016
+ "p",
1017
+ {
1018
+ className: autoAdvanceHint,
1019
+ style: { marginTop: "12px" },
1020
+ children: "Once both selections are made, you'll continue automatically."
1021
+ }
1022
+ )
1023
+ ]
668
1024
  },
669
- className: inputRowTwo,
670
- children: loanTermOptions.map((option) => {
671
- const checked = values.loanTerm === option.value;
672
- return /* @__PURE__ */ jsx(
673
- Radio,
674
- {
675
- value: option.value,
676
- className: `${radioOption} ${checked ? radioOptionChecked : ""}`,
677
- children: /* @__PURE__ */ jsx("span", { className: radioText, children: option.label })
678
- },
679
- option.value
680
- );
681
- })
682
- }
683
- ),
684
- /* @__PURE__ */ jsx("p", { className: autoAdvanceHint, children: "Your selection will continue automatically." }),
685
- renderAutoAdvanceStatus(),
686
- errors.loanTerm ? /* @__PURE__ */ jsx("p", { className: errorText, children: errors.loanTerm }) : null
1025
+ `loan-term-${values.amortizationType}`
1026
+ ) })
1027
+ ] }),
1028
+ renderAutoAdvanceStatus()
687
1029
  ] });
1030
+ }
688
1031
  case "finalDetails":
689
1032
  return /* @__PURE__ */ jsxs("div", { className: inputGrid, children: [
690
1033
  /* @__PURE__ */ jsxs(Field, { className: fieldBlock, children: [
691
- /* @__PURE__ */ jsx(Label, { className: fieldLabel, children: "Desired Rate" }),
1034
+ /* @__PURE__ */ jsxs(Label, { className: fieldLabel, children: [
1035
+ "Desired Rate",
1036
+ /* @__PURE__ */ jsx("sup", { children: "*" })
1037
+ ] }),
692
1038
  /* @__PURE__ */ jsx(
693
1039
  Input,
694
1040
  {
695
1041
  className: input,
696
1042
  type: "text",
697
1043
  inputMode: "decimal",
698
- placeholder: "6.25%",
1044
+ placeholder: "Enter your desired rate",
699
1045
  value: values.desiredRate ? `${values.desiredRate}%` : "",
700
- onChange: (e) => updateValue("desiredRate", formatPercentInput(e.target.value))
1046
+ onChange: (e) => updateValue("desiredRate", formatPercentInput(e.target.value)),
1047
+ "aria-describedby": errors.desiredRate ? "desiredRate-error" : void 0,
1048
+ "aria-invalid": !!errors.desiredRate
701
1049
  }
702
1050
  ),
703
- errors.desiredRate ? /* @__PURE__ */ jsx("p", { className: errorText, children: errors.desiredRate }) : null
1051
+ errors.desiredRate ? /* @__PURE__ */ jsx(
1052
+ "p",
1053
+ {
1054
+ id: "desiredRate-error",
1055
+ className: errorText,
1056
+ role: "alert",
1057
+ children: errors.desiredRate
1058
+ }
1059
+ ) : null
704
1060
  ] }),
705
1061
  /* @__PURE__ */ jsxs(Field, { className: fieldBlock, children: [
706
- /* @__PURE__ */ jsxs(Label, { className: fieldLabel, children: [
707
- "Discount Points",
708
- " ",
709
- /* @__PURE__ */ jsx("span", { className: optionalText, children: "(Optional)" })
710
- ] }),
1062
+ /* @__PURE__ */ jsx(Label, { className: fieldLabel, children: "Discount Points" }),
711
1063
  /* @__PURE__ */ jsx(
712
1064
  Input,
713
1065
  {
@@ -715,72 +1067,108 @@ function MortgageRateStep() {
715
1067
  type: "text",
716
1068
  inputMode: "decimal",
717
1069
  placeholder: "1.0",
718
- value: values.discountPoints,
719
- onChange: (e) => updateValue("discountPoints", e.target.value)
1070
+ value: values.discountPoints ? `${values.discountPoints}%` : "",
1071
+ onChange: (e) => updateValue(
1072
+ "discountPoints",
1073
+ formatPercentInput(e.target.value)
1074
+ ),
1075
+ "aria-describedby": errors.discountPoints ? "discountPoints-error" : void 0,
1076
+ "aria-invalid": !!errors.discountPoints
720
1077
  }
721
- )
722
- ] })
723
- ] });
724
- case "contact":
725
- return /* @__PURE__ */ jsxs("div", { className: inputGrid, style: { marginTop: "24px" }, children: [
726
- /* @__PURE__ */ jsxs("div", { className: inputRowTwo, children: [
727
- /* @__PURE__ */ jsxs(Field, { className: fieldBlock, children: [
728
- /* @__PURE__ */ jsx(Label, { className: fieldLabel, children: "First Name" }),
729
- /* @__PURE__ */ jsx(
730
- Input,
731
- {
732
- className: input,
733
- type: "text",
734
- placeholder: "Enter your first name",
735
- value: values.firstName,
736
- onChange: (e) => updateValue("firstName", e.target.value)
737
- }
738
- ),
739
- errors.firstName ? /* @__PURE__ */ jsx("p", { className: errorText, children: errors.firstName }) : null
740
- ] }),
741
- /* @__PURE__ */ jsxs(Field, { className: fieldBlock, children: [
742
- /* @__PURE__ */ jsx(Label, { className: fieldLabel, children: "Last Name" }),
743
- /* @__PURE__ */ jsx(
744
- Input,
745
- {
746
- className: input,
747
- type: "text",
748
- placeholder: "Enter your last name",
749
- value: values.lastName,
750
- onChange: (e) => updateValue("lastName", e.target.value)
751
- }
752
- ),
753
- errors.lastName ? /* @__PURE__ */ jsx("p", { className: errorText, children: errors.lastName }) : null
754
- ] })
755
- ] }),
756
- /* @__PURE__ */ jsxs(Field, { className: fieldBlock, children: [
757
- /* @__PURE__ */ jsx(Label, { className: fieldLabel, children: "Email" }),
758
- /* @__PURE__ */ jsx(
759
- Input,
1078
+ ),
1079
+ errors.discountPoints ? /* @__PURE__ */ jsx(
1080
+ "p",
760
1081
  {
761
- className: input,
762
- type: "email",
763
- placeholder: "Enter your email",
764
- value: values.email,
765
- onChange: (e) => updateValue("email", e.target.value)
1082
+ id: "discountPoints-error",
1083
+ className: errorText,
1084
+ role: "alert",
1085
+ children: errors.discountPoints
766
1086
  }
767
- ),
768
- errors.email ? /* @__PURE__ */ jsx("p", { className: errorText, children: errors.email }) : null
1087
+ ) : null
769
1088
  ] }),
770
1089
  /* @__PURE__ */ jsxs(Field, { className: fieldBlock, children: [
771
- /* @__PURE__ */ jsx(Label, { className: fieldLabel, children: "Phone Number" }),
772
- /* @__PURE__ */ jsx(
773
- Input,
1090
+ /* @__PURE__ */ jsxs(
1091
+ Listbox,
774
1092
  {
775
- className: input,
776
- type: "tel",
777
- inputMode: "tel",
778
- placeholder: "(XXX) XXX-XXXX",
779
- value: values.phoneNumber,
780
- onChange: (e) => updateValue("phoneNumber", formatPhoneInput(e.target.value))
1093
+ value: values.updateFrequency,
1094
+ onChange: (value) => updateValue("updateFrequency", value),
1095
+ children: [
1096
+ /* @__PURE__ */ jsx(Label, { className: fieldLabel, children: "How often would you like rate updates?" }),
1097
+ /* @__PURE__ */ jsxs("div", { className: selectWrap, style: { marginTop: "0px" }, children: [
1098
+ /* @__PURE__ */ jsx(
1099
+ ListboxButton,
1100
+ {
1101
+ className: selectButton,
1102
+ "aria-describedby": errors.updateFrequency ? "updateFrequency-error" : void 0,
1103
+ "aria-invalid": !!errors.updateFrequency,
1104
+ children: ({ open }) => /* @__PURE__ */ jsxs(Fragment, { children: [
1105
+ /* @__PURE__ */ jsx(
1106
+ "span",
1107
+ {
1108
+ className: values.updateFrequency ? "" : selectButtonPlaceholder,
1109
+ children: values.updateFrequency ? updateFrequencyOptions.find(
1110
+ (o) => o.value === values.updateFrequency
1111
+ )?.label : "Select update frequency"
1112
+ }
1113
+ ),
1114
+ /* @__PURE__ */ jsx(
1115
+ "svg",
1116
+ {
1117
+ className: selectChevron,
1118
+ "data-open": open,
1119
+ viewBox: "0 0 20 20",
1120
+ fill: "none",
1121
+ xmlns: "http://www.w3.org/2000/svg",
1122
+ "aria-hidden": "true",
1123
+ children: /* @__PURE__ */ jsx(
1124
+ "path",
1125
+ {
1126
+ d: "M5 7.5L10 12.5L15 7.5",
1127
+ stroke: "currentColor",
1128
+ strokeWidth: "2",
1129
+ strokeLinecap: "round",
1130
+ strokeLinejoin: "round"
1131
+ }
1132
+ )
1133
+ }
1134
+ )
1135
+ ] })
1136
+ }
1137
+ ),
1138
+ /* @__PURE__ */ jsx(
1139
+ ListboxOptions,
1140
+ {
1141
+ className: selectOptions,
1142
+ modal: false,
1143
+ children: updateFrequencyOptions.map((option) => /* @__PURE__ */ jsx(
1144
+ ListboxOption,
1145
+ {
1146
+ value: option.value,
1147
+ className: selectOption,
1148
+ children: option.label
1149
+ },
1150
+ option.value
1151
+ ))
1152
+ }
1153
+ )
1154
+ ] })
1155
+ ]
781
1156
  }
782
1157
  ),
783
- errors.phoneNumber ? /* @__PURE__ */ jsx("p", { className: errorText, children: errors.phoneNumber }) : null
1158
+ errors.updateFrequency ? /* @__PURE__ */ jsx(
1159
+ "p",
1160
+ {
1161
+ id: "updateFrequency-error",
1162
+ className: errorText,
1163
+ role: "alert",
1164
+ style: { marginTop: "8px" },
1165
+ children: errors.updateFrequency
1166
+ }
1167
+ ) : null
1168
+ ] }),
1169
+ /* @__PURE__ */ jsxs("p", { className: autoAdvanceHint, children: [
1170
+ /* @__PURE__ */ jsx("sup", { children: "*" }),
1171
+ "Minimum 2% and no more than 10%."
784
1172
  ] })
785
1173
  ] });
786
1174
  default:
@@ -803,7 +1191,7 @@ function MortgageRateStep() {
803
1191
  const continueButtonLabel = isCurrentStepAutoAdvanceStep && isAutoAdvancing ? "Continuing…" : currentStepIndex === totalSteps - 1 ? "Start Watching Rates" : "Continue";
804
1192
  const continueButtonDisabled = isCurrentStepAutoAdvanceStep && isAutoAdvancing;
805
1193
  return /* @__PURE__ */ jsxs("section", { className: section, children: [
806
- stepKey === "mortgageType" && /* @__PURE__ */ jsxs("div", { className: "text_center", children: [
1194
+ stepKey === "contact" && /* @__PURE__ */ jsxs("div", { className: "text_center", children: [
807
1195
  /* @__PURE__ */ jsxs("div", { className: "flex middle center", style: { gap: "16px" }, children: [
808
1196
  /* @__PURE__ */ jsx("h1", { className: mainTitle, children: "Rate Watch" }),
809
1197
  /* @__PURE__ */ jsx(AlertBellIcon, {})
@@ -813,24 +1201,43 @@ function MortgageRateStep() {
813
1201
  /* @__PURE__ */ jsxs("div", { className: formShell, children: [
814
1202
  /* @__PURE__ */ jsxs("div", { className: progressWrap, children: [
815
1203
  /* @__PURE__ */ jsxs("div", { className: progressRow, children: [
816
- /* @__PURE__ */ jsxs("span", { className: progressLabel, children: [
817
- "Step ",
818
- currentStepIndex + 1,
819
- " of ",
820
- totalSteps
821
- ] }),
1204
+ /* @__PURE__ */ jsxs(
1205
+ "span",
1206
+ {
1207
+ className: progressLabel,
1208
+ "aria-live": "polite",
1209
+ "aria-atomic": "true",
1210
+ children: [
1211
+ "Step ",
1212
+ currentStepIndex + 1,
1213
+ " of ",
1214
+ totalSteps
1215
+ ]
1216
+ }
1217
+ ),
822
1218
  /* @__PURE__ */ jsxs("span", { className: progressLabel, children: [
823
1219
  Math.round(progress),
824
1220
  "%"
825
1221
  ] })
826
1222
  ] }),
827
- /* @__PURE__ */ jsx("div", { className: progressTrack, children: /* @__PURE__ */ jsx(
1223
+ /* @__PURE__ */ jsx(
828
1224
  "div",
829
1225
  {
830
- className: progressFill,
831
- style: { width: `${progress}%` }
1226
+ className: progressTrack,
1227
+ role: "progressbar",
1228
+ "aria-valuenow": Math.round(progress),
1229
+ "aria-valuemin": 0,
1230
+ "aria-valuemax": 100,
1231
+ "aria-label": "Form completion progress",
1232
+ children: /* @__PURE__ */ jsx(
1233
+ "div",
1234
+ {
1235
+ className: progressFill,
1236
+ style: { width: `${progress}%` }
1237
+ }
1238
+ )
832
1239
  }
833
- ) })
1240
+ )
834
1241
  ] }),
835
1242
  /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: /* @__PURE__ */ jsxs(
836
1243
  motion.div,
@@ -852,6 +1259,8 @@ function MortgageRateStep() {
852
1259
  className: secondaryButton,
853
1260
  onClick: goBack,
854
1261
  disabled: currentStepIndex === 0,
1262
+ "aria-disabled": currentStepIndex === 0,
1263
+ "aria-label": currentStepIndex === 0 ? "Back button, disabled, you are on the first step" : "Back button, go to previous step",
855
1264
  children: "Back"
856
1265
  }
857
1266
  ),
@@ -862,21 +1271,19 @@ function MortgageRateStep() {
862
1271
  className: primaryButton,
863
1272
  onClick: goNext,
864
1273
  disabled: continueButtonDisabled,
1274
+ "aria-disabled": continueButtonDisabled,
1275
+ "aria-label": continueButtonDisabled ? `${continueButtonLabel} button, disabled, processing selection` : `${continueButtonLabel} button, go to next step`,
865
1276
  children: continueButtonLabel
866
1277
  }
867
1278
  )
868
1279
  ] }),
869
- stepKey === "mortgageType" && /* @__PURE__ */ jsxs("div", { className: disclosure, children: [
870
- /* @__PURE__ */ jsx("p", { className: disclosureTitle, children: "Important information" }),
871
- /* @__PURE__ */ jsx("p", { className: disclosureText, children: "By continuing, you agree to provide information so Axos can better understand your mortgage needs. Mortgage products are subject to underwriting and approval. Rates, terms, and product availability may vary based on credit profile, property type, occupancy, loan amount, and other factors. This form is for informational purposes only and is not a commitment to lend. NMLS #524995." })
872
- ] }),
873
1280
  stepKey === "contact" && /* @__PURE__ */ jsxs("div", { className: disclosure, children: [
874
1281
  /* @__PURE__ */ jsxs(
875
1282
  "p",
876
1283
  {
877
1284
  className: `${disclosureText} ${disclosureTextSm}`,
878
1285
  children: [
879
- 'By clicking "Start Watching Rates", you have agreed to these',
1286
+ "By continuing, you agree to these",
880
1287
  " ",
881
1288
  /* @__PURE__ */ jsx("a", { href: "https://assets.axos.com/o9ov1v03uwqk/6gQNHxCzLuaoCeivQk84N4/14ae33b36f550b3c776d2840be513fb9/SMS-and-MMS-Disclosure-Axos-Bank.pdf", children: "Terms and Conditions" }),
882
1289
  ", and you have agreed to receive automated SMS text messages, calls, and emails for any purpose including but not limited to marketing of products and services by Axos Bank. You understand and agree that such messages may be sent via Automatic Telephone Dialing System and/or artificial or pre-recorded voice, and that such consent is not a condition of receipt of any good or service. Mobile carrier messages and data rates may apply. You may opt out at any time."
@@ -891,6 +1298,10 @@ function MortgageRateStep() {
891
1298
  children: "If you do not wish to receive your rate quote by email or SMS text, please call 888-546-2364 to speak with a mortgage expert to receive a rate quote."
892
1299
  }
893
1300
  )
1301
+ ] }),
1302
+ stepKey === "finalDetails" && /* @__PURE__ */ jsxs("div", { className: disclosure, children: [
1303
+ /* @__PURE__ */ jsx("p", { className: disclosureTitle, children: "Important information" }),
1304
+ /* @__PURE__ */ jsx("p", { className: disclosureText, children: "Mortgage products are subject to underwriting and approval. Rates, terms, and product availability may vary based on credit profile, property type, occupancy, loan amount, and other factors. This form is for informational purposes only and is not a commitment to lend. NMLS #524995." })
894
1305
  ] })
895
1306
  ]
896
1307
  },