Merge branch 'develop' into feature/CASH-488
This commit is contained in:
commit
e22d664ddf
43 changed files with 1788 additions and 1289 deletions
|
|
@ -35,7 +35,7 @@ module.exports = {
|
||||||
testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"],
|
testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"],
|
||||||
coverageThreshold: {
|
coverageThreshold: {
|
||||||
global: {
|
global: {
|
||||||
statements: 74,
|
statements: 72,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// Uncomment this to avoid the massive amount of warnings we are getting for onSubmit and onInvalidSubmit
|
// Uncomment this to avoid the massive amount of warnings we are getting for onSubmit and onInvalidSubmit
|
||||||
|
|
|
||||||
6
src/assets/img/icons/calendar-icon-black.svg
Normal file
6
src/assets/img/icons/calendar-icon-black.svg
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M2.65567 6.44287H13.35" stroke="#0A0A0A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M10.4256 2V3.97449" stroke="#0A0A0A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M5.57864 2V3.97449" stroke="#0A0A0A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5427 2.94792H5.4625C3.70011 2.94792 2.60001 3.92933 2.60001 5.73323V11.1637C2.60001 12.9962 3.70011 14.0003 5.4625 14.0003H10.5375C12.3051 14.0003 13.4001 13.0137 13.4001 11.2091V5.73323C13.4052 3.92933 12.3103 2.94792 10.5427 2.94792Z" stroke="#0A0A0A" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 748 B |
6
src/assets/img/icons/calendar-icon-grey.svg
Normal file
6
src/assets/img/icons/calendar-icon-grey.svg
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M3.15567 6.44287H13.85" stroke="#4B4C4E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M10.9256 2V3.97449" stroke="#4B4C4E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M6.07864 2V3.97449" stroke="#4B4C4E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.0427 2.94791H5.9625C4.20011 2.94791 3.10001 3.92931 3.10001 5.73321V11.1637C3.10001 12.9962 4.20011 14.0003 5.9625 14.0003H11.0375C12.8051 14.0003 13.9001 13.0137 13.9001 11.2091V5.73321C13.9052 3.92931 12.8103 2.94791 11.0427 2.94791Z" stroke="#4B4C4E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 748 B |
4
src/assets/img/icons/info-circle.svg
Normal file
4
src/assets/img/icons/info-circle.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M3 12C3 16.9699 7.02908 21 12 21C16.9709 21 21 16.9699 21 12C21 7.02908 16.9709 3 12 3C7.02908 3 3 7.02908 3 12Z" stroke="#0070D1" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M12.0057 15.6932V11.3936M12 8.35426V8.29102" stroke="#0070D1" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 450 B |
6
src/assets/img/icons/payment-icon-green.svg
Normal file
6
src/assets/img/icons/payment-icon-green.svg
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M2 13.8143H13.5894" stroke="#00723E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.20013 2.88802L3.08796 7.00022C2.09364 7.99456 2.17407 9.32289 3.17099 10.3198L5.86013 13.0096C6.85706 14.0065 8.18213 14.0896 9.18039 13.092L13.2919 8.98042C14.2901 7.98222 14.2065 6.6571 13.2095 5.66018L10.5197 2.97039C9.52286 1.97348 8.19773 1.89046 7.20013 2.88802Z" stroke="#00723E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M3.6315 10.782L10.9809 3.4326" stroke="#00723E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M7.51559 11.2542L8.72786 10.042" stroke="#00723E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 800 B |
6
src/assets/img/icons/payment-icon-grey.svg
Normal file
6
src/assets/img/icons/payment-icon-grey.svg
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M2.5 13.8143H14.0894" stroke="#4B4C4E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.70014 2.88802L3.58797 7.00022C2.59365 7.99456 2.67408 9.32289 3.671 10.3198L6.36014 13.0096C7.35707 14.0065 8.68214 14.0896 9.68041 13.092L13.7919 8.98042C14.7901 7.98222 14.7065 6.6571 13.7095 5.66018L11.0197 2.97039C10.0229 1.97348 8.69774 1.89046 7.70014 2.88802Z" stroke="#4B4C4E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M4.13152 10.782L11.4809 3.43262" stroke="#4B4C4E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M8.01558 11.2543L9.22785 10.042" stroke="#4B4C4E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 802 B |
|
|
@ -3,6 +3,7 @@ const experimentUniverses = {
|
||||||
RECAL_PRICE_REMOVAL: "NextGen_RecalPriceRemoval",
|
RECAL_PRICE_REMOVAL: "NextGen_RecalPriceRemoval",
|
||||||
MSR: "MSR",
|
MSR: "MSR",
|
||||||
IGQ_SkipQuote: "NextGen_IGQSkipToInsurance",
|
IGQ_SkipQuote: "NextGen_IGQSkipToInsurance",
|
||||||
|
AFTERPAY_BREAKOUT_DISPLAY: "AfterpayBreakoutDisplay",
|
||||||
};
|
};
|
||||||
|
|
||||||
const experimentSettings = {
|
const experimentSettings = {
|
||||||
|
|
@ -24,6 +25,7 @@ const experimentSettings = {
|
||||||
PRICING_BY_DAY: "DisplayPricingByDay",
|
PRICING_BY_DAY: "DisplayPricingByDay",
|
||||||
FOSTER_LOVE: "DisplayFosterLove",
|
FOSTER_LOVE: "DisplayFosterLove",
|
||||||
SKIP_TO_INSURANCE: "SkipToInsurance",
|
SKIP_TO_INSURANCE: "SkipToInsurance",
|
||||||
|
DISPLAY_AFTERPAY_BREAKOUT_DISPLAY: "Display_AfterpayBreakoutDisplay",
|
||||||
};
|
};
|
||||||
|
|
||||||
const experimentTriggers = {
|
const experimentTriggers = {
|
||||||
|
|
|
||||||
26
src/constants/progress-bar-mapper.js
Normal file
26
src/constants/progress-bar-mapper.js
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
const pagePercentageMapper = {
|
||||||
|
vehicle: 4,
|
||||||
|
"vehicle-damage": 16,
|
||||||
|
estimate: 28,
|
||||||
|
"service-zip": 32,
|
||||||
|
"vin-lookup": 32,
|
||||||
|
"license-plate-lookup": 32,
|
||||||
|
"address-lookup": 32,
|
||||||
|
"address-vehicles": 36,
|
||||||
|
"part-questions": 40,
|
||||||
|
"molding-questions": 40,
|
||||||
|
"vehicle-parts": 40,
|
||||||
|
"capability-questions": 40,
|
||||||
|
quote: 48,
|
||||||
|
"insurance-company": 52,
|
||||||
|
"service-location": 60,
|
||||||
|
schedule: 72,
|
||||||
|
"customer-details": 84,
|
||||||
|
"payment-method": 92,
|
||||||
|
payment: 96,
|
||||||
|
confirmation: 100,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getProgressBarPercentage = (page) => {
|
||||||
|
return pagePercentageMapper[page] || 0;
|
||||||
|
};
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="date-picker text-center" :class="calendarViewDirection">
|
<div
|
||||||
|
class="date-picker text-center"
|
||||||
|
:class="`${calendarViewDirection} ${isPricingByDayClass} ${showPricingByDayClass}`">
|
||||||
<fieldset id="date-picker-fieldset" ref="datePickerFieldset">
|
<fieldset id="date-picker-fieldset" ref="datePickerFieldset">
|
||||||
<legend class="sr-only">Select a day and time</legend>
|
<legend class="sr-only">Select a day and time</legend>
|
||||||
<div
|
<div
|
||||||
|
|
@ -60,18 +62,24 @@
|
||||||
date.dateNum === 1 ? 'first-day-' + month.startDateDayIndex : '',
|
date.dateNum === 1 ? 'first-day-' + month.startDateDayIndex : '',
|
||||||
date.dayClasses,
|
date.dayClasses,
|
||||||
date.isSelectable ? 'selectable-day' : '',
|
date.isSelectable ? 'selectable-day' : '',
|
||||||
|
date.isPricingByDayUpchargeDay ? 'upch-day' : '',
|
||||||
]">
|
]">
|
||||||
<input
|
<input
|
||||||
:disabled="!date.isSelectable"
|
:disabled="!date.isSelectable"
|
||||||
type="radio"
|
type="radio"
|
||||||
name="day-of-month"
|
name="day-of-month"
|
||||||
v-model="selectedDate"
|
v-model="selectedDate"
|
||||||
@click="fireDateSelectedEvent"
|
@click="fireDateSelectedEvent($event, date)"
|
||||||
@keypress.enter="fireDateSelectedEvent"
|
@keypress.enter="fireDateSelectedEvent($event, date)"
|
||||||
:value="date.inputValue"
|
:value="date.inputValue"
|
||||||
:id="`${month.monthLabel}-${date.dateNum.toString()}`" />
|
:id="`${month.monthLabel}-${date.dateNum.toString()}`" />
|
||||||
<label :for="`${month.monthLabel}-${date.dateNum.toString()}`">
|
<label :for="`${month.monthLabel}-${date.dateNum.toString()}`">
|
||||||
<span>{{ date.dateNum.toString() }}</span>
|
<span>{{ date.dateNum.toString() }}</span>
|
||||||
|
<span
|
||||||
|
v-if="isPricingByDayExperiment && showPricingByDay && date.isSelectable"
|
||||||
|
class="price">
|
||||||
|
{{ date.priceString }}
|
||||||
|
</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -99,8 +107,17 @@
|
||||||
// Supporting files
|
// Supporting files
|
||||||
import loader from "@/ux-components/loader/loader";
|
import loader from "@/ux-components/loader/loader";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
import { TIMINGFUNC_MAP, BUFFER_OFFSET, MONTHS_OF_YEAR } from "./mixins/constants";
|
import {
|
||||||
import { selectableDaysOptions, requiredParameter, forceTwoDigitString } from "./mixins/helpers";
|
TIMINGFUNC_MAP,
|
||||||
|
BUFFER_OFFSET,
|
||||||
|
MONTHS_OF_YEAR,
|
||||||
|
DAYS_OF_WEEK,
|
||||||
|
} from "@/digital-components/date-picker/mixins/constants";
|
||||||
|
import {
|
||||||
|
selectableDaysOptions,
|
||||||
|
requiredParameter,
|
||||||
|
forceTwoDigitString,
|
||||||
|
} from "@/digital-components/date-picker/mixins/helpers";
|
||||||
import {
|
import {
|
||||||
convertDateToDateString,
|
convertDateToDateString,
|
||||||
convertDateStringToDate,
|
convertDateStringToDate,
|
||||||
|
|
@ -148,6 +165,10 @@ export default {
|
||||||
type: String,
|
type: String,
|
||||||
default: "",
|
default: "",
|
||||||
},
|
},
|
||||||
|
showPricingByDay: Boolean,
|
||||||
|
pricingByDayBasePrice: Number,
|
||||||
|
pricingByDayUpcharge: Number,
|
||||||
|
isPricingByDayExperiment: Boolean,
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const uuid = uuidv4();
|
const uuid = uuidv4();
|
||||||
|
|
@ -199,6 +220,12 @@ export default {
|
||||||
if (this.selectableDatesSetting === "custom") return "future";
|
if (this.selectableDatesSetting === "custom") return "future";
|
||||||
return "past";
|
return "past";
|
||||||
},
|
},
|
||||||
|
isPricingByDayClass() {
|
||||||
|
return this.isPricingByDayExperiment ? "pricing-by-day" : "";
|
||||||
|
},
|
||||||
|
showPricingByDayClass() {
|
||||||
|
return this.showPricingByDay ? "show-pricing-by-day" : "";
|
||||||
|
},
|
||||||
selectedDate: {
|
selectedDate: {
|
||||||
get() {
|
get() {
|
||||||
return this.modelValue;
|
return this.modelValue;
|
||||||
|
|
@ -212,12 +239,12 @@ export default {
|
||||||
initializeComponent(initialData) {
|
initializeComponent(initialData) {
|
||||||
this.setCalendarData(initialData);
|
this.setCalendarData(initialData);
|
||||||
},
|
},
|
||||||
fireDateSelectedEvent(event) {
|
fireDateSelectedEvent(event, date) {
|
||||||
// Ignore if arrow key selected radioButton
|
// Ignore if arrow key selected radioButton
|
||||||
if (event.screenX === 0 && event.screenY === 0) {
|
if (event.screenX === 0 && event.screenY === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.$emit("date-clicked");
|
this.$emit("date-clicked", date);
|
||||||
},
|
},
|
||||||
getWeekStartDate(dateString) {
|
getWeekStartDate(dateString) {
|
||||||
const date = convertDateStringToDate(dateString);
|
const date = convertDateStringToDate(dateString);
|
||||||
|
|
@ -440,6 +467,8 @@ export default {
|
||||||
initialViewEndDate: config.initialViewEndDate,
|
initialViewEndDate: config.initialViewEndDate,
|
||||||
hideSecondMonth: hideSecondMonth,
|
hideSecondMonth: hideSecondMonth,
|
||||||
preSelectedDate: config.preSelectedDate,
|
preSelectedDate: config.preSelectedDate,
|
||||||
|
pricingByDayBasePrice: config.pricingByDayBasePrice,
|
||||||
|
pricingByDayUpcharge: config.pricingByDayUpcharge,
|
||||||
};
|
};
|
||||||
if (direction === "future") {
|
if (direction === "future") {
|
||||||
// first 0, then 1
|
// first 0, then 1
|
||||||
|
|
@ -573,6 +602,17 @@ export default {
|
||||||
("0" + monthNum).slice(-2) +
|
("0" + monthNum).slice(-2) +
|
||||||
"-" +
|
"-" +
|
||||||
("0" + i).slice(-2);
|
("0" + i).slice(-2);
|
||||||
|
const dayIndex = convertDateStringToDate(dateString).getDay();
|
||||||
|
const dayObject = DAYS_OF_WEEK[dayIndex];
|
||||||
|
const isSelectable =
|
||||||
|
this.selectableDatesData.findIndex((date) => date.date === dateString) > -1
|
||||||
|
? true
|
||||||
|
: false;
|
||||||
|
const isPricingByDayUpchargeDay = dayObject.isPricingByDayUpchargeDay;
|
||||||
|
const displayPrice = isPricingByDayUpchargeDay
|
||||||
|
? options.pricingByDayBasePrice + options.pricingByDayUpcharge
|
||||||
|
: options.pricingByDayBasePrice;
|
||||||
|
const priceString = "$" + displayPrice;
|
||||||
|
|
||||||
if (offset === 0 && i === this.todayDateNum) {
|
if (offset === 0 && i === this.todayDateNum) {
|
||||||
dayClasses += " current-day";
|
dayClasses += " current-day";
|
||||||
|
|
@ -583,8 +623,8 @@ export default {
|
||||||
if (offset === 0 && i > this.todayDateNum && calendarViewDirection === "past") {
|
if (offset === 0 && i > this.todayDateNum && calendarViewDirection === "past") {
|
||||||
dayClasses += " unavailable-day";
|
dayClasses += " unavailable-day";
|
||||||
}
|
}
|
||||||
if (convertDateStringToDate(dateString).getDay() === 0) {
|
if (dayIndex === 0) {
|
||||||
dayClasses += " sunday";
|
dayClasses += " " + dayObject.cssClass;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
this.hideSomeDaysForInitialView &&
|
this.hideSomeDaysForInitialView &&
|
||||||
|
|
@ -598,10 +638,9 @@ export default {
|
||||||
dateNum: i,
|
dateNum: i,
|
||||||
dayClasses: dayClasses,
|
dayClasses: dayClasses,
|
||||||
inputValue: dateString,
|
inputValue: dateString,
|
||||||
isSelectable:
|
priceString: priceString,
|
||||||
this.selectableDatesData.findIndex((date) => date.date === dateString) > -1
|
isSelectable: isSelectable,
|
||||||
? true
|
isPricingByDayUpchargeDay: isPricingByDayUpchargeDay,
|
||||||
: false,
|
|
||||||
};
|
};
|
||||||
dates.push(dateObject);
|
dates.push(dateObject);
|
||||||
}
|
}
|
||||||
|
|
@ -897,7 +936,6 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:checked {
|
&:checked {
|
||||||
@include media-breakpoint-up(sm) {
|
@include media-breakpoint-up(sm) {
|
||||||
box-shadow: 0 0 0 4px transparent;
|
box-shadow: 0 0 0 4px transparent;
|
||||||
|
|
@ -924,9 +962,7 @@ export default {
|
||||||
|
|
||||||
&.selectable-day {
|
&.selectable-day {
|
||||||
label {
|
label {
|
||||||
color: $blue;
|
|
||||||
background-color: $blue-100;
|
background-color: $blue-100;
|
||||||
border: 1px solid $blue;
|
|
||||||
min-width: 2.5rem;
|
min-width: 2.5rem;
|
||||||
width: 2.5rem;
|
width: 2.5rem;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
|
@ -934,12 +970,11 @@ export default {
|
||||||
}
|
}
|
||||||
&.unavailable-day {
|
&.unavailable-day {
|
||||||
label {
|
label {
|
||||||
color: $gray-500;
|
|
||||||
background-color: $gray-100;
|
|
||||||
border: none;
|
border: none;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//NEEDED?
|
||||||
&.unavailable-day:not(&.sunday) {
|
&.unavailable-day:not(&.sunday) {
|
||||||
label {
|
label {
|
||||||
&::before {
|
&::before {
|
||||||
|
|
@ -949,7 +984,6 @@ export default {
|
||||||
left: -1rem;
|
left: -1rem;
|
||||||
width: 1rem;
|
width: 1rem;
|
||||||
height: 2.5rem;
|
height: 2.5rem;
|
||||||
background: $gray-100;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1056,7 +1090,107 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// pricing by day override styles
|
||||||
|
&.pricing-by-day {
|
||||||
|
.calendar-grid-container .grid-item {
|
||||||
|
margin: 0 0 0.15rem 0;
|
||||||
|
}
|
||||||
|
.month-year {
|
||||||
|
grid-area: 1 / 1 / 2 / 8;
|
||||||
|
}
|
||||||
|
.legend {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.radio-wrapper {
|
||||||
|
align-items: flex-start;
|
||||||
|
width: 100%;
|
||||||
|
height: 2.5rem;
|
||||||
|
color: $black;
|
||||||
|
|
||||||
|
input[type="radio"] {
|
||||||
|
&:focus-visible + label {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus + label,
|
||||||
|
&:checked:focus + label {
|
||||||
|
box-shadow: none;
|
||||||
|
background-color: $white;
|
||||||
|
color: $black;
|
||||||
|
}
|
||||||
|
&:checked + label {
|
||||||
|
background: $blue-100;
|
||||||
|
border: 2px solid $blue;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
color: $black;
|
||||||
|
span {
|
||||||
|
font-family: AvertaSemibold;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
flex-direction: column;
|
||||||
|
height: 2.5rem;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
line-height: 1.2;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0.25rem;
|
||||||
|
}
|
||||||
|
&.selectable-day {
|
||||||
|
label {
|
||||||
|
color: $black;
|
||||||
|
background-color: $blue-100;
|
||||||
|
min-width: 2.5rem;
|
||||||
|
width: 2.5rem;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.current-day {
|
||||||
|
label:after {
|
||||||
|
margin-top: 0.2rem;
|
||||||
|
position: relative;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.show-pricing-by-day {
|
||||||
|
.radio-wrapper {
|
||||||
|
&.selectable-day label span {
|
||||||
|
text-decoration: none;
|
||||||
|
color: $black;
|
||||||
|
font-weight: 400;
|
||||||
|
font-family: "AvertaSemibold";
|
||||||
|
|
||||||
|
&.price {
|
||||||
|
font-family: "AvertaRegular";
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.upch-day label span.price {
|
||||||
|
color: $gray-600;
|
||||||
|
font-weight: 400;
|
||||||
|
font-family: $font-family-sans-serif;
|
||||||
|
}
|
||||||
|
input[type="radio"] {
|
||||||
|
&:focus + label,
|
||||||
|
&:checked:focus + label {
|
||||||
|
color: $gray-600;
|
||||||
|
}
|
||||||
|
&:checked + label {
|
||||||
|
color: $blue;
|
||||||
|
span {
|
||||||
|
font-weight: 400;
|
||||||
|
font-family: "AvertaSemibold";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-link {
|
.btn-link {
|
||||||
display: block;
|
display: block;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
The code below is generated from salesforce but modified in the following ways:
|
||||||
|
* Only the javascript inside the second <script> tag in the provided salesforce code is copy pasted
|
||||||
|
* CSS is not copied in
|
||||||
|
* First <script> tag loading in the file from the CDN is not copied in
|
||||||
|
* The only custom code added here is `let embedded_svc = window.embedded_svc` in the first line of the `initESW` function
|
||||||
|
and the wrapper exported function `initializeSalesforceWebChatForDev`
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function initializeSalesforceWebchatForDev() {
|
||||||
|
var initESW = function (gslbBaseURL) {
|
||||||
|
let embedded_svc = window.embedded_svc;
|
||||||
|
embedded_svc.settings.displayHelpButton = true; //Or false
|
||||||
|
embedded_svc.settings.language = ""; //For example, enter 'en' or 'en-US'
|
||||||
|
|
||||||
|
//embedded_svc.settings.defaultMinimizedText = '...'; //(Defaults to Chat with an Expert)
|
||||||
|
//embedded_svc.settings.disabledMinimizedText = '...'; //(Defaults to Agent Offline)
|
||||||
|
|
||||||
|
//embedded_svc.settings.loadingText = ''; //(Defaults to Loading)
|
||||||
|
//embedded_svc.settings.storageDomain = 'yourdomain.com'; //(Sets the domain for your deployment so that visitors can navigate subdomains during a chat session)
|
||||||
|
|
||||||
|
// Settings for Chat
|
||||||
|
//embedded_svc.settings.directToButtonRouting = function(prechatFormData) {
|
||||||
|
// Dynamically changes the button ID based on what the visitor enters in the pre-chat form.
|
||||||
|
// Returns a valid button ID.
|
||||||
|
//};
|
||||||
|
//embedded_svc.settings.prepopulatedPrechatFields = {}; //Sets the auto-population of pre-chat form fields
|
||||||
|
//embedded_svc.settings.fallbackRouting = []; //An array of button IDs, user IDs, or userId_buttonId
|
||||||
|
//embedded_svc.settings.offlineSupportMinimizedText = '...'; //(Defaults to Contact Us)
|
||||||
|
|
||||||
|
embedded_svc.settings.enabledFeatures = ["LiveAgent"];
|
||||||
|
embedded_svc.settings.entryFeature = "LiveAgent";
|
||||||
|
|
||||||
|
embedded_svc.init(
|
||||||
|
"https://safelite2--dev.sandbox.my.salesforce.com",
|
||||||
|
"https://safelite2--dev.sandbox.my.salesforce-sites.com/chat",
|
||||||
|
gslbBaseURL,
|
||||||
|
"00D6s0000008r6A",
|
||||||
|
"Sales_Central_Snap_in",
|
||||||
|
{
|
||||||
|
baseLiveAgentContentURL:
|
||||||
|
"https://c.la12s-core1.sfdc-8tgtt5.salesforceliveagent.com/content",
|
||||||
|
deploymentId: "572Wr0000017xKX",
|
||||||
|
buttonId: "573Wr000000CpgH",
|
||||||
|
baseLiveAgentURL: "https://d.la12s-core1.sfdc-8tgtt5.salesforceliveagent.com/chat",
|
||||||
|
eswLiveAgentDevName:
|
||||||
|
"EmbeddedServiceLiveAgent_Parent04IWr000003yKq9MAE_1961681659a",
|
||||||
|
isOfflineSupportEnabled: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!window.embedded_svc) {
|
||||||
|
var s = document.createElement("script");
|
||||||
|
s.setAttribute(
|
||||||
|
"src",
|
||||||
|
"https://safelite2--dev.sandbox.my.salesforce.com/embeddedservice/5.0/esw.min.js"
|
||||||
|
);
|
||||||
|
s.onload = function () {
|
||||||
|
initESW(null);
|
||||||
|
};
|
||||||
|
document.body.appendChild(s);
|
||||||
|
} else {
|
||||||
|
initESW("https://service.force.com");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
The code below is generated from salesforce but modified in the following ways:
|
||||||
|
* Only the javascript inside the second <script> tag in the provided salesforce code is copy pasted
|
||||||
|
* CSS is not copied in
|
||||||
|
* First <script> tag loading in the file from the CDN is not copied in
|
||||||
|
* The only custom code added here is `let embedded_svc = window.embedded_svc` in the first line of the `initESW` function
|
||||||
|
and the wrapper exported function `initializeSalesforceWebChatForProd`
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function initializeSalesforceWebchatForProd(embedded_svc) {
|
||||||
|
var initESW = function (gslbBaseURL) {
|
||||||
|
let embedded_svc = window.embedded_svc;
|
||||||
|
embedded_svc.settings.displayHelpButton = true; //Or false
|
||||||
|
embedded_svc.settings.language = ""; //For example, enter 'en' or 'en-US'
|
||||||
|
|
||||||
|
//embedded_svc.settings.defaultMinimizedText = '...'; //(Defaults to Chat with an Expert)
|
||||||
|
//embedded_svc.settings.disabledMinimizedText = '...'; //(Defaults to Agent Offline)
|
||||||
|
|
||||||
|
//embedded_svc.settings.loadingText = ''; //(Defaults to Loading)
|
||||||
|
//embedded_svc.settings.storageDomain = 'yourdomain.com'; //(Sets the domain for your deployment so that visitors can navigate subdomains during a chat session)
|
||||||
|
|
||||||
|
// Settings for Chat
|
||||||
|
//embedded_svc.settings.directToButtonRouting = function(prechatFormData) {
|
||||||
|
// Dynamically changes the button ID based on what the visitor enters in the pre-chat form.
|
||||||
|
// Returns a valid button ID.
|
||||||
|
//};
|
||||||
|
//embedded_svc.settings.prepopulatedPrechatFields = {}; //Sets the auto-population of pre-chat form fields
|
||||||
|
//embedded_svc.settings.fallbackRouting = []; //An array of button IDs, user IDs, or userId_buttonId
|
||||||
|
//embedded_svc.settings.offlineSupportMinimizedText = '...'; //(Defaults to Contact Us)
|
||||||
|
|
||||||
|
embedded_svc.settings.enabledFeatures = ["LiveAgent"];
|
||||||
|
embedded_svc.settings.entryFeature = "LiveAgent";
|
||||||
|
|
||||||
|
embedded_svc.init(
|
||||||
|
"https://safelite2.my.salesforce.com",
|
||||||
|
"https://safelite2.my.salesforce-sites.com/chat",
|
||||||
|
gslbBaseURL,
|
||||||
|
"00D3t000004nvsT",
|
||||||
|
"Sales_Central_Snap_in",
|
||||||
|
{
|
||||||
|
baseLiveAgentContentURL:
|
||||||
|
"https://c.la13-core2.sfdc-lywfpd.salesforceliveagent.com/content",
|
||||||
|
deploymentId: "5723t0000008wWB",
|
||||||
|
buttonId: "5733t000000D90z",
|
||||||
|
baseLiveAgentURL: "https://d.la13-core2.sfdc-lywfpd.salesforceliveagent.com/chat",
|
||||||
|
eswLiveAgentDevName:
|
||||||
|
"EmbeddedServiceLiveAgent_Parent04I3t000000Cq0SEAS_188de1871ea",
|
||||||
|
isOfflineSupportEnabled: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!window.embedded_svc) {
|
||||||
|
var s = document.createElement("script");
|
||||||
|
s.setAttribute("src", "https://safelite2.my.salesforce.com/embeddedservice/5.0/esw.min.js");
|
||||||
|
s.onload = function () {
|
||||||
|
initESW(null);
|
||||||
|
};
|
||||||
|
document.body.appendChild(s);
|
||||||
|
} else {
|
||||||
|
initESW("https://service.force.com");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
The code below is generated from salesforce but modified in the following ways:
|
||||||
|
* Only the javascript inside the second <script> tag in the provided salesforce code is copy pasted
|
||||||
|
* CSS is not copied in
|
||||||
|
* First <script> tag loading in the file from the CDN is not copied in
|
||||||
|
* The only custom code added here is `let embedded_svc = window.embedded_svc` in the first line of the `initESW` function
|
||||||
|
and the wrapper exported function `initializeSalesforceWebChatForQa`
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function initializeSalesforceWebchatForQa() {
|
||||||
|
var initESW = function (gslbBaseURL) {
|
||||||
|
let embedded_svc = window.embedded_svc;
|
||||||
|
|
||||||
|
embedded_svc.settings.displayHelpButton = true; //Or false
|
||||||
|
embedded_svc.settings.language = ""; //For example, enter 'en' or 'en-US'
|
||||||
|
|
||||||
|
//embedded_svc.settings.defaultMinimizedText = '...'; //(Defaults to Chat with an Expert)
|
||||||
|
//embedded_svc.settings.disabledMinimizedText = '...'; //(Defaults to Agent Offline)
|
||||||
|
|
||||||
|
//embedded_svc.settings.loadingText = ''; //(Defaults to Loading)
|
||||||
|
//embedded_svc.settings.storageDomain = 'yourdomain.com'; //(Sets the domain for your deployment so that visitors can navigate subdomains during a chat session)
|
||||||
|
|
||||||
|
// Settings for Chat
|
||||||
|
//embedded_svc.settings.directToButtonRouting = function(prechatFormData) {
|
||||||
|
// Dynamically changes the button ID based on what the visitor enters in the pre-chat form.
|
||||||
|
// Returns a valid button ID.
|
||||||
|
//};
|
||||||
|
//embedded_svc.settings.prepopulatedPrechatFields = {}; //Sets the auto-population of pre-chat form fields
|
||||||
|
//embedded_svc.settings.fallbackRouting = []; //An array of button IDs, user IDs, or userId_buttonId
|
||||||
|
//embedded_svc.settings.offlineSupportMinimizedText = '...'; //(Defaults to Contact Us)
|
||||||
|
|
||||||
|
embedded_svc.settings.enabledFeatures = ["LiveAgent"];
|
||||||
|
embedded_svc.settings.entryFeature = "LiveAgent";
|
||||||
|
|
||||||
|
embedded_svc.init(
|
||||||
|
"https://safelite2--safeliteua.sandbox.my.salesforce.com",
|
||||||
|
"https://safelite2--safeliteua.sandbox.my.salesforce-sites.com/chat",
|
||||||
|
gslbBaseURL,
|
||||||
|
"00DDJ000000HRqO",
|
||||||
|
"Sales_Central_Snap_in",
|
||||||
|
{
|
||||||
|
baseLiveAgentContentURL:
|
||||||
|
"https://c.la12s-core1.sfdc-8tgtt5.salesforceliveagent.com/content",
|
||||||
|
deploymentId: "572DJ00000001j3",
|
||||||
|
buttonId: "573DJ00000001ur",
|
||||||
|
baseLiveAgentURL: "https://d.la12s-core1.sfdc-8tgtt5.salesforceliveagent.com/chat",
|
||||||
|
eswLiveAgentDevName:
|
||||||
|
"EmbeddedServiceLiveAgent_Parent04IDJ0000008OZk2AM_1883057c9a6",
|
||||||
|
isOfflineSupportEnabled: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!window.embedded_svc) {
|
||||||
|
var s = document.createElement("script");
|
||||||
|
s.setAttribute(
|
||||||
|
"src",
|
||||||
|
"https://safelite2--safeliteua.sandbox.my.salesforce.com/embeddedservice/5.0/esw.min.js"
|
||||||
|
);
|
||||||
|
s.onload = function () {
|
||||||
|
initESW(null);
|
||||||
|
};
|
||||||
|
document.body.appendChild(s);
|
||||||
|
} else {
|
||||||
|
initESW("https://service.force.com");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
<template>
|
||||||
|
<span class="salesforce-webchat"></span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Supporting files
|
||||||
|
import { initializeSalesforceWebchatForDev } from "./salesforce-helper-dev";
|
||||||
|
import { initializeSalesforceWebchatForQa } from "./salesforce-helper-qa";
|
||||||
|
import { initializeSalesforceWebchatForProd } from "./salesforce-helper-prod";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "salesforceWebchat",
|
||||||
|
data() {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
hideSalesforceWebchatLaunchButton: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
// Load in script from salesforce CDN
|
||||||
|
const script = document.createElement("script");
|
||||||
|
script.src = "https://service.force.com/embeddedservice/5.0/esw.min.js";
|
||||||
|
script.onload = () => {
|
||||||
|
switch (process.env.VUE_APP_CURRENT_ENVIRONMENT) {
|
||||||
|
case "Prod":
|
||||||
|
initializeSalesforceWebchatForProd();
|
||||||
|
break;
|
||||||
|
case "QA":
|
||||||
|
case "SysTest":
|
||||||
|
initializeSalesforceWebchatForQa();
|
||||||
|
break;
|
||||||
|
case "Dev":
|
||||||
|
case "Localhost":
|
||||||
|
initializeSalesforceWebchatForDev();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.body.appendChild(script);
|
||||||
|
},
|
||||||
|
methods: {},
|
||||||
|
computed: {},
|
||||||
|
components: {},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
.embeddedServiceHelpButton .helpButton .uiButton {
|
||||||
|
background-color: #167cac;
|
||||||
|
font-family: "Arial", sans-serif;
|
||||||
|
}
|
||||||
|
.embeddedServiceHelpButton .helpButton .uiButton:focus {
|
||||||
|
outline: 1px solid #167cac;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="donation-block">
|
<div class="donation-block">
|
||||||
<form ref="donationForm" id="donation-form" @submit.prevent="handleDonationAction">
|
<form ref="donationForm" id="donation-form" @submit.prevent>
|
||||||
<div class="row g-0">
|
<div class="row g-0">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<p class="headline text-center" v-html="DonationHeadline"></p>
|
<p class="headline text-center" v-html="DonationHeadline"></p>
|
||||||
|
|
@ -52,9 +52,12 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<button
|
<buttonMain
|
||||||
class="btn btn-secondary w-100 mb-2"
|
ref="buttonMain"
|
||||||
v-html="DonationAddButtonCopy"></button>
|
:buttonText="DonationAddButtonCopy"
|
||||||
|
loaderColor="blue"
|
||||||
|
class="w-100 mb-2 custom-secondary"
|
||||||
|
@click-event="handleDonationAction" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -86,6 +89,7 @@
|
||||||
<script>
|
<script>
|
||||||
import textLink from "@/ux-components/text-link/text-link";
|
import textLink from "@/ux-components/text-link/text-link";
|
||||||
import alert from "@/ux-components/alert/alert";
|
import alert from "@/ux-components/alert/alert";
|
||||||
|
import buttonMain from "@/ux-components/button-main/button-main";
|
||||||
export default {
|
export default {
|
||||||
name: "donationBlock",
|
name: "donationBlock",
|
||||||
props: {
|
props: {
|
||||||
|
|
@ -131,13 +135,20 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleDonationAction(submitEvent) {
|
handleDonationAction() {
|
||||||
this.selectedValue = submitEvent.target.elements.amount.value;
|
const selectedAmount = this.$refs.donationForm.elements.amount?.value;
|
||||||
|
if (selectedAmount) {
|
||||||
|
this.selectedValue = selectedAmount;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeLoader() {
|
||||||
|
this.$refs.buttonMain.removeLoader();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
textLink,
|
textLink,
|
||||||
alert,
|
alert,
|
||||||
|
buttonMain,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -280,5 +291,12 @@ export default {
|
||||||
left: -0.25rem;
|
left: -0.25rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.custom-secondary {
|
||||||
|
&:hover {
|
||||||
|
color: $blue;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid $blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
476
src/experiment-components/service-package-radio-for-afterpay.vue
Normal file
476
src/experiment-components/service-package-radio-for-afterpay.vue
Normal file
|
|
@ -0,0 +1,476 @@
|
||||||
|
<template>
|
||||||
|
<baseInputButton v-bind="$props" @buttonClicked="handleAnswerChange" v-model="selectedValue">
|
||||||
|
<div
|
||||||
|
class="package-label"
|
||||||
|
:class="[
|
||||||
|
this.buttonLabelSubCopy ? 'has-subheader' : '',
|
||||||
|
this.additionalButtonData.isInsuranceSelected ? 'is-insurance' : '',
|
||||||
|
this.hasPackageDiscount() ? 'has-package-discount' : '',
|
||||||
|
]"
|
||||||
|
for="testradio">
|
||||||
|
<div class="package-specs">
|
||||||
|
<div>
|
||||||
|
<p class="m-0">
|
||||||
|
<span v-html="this.buttonLabel"></span>
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
class="sub-label m-0"
|
||||||
|
v-if="this.buttonLabelSubCopy"
|
||||||
|
v-html="this.buttonLabelSubCopy"></p>
|
||||||
|
</div>
|
||||||
|
<div class="hide-when-closed">
|
||||||
|
<!-- Parse the body text containing <ul> with logic -->
|
||||||
|
<ul>
|
||||||
|
<li v-for="listItem in this.arrayOfListItemsFromBodyText" :key="listItem">
|
||||||
|
<!-- no textLink -->
|
||||||
|
<span
|
||||||
|
v-if="!doesCopyContainTextLink(listItem)"
|
||||||
|
v-html="listItem"></span>
|
||||||
|
<!-- with textLink -->
|
||||||
|
<template
|
||||||
|
v-else
|
||||||
|
v-for="copy in splitCopyOnCMSPlaceHolder(listItem)"
|
||||||
|
:key="copy">
|
||||||
|
<span v-if="!doesCopyContainTextLink(copy)" v-html="copy"></span>
|
||||||
|
<span v-else>
|
||||||
|
<textLink
|
||||||
|
linkType="text"
|
||||||
|
:text="getRouterLinkDisplayTextFromCopy(copy)"
|
||||||
|
href="#!"
|
||||||
|
@click-event="
|
||||||
|
$emit('buttonEvent', {
|
||||||
|
eventName: 'openModal',
|
||||||
|
args: getRouterLinkRouteFromCopy(copy),
|
||||||
|
})
|
||||||
|
"
|
||||||
|
:data-bs-target="'#' + getRouterLinkRouteFromCopy(copy)" />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<!-- End of body text parsing -->
|
||||||
|
<div
|
||||||
|
class="package-footer fw-bold caption ms-n4 mt-4"
|
||||||
|
v-if="this.buttonFooterCopy"
|
||||||
|
v-html="this.buttonFooterCopy"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="hasPackageDiscount()" class="special-save-box">
|
||||||
|
<span v-html="this.additionalButtonData.Text"></span>
|
||||||
|
</div>
|
||||||
|
<span class="pricing-info">
|
||||||
|
<div
|
||||||
|
class="insurance-pricing"
|
||||||
|
v-if="this.additionalButtonData.isInsuranceSelected">
|
||||||
|
<span
|
||||||
|
class="strikethrough-price"
|
||||||
|
v-if="shouldDisplayStrikeThroughPrice"
|
||||||
|
v-html="this.additionalButtonData.strikeThroughPrice"></span>
|
||||||
|
<span v-html="this.buttonAuxillaryCopy"></span>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<span v-html="afterpayPrice()"></span>
|
||||||
|
<span class="afterpay-breakout">
|
||||||
|
<span>in 4 interest-free payments</span>
|
||||||
|
<div>
|
||||||
|
or
|
||||||
|
<span v-html="truncatedSinglePayment()"></span>
|
||||||
|
<span
|
||||||
|
class="strikethrough-price"
|
||||||
|
v-if="shouldDisplayStrikeThroughPrice"
|
||||||
|
v-html="this.additionalButtonData.strikeThroughPrice"></span>
|
||||||
|
in single payment
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</baseInputButton>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import baseInputButton from "@/digital-components/base-input-button/base-input-button";
|
||||||
|
import textLink from "@/ux-components/text-link/text-link";
|
||||||
|
import inputButtonWrapperMixin from "@/mixins/input-button-wrapper-mixin";
|
||||||
|
import {
|
||||||
|
getRouterLinkDisplayTextFromCopy,
|
||||||
|
getRouterLinkRouteFromCopy,
|
||||||
|
splitCopyOnCMSPlaceHolder,
|
||||||
|
doesCopyContainTextLink,
|
||||||
|
} from "@/helpers/cms-content-helper";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "servicePackageRadio",
|
||||||
|
mixins: [inputButtonWrapperMixin],
|
||||||
|
components: {
|
||||||
|
baseInputButton,
|
||||||
|
textLink,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
arrayOfListItemsFromBodyText() {
|
||||||
|
return this.getArrayOfListItemsFromRawCmsCopy(this.buttonBodyCopy);
|
||||||
|
},
|
||||||
|
shouldDisplayStrikeThroughPrice() {
|
||||||
|
const displayPrice = this.buttonAuxillaryCopy.substring(
|
||||||
|
this.buttonAuxillaryCopy.indexOf("$")
|
||||||
|
);
|
||||||
|
return displayPrice != this.additionalButtonData.strikeThroughPrice;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getRouterLinkDisplayTextFromCopy,
|
||||||
|
getRouterLinkRouteFromCopy,
|
||||||
|
splitCopyOnCMSPlaceHolder,
|
||||||
|
doesCopyContainTextLink,
|
||||||
|
stripUlTagFromCopy(copy) {
|
||||||
|
const regex = /(?:<ul(?:.*?)>)|(?:<\/ul>)/g;
|
||||||
|
return copy.replace(regex, "");
|
||||||
|
},
|
||||||
|
getArrayOfListItemsFromRawCmsCopy(copy) {
|
||||||
|
const withoutUlTags = this.stripUlTagFromCopy(copy);
|
||||||
|
return withoutUlTags
|
||||||
|
.split(/(?:<li(?:.*?)>)|(?:<\/li>)/g)
|
||||||
|
.filter((lineItem) => lineItem);
|
||||||
|
},
|
||||||
|
afterpayPrice() {
|
||||||
|
const decimalPrice = parseFloat(this.buttonAuxillaryCopy.replace("$", ""));
|
||||||
|
return "$" + (decimalPrice / 4).toFixed(2);
|
||||||
|
},
|
||||||
|
truncatedSinglePayment() {
|
||||||
|
return "$" + Math.trunc(this.buttonAuxillaryCopy.replace("$", ""));
|
||||||
|
},
|
||||||
|
hasPackageDiscount() {
|
||||||
|
return (
|
||||||
|
this.additionalButtonData.servicePackageDiscount && this.additionalButtonData.Text
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.package-main {
|
||||||
|
.package-wrapper {
|
||||||
|
margin: 1rem 0;
|
||||||
|
&:first-child {
|
||||||
|
margin: 1rem 0 0 0;
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
margin: 1rem 0 0 0;
|
||||||
|
}
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
margin: 1rem 0.5rem 0 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
&:first-child {
|
||||||
|
margin: 1rem 0.5rem 0 0;
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
margin: 1rem 0 0 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="radio"] {
|
||||||
|
opacity: 0;
|
||||||
|
position: absolute;
|
||||||
|
left: -9999px;
|
||||||
|
|
||||||
|
+ .package-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.75rem;
|
||||||
|
border: 1px solid $gray-300;
|
||||||
|
box-shadow:
|
||||||
|
0px 4px 8px -4px rgba(0, 0, 0, 0.15),
|
||||||
|
0px 4px 24px -8px rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
overflow: hidden;
|
||||||
|
min-height: 52px;
|
||||||
|
max-height: 126px;
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
max-height: 500px;
|
||||||
|
padding-bottom: 4.75rem;
|
||||||
|
}
|
||||||
|
@include media-breakpoint-down(md) {
|
||||||
|
padding-bottom: 4rem;
|
||||||
|
}
|
||||||
|
transition: max-height 0.25s ease-in;
|
||||||
|
|
||||||
|
&.is-insurance {
|
||||||
|
padding-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.has-subheader {
|
||||||
|
& > .package-specs {
|
||||||
|
& > div:first-of-type {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.has-package-discount {
|
||||||
|
min-height: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: "";
|
||||||
|
position: relative;
|
||||||
|
top: 5px;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 1px solid $gray-500;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
min-width: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: 15px;
|
||||||
|
top: 20px;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
min-width: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
+ .package-label {
|
||||||
|
&:before {
|
||||||
|
border: 1px solid #8e9292;
|
||||||
|
box-shadow:
|
||||||
|
0px 0px 0px 4px #9fcee6,
|
||||||
|
0px 1px 4px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:checked {
|
||||||
|
+ .package-label {
|
||||||
|
.package-specs {
|
||||||
|
max-height: 1000px;
|
||||||
|
|
||||||
|
.sub-label {
|
||||||
|
background-color: $green-200;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+ .package-label {
|
||||||
|
.package-specs {
|
||||||
|
.hide-when-closed {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+ .package-label {
|
||||||
|
background-color: $blue-100;
|
||||||
|
border: 1px solid $blue;
|
||||||
|
max-height: 500px;
|
||||||
|
.special-save-box {
|
||||||
|
background-color: $green-200;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+ .package-label {
|
||||||
|
&:before {
|
||||||
|
box-shadow: 0px 0px 0px 1px $blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+ .package-label {
|
||||||
|
&:after {
|
||||||
|
background: $blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
+ .package-label {
|
||||||
|
&:before {
|
||||||
|
border: 2px solid $blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.package-footer {
|
||||||
|
color: $red;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.package-specs {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
transition: all 0.5s ease;
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
max-height: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-weight: 600;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
&.sub-label {
|
||||||
|
color: $green;
|
||||||
|
background-color: $green-100;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
&.service-package-discount {
|
||||||
|
color: $green;
|
||||||
|
text-align: end;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
align-self: flex-start;
|
||||||
|
div.strikethrough-discount-price {
|
||||||
|
text-decoration: line-through;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
line-height: 20px;
|
||||||
|
color: $gray-550;
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div.discount-price {
|
||||||
|
color: $green;
|
||||||
|
}
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0.75rem;
|
||||||
|
left: 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
margin: 1rem 0 0 -5px;
|
||||||
|
padding: 0;
|
||||||
|
li {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.714;
|
||||||
|
color: $gray-550;
|
||||||
|
a {
|
||||||
|
line-height: 1.714;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideaway {
|
||||||
|
from {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translateY(40px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hide-when-closed {
|
||||||
|
display: none;
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.discount-text {
|
||||||
|
height: 2rem;
|
||||||
|
display: flex;
|
||||||
|
border-top-right-radius: 0.5rem;
|
||||||
|
border-top-left-radius: 0.5rem;
|
||||||
|
background-color: $green;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
color: white;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0.75px;
|
||||||
|
}
|
||||||
|
.special-save-box {
|
||||||
|
background-color: $green-100;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
margin: 0.5rem auto 1rem -1.25rem;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
span {
|
||||||
|
color: $green;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.pricing-info {
|
||||||
|
color: $green;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0.75rem;
|
||||||
|
left: 0;
|
||||||
|
width: calc(100% - 1.25rem);
|
||||||
|
margin-left: 1.25rem;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 20px;
|
||||||
|
|
||||||
|
span.strikethrough-price {
|
||||||
|
text-decoration: line-through;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 400;
|
||||||
|
color: $gray-550;
|
||||||
|
margin-left: 0.25rem;
|
||||||
|
}
|
||||||
|
span.afterpay-breakout {
|
||||||
|
color: $gray-550;
|
||||||
|
font-weight: initial;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
|
||||||
|
& > span {
|
||||||
|
margin-left: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
font-size: 0.625rem;
|
||||||
|
|
||||||
|
& > span:first-of-type {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -32,17 +32,13 @@
|
||||||
linkType="navigation"
|
linkType="navigation"
|
||||||
text="Cookie preferences"
|
text="Cookie preferences"
|
||||||
href="javascript:OneTrust.ToggleInfoDisplay();" />
|
href="javascript:OneTrust.ToggleInfoDisplay();" />
|
||||||
<textLink
|
|
||||||
linkType="newWindowLink"
|
|
||||||
text="Warranty"
|
|
||||||
href="//www.safelite.com/national-lifetime-warranty"
|
|
||||||
target="_blank" />
|
|
||||||
<textLink
|
<textLink
|
||||||
linkType="newWindowLink"
|
linkType="newWindowLink"
|
||||||
text="Notice at collection"
|
text="Notice at collection"
|
||||||
href="https://www.safelite.com/ccpa-privacy-policy"
|
href="https://www.safelite.com/ccpa-privacy-policy"
|
||||||
target="_blank" />
|
target="_blank" />
|
||||||
</div>
|
</div>
|
||||||
|
<p class="mx-3 my-0 pb-1">© 2025 Safelite Group</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -64,8 +60,9 @@ export default {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
align-items: end;
|
align-items: center;
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
|
padding: 0 0 2rem 0;
|
||||||
|
|
||||||
.skyline {
|
.skyline {
|
||||||
height: 96px;
|
height: 96px;
|
||||||
|
|
@ -74,22 +71,34 @@ export default {
|
||||||
height: 48px;
|
height: 48px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
font-size: 1rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.footer-inner-wrapper {
|
.footer-inner-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
border-top: 1px solid $gray;
|
border-top: 1px solid $gray;
|
||||||
padding: 2rem 0;
|
padding: 2rem 0 0 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
color: $gray-600;
|
||||||
|
|
||||||
:deep(a) {
|
:deep(a) {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
&.new-window-link {
|
&.new-window-link {
|
||||||
margin: 0 0.75rem;
|
margin: 0 0.75rem;
|
||||||
|
color: $gray-600;
|
||||||
}
|
}
|
||||||
&.navigation-link {
|
&.navigation-link {
|
||||||
margin: 0 0.75rem;
|
margin: 0 0.75rem;
|
||||||
|
color: $gray-600;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,12 @@
|
||||||
<div
|
<div
|
||||||
class="funnel-header d-flex justify-content-center align-items-center flex-column"
|
class="funnel-header d-flex justify-content-center align-items-center flex-column"
|
||||||
v-if="imageSrc">
|
v-if="imageSrc">
|
||||||
|
<salesforceWebchat hideSalesforceWebchatLaunchButton />
|
||||||
<img class="logo-image img-fluid" :src="imageSrc" alt="Safelite logo" />
|
<img class="logo-image img-fluid" :src="imageSrc" alt="Safelite logo" />
|
||||||
<!-- // menu-modal temporarily not in use until the hamburger menu is re-implemented for use with the progress bar. See display: none below. -->
|
<!-- // menu-modal temporarily not in use until the hamburger menu is re-implemented for use with the progress bar. See display: none below. -->
|
||||||
<menuModal />
|
<menuModal />
|
||||||
<!-- // menu-modal temporarily not in use until the hamburger menu is re-implemented for use with the progress bar. See display: none below. -->
|
<!-- // menu-modal temporarily not in use until the hamburger menu is re-implemented for use with the progress bar. See display: none below. -->
|
||||||
|
<progressBar />
|
||||||
</div>
|
</div>
|
||||||
<template v-for="globalAlert in globalAlertMessages" :key="globalAlert.id">
|
<template v-for="globalAlert in globalAlertMessages" :key="globalAlert.id">
|
||||||
<transition name="fade" mode="out-in">
|
<transition name="fade" mode="out-in">
|
||||||
|
|
@ -26,6 +28,8 @@ import alert from "@/ux-components/alert/alert";
|
||||||
import eventBus from "@/helpers/event-bus/event-bus";
|
import eventBus from "@/helpers/event-bus/event-bus";
|
||||||
import { globalEvents } from "@/constants/events";
|
import { globalEvents } from "@/constants/events";
|
||||||
import menuModal from "@/fmg-components/funnel-header/menu-modal/menu-modal";
|
import menuModal from "@/fmg-components/funnel-header/menu-modal/menu-modal";
|
||||||
|
import salesforceWebchat from "../../digital-components/salesforce-webchat/salesforce-webchat.vue";
|
||||||
|
import progressBar from "@/fmg-components/funnel-header/progress-bar/progress-bar";
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
const ALERT_DURATION = 3000; // millisecond time to display alert before dismissal
|
const ALERT_DURATION = 3000; // millisecond time to display alert before dismissal
|
||||||
|
|
@ -39,6 +43,10 @@ export default {
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
cmsWidgetName: String,
|
cmsWidgetName: String,
|
||||||
|
hideSalesforceWebchatLaunchButton: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
imageSrc() {
|
imageSrc() {
|
||||||
|
|
@ -62,6 +70,8 @@ export default {
|
||||||
components: {
|
components: {
|
||||||
alert,
|
alert,
|
||||||
menuModal,
|
menuModal,
|
||||||
|
progressBar,
|
||||||
|
salesforceWebchat,
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
// Check if alert event is on the bus
|
// Check if alert event is on the bus
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
import { shallowMount } from "@vue/test-utils";
|
||||||
|
import progressBar from "./progress-bar";
|
||||||
|
import store from "@/store";
|
||||||
|
|
||||||
|
describe("progressBar", () => {
|
||||||
|
test("progress should be 0", () => {
|
||||||
|
// Arrange
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const wrapper = shallowMount(progressBar, {
|
||||||
|
mixins: [mockMixin],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(wrapper.vm.progress).toBe(0);
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("progressBar", () => {
|
||||||
|
test("progress should be 4%", () => {
|
||||||
|
// Arrange
|
||||||
|
store.getters = {
|
||||||
|
applicationUser: {
|
||||||
|
lastPageVisited: "vehicle",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const wrapper = shallowMount(progressBar, {
|
||||||
|
mixins: [mockMixin],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(wrapper.vm.progress).toBe(4);
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("progressBar", () => {
|
||||||
|
test("progress should be 48%", () => {
|
||||||
|
// Arrange
|
||||||
|
store.getters = {
|
||||||
|
applicationUser: {
|
||||||
|
lastPageVisited: "quote",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const wrapper = shallowMount(progressBar, {
|
||||||
|
mixins: [mockMixin],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(wrapper.vm.progress).toBe(48);
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("progressBar", () => {
|
||||||
|
test("progress should be 100%", () => {
|
||||||
|
// Arrange
|
||||||
|
store.getters = {
|
||||||
|
applicationUser: {
|
||||||
|
lastPageVisited: "confirmation",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const wrapper = shallowMount(progressBar, {
|
||||||
|
mixins: [mockMixin],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(wrapper.vm.progress).toBe(100);
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockMixin = {
|
||||||
|
methods: {
|
||||||
|
getProgress: jest.fn(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
<template>
|
||||||
|
<div id="progress-bar-container">
|
||||||
|
<progress v-if="progress > 0" :value="progress" max="100" v-html="progress + '%'" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import store from "@/store";
|
||||||
|
import { getProgressBarPercentage } from "@/constants/progress-bar-mapper";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "progressBar",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
progress: this.getProgress(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getProgress() {
|
||||||
|
var pageName = store.getters.applicationUser.lastPageVisited;
|
||||||
|
return getProgressBarPercentage(pageName);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="scss">
|
||||||
|
#progress-bar-container {
|
||||||
|
padding: 0.75rem 0;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
progress {
|
||||||
|
height: 10px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
/* Firefox */
|
||||||
|
appearance: none;
|
||||||
|
background-color: $blue-100;
|
||||||
|
border: 0;
|
||||||
|
border-radius: $border-radius-pill;
|
||||||
|
box-shadow: $box-shadow-inset;
|
||||||
|
|
||||||
|
&::-moz-progress-bar {
|
||||||
|
background-color: $blue;
|
||||||
|
border-radius: $border-radius-pill;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Webkit */
|
||||||
|
-webkit-appearance: none;
|
||||||
|
&::-webkit-progress-bar {
|
||||||
|
background-color: $blue-100;
|
||||||
|
border-radius: $border-radius-pill;
|
||||||
|
box-shadow: $box-shadow-inset;
|
||||||
|
}
|
||||||
|
&::-webkit-progress-value {
|
||||||
|
background-color: $blue;
|
||||||
|
border-radius: $border-radius-pill;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -72,6 +72,7 @@ export class Logger {
|
||||||
applicationConfig.CONSUMER_CF_DISTRO + applicationConfig.FRONTEND_LOGGER_PATH;
|
applicationConfig.CONSUMER_CF_DISTRO + applicationConfig.FRONTEND_LOGGER_PATH;
|
||||||
const headers = {
|
const headers = {
|
||||||
[headerKeys.EXPERIMENT]: JSON.stringify(store.getters.experimentSettings),
|
[headerKeys.EXPERIMENT]: JSON.stringify(store.getters.experimentSettings),
|
||||||
|
[headerKeys.APPLICATION_NAME]: applicationConfig.APPLICATION_NAME,
|
||||||
};
|
};
|
||||||
|
|
||||||
axios({
|
axios({
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,8 @@ export default {
|
||||||
emailOrSms: this.getEmailOrSmsFromStore(),
|
emailOrSms: this.getEmailOrSmsFromStore(),
|
||||||
},
|
},
|
||||||
serviceZipCode: this.getServiceZipFromStore(),
|
serviceZipCode: this.getServiceZipFromStore(),
|
||||||
|
carId: this.getCarIdfromStore(),
|
||||||
|
isHeavyTruck: this.getIsVehicleHeavyTruckServiceableFromStore(),
|
||||||
displayNonServiceableZipAlert: false,
|
displayNonServiceableZipAlert: false,
|
||||||
displayVinNotFoundAlert: false,
|
displayVinNotFoundAlert: false,
|
||||||
displayMatchedDifferentVehicleAlert: false,
|
displayMatchedDifferentVehicleAlert: false,
|
||||||
|
|
@ -219,6 +221,19 @@ export default {
|
||||||
getServiceZipFromStore() {
|
getServiceZipFromStore() {
|
||||||
return this.$store.getters.order.serviceLocation.zipCode;
|
return this.$store.getters.order.serviceLocation.zipCode;
|
||||||
},
|
},
|
||||||
|
getCarIdfromStore() {
|
||||||
|
return store.getters.vehicle.carId;
|
||||||
|
},
|
||||||
|
getIsVehicleHeavyTruckServiceableFromStore() {
|
||||||
|
return store.getters.vehicle.isBigTruck && store.getters.vehicle.canSafeliteService;
|
||||||
|
},
|
||||||
|
async findClosestApplicableShops() {
|
||||||
|
return await this.dispatchStoreActionWithLogging(
|
||||||
|
storeActions.GET_CLOSEST_APPLICABLE_SHOPS,
|
||||||
|
{ zip: this.serviceZipCode, carId: this.carId },
|
||||||
|
"address-lookup"
|
||||||
|
);
|
||||||
|
},
|
||||||
async forwardButtonAction() {
|
async forwardButtonAction() {
|
||||||
this.resetWarningsAndErrors();
|
this.resetWarningsAndErrors();
|
||||||
|
|
||||||
|
|
@ -278,7 +293,6 @@ export default {
|
||||||
return this.$refs.navbar.removeLoader();
|
return this.$refs.navbar.removeLoader();
|
||||||
}
|
}
|
||||||
this.displayInvalidZipAlert = false;
|
this.displayInvalidZipAlert = false;
|
||||||
|
|
||||||
const carsFound = resultMap.vinLookupResponse.vinVehicles;
|
const carsFound = resultMap.vinLookupResponse.vinVehicles;
|
||||||
|
|
||||||
// Handle cases for different amounts of VINS found for the address.
|
// Handle cases for different amounts of VINS found for the address.
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,10 @@
|
||||||
<div class="container-fluid pb-2">
|
<div class="container-fluid pb-2">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
|
<funnelHeader
|
||||||
|
cmsWidgetName="FunnelHeaderWidget"
|
||||||
|
ref="funnelHeader"
|
||||||
|
hideSalesforceWebchatLaunchButton />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
|
|
@ -72,7 +75,8 @@
|
||||||
:donationValues="donationValues"
|
:donationValues="donationValues"
|
||||||
:showDonationSuccess="showDonationSuccess"
|
:showDonationSuccess="showDonationSuccess"
|
||||||
:showDonationError="showDonationError"
|
:showDonationError="showDonationError"
|
||||||
@DonationAddedEvent="addDonationToOrder" />
|
@DonationAddedEvent="addDonationToOrder"
|
||||||
|
ref="donationBlock" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr class="mb-5" />
|
<hr class="mb-5" />
|
||||||
|
|
@ -544,10 +548,12 @@ export default {
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
this.lineItems = deepClone(this.getSubmittedOrder().lineItems);
|
this.lineItems = deepClone(this.getSubmittedOrder().lineItems);
|
||||||
|
this.$refs.donationBlock.removeLoader();
|
||||||
} else {
|
} else {
|
||||||
this.showDonationError = true;
|
this.showDonationError = true;
|
||||||
this.showDonationSuccess = false;
|
this.showDonationSuccess = false;
|
||||||
this.donationAmount = 0;
|
this.donationAmount = 0;
|
||||||
|
this.$refs.donationBlock.removeLoader();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,10 @@
|
||||||
@back-clicked="backButtonAction"
|
@back-clicked="backButtonAction"
|
||||||
@ForwardClicked="forwardButtonAction" />
|
@ForwardClicked="forwardButtonAction" />
|
||||||
|
|
||||||
<textBlock cmsWidgetName="DisclaimerCopyWidget" typeStyle="caption" />
|
<textBlock
|
||||||
|
class="mb-5"
|
||||||
|
cmsWidgetName="DisclaimerCopyWidget"
|
||||||
|
typeStyle="caption" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -53,11 +53,33 @@ import { Form, defineRule } from "vee-validate";
|
||||||
import baseMixin from "@/mixins/base-mixin.js";
|
import baseMixin from "@/mixins/base-mixin.js";
|
||||||
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||||
import { applicationConfig } from "@/constants/application-config";
|
import { applicationConfig } from "@/constants/application-config";
|
||||||
|
import experimentMixin from "@/mixins/experiment-mixin.js";
|
||||||
|
import { experimentSettings } from "@/constants/experiments";
|
||||||
|
import { partNumberStrings } from "@/constants/part-number-strings";
|
||||||
|
import { deepClone } from "@/helpers/object-helper";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "insurance-company",
|
name: "insurance-company",
|
||||||
async beforeRouteEnter(to, from, next) {
|
async beforeRouteEnter(to, from, next) {
|
||||||
|
// Remove any Pricing By Day lineItems before going to Heritage insurance
|
||||||
|
const isPricingByDayExperiment = experimentMixin.methods.hasSettingEqualTo(
|
||||||
|
experimentSettings.PRICING_BY_DAY,
|
||||||
|
"true"
|
||||||
|
);
|
||||||
|
if (isPricingByDayExperiment) {
|
||||||
|
const lineItems = deepClone(store.getters.order.lineItems);
|
||||||
|
const supportingItemsWithoutPricingByDay =
|
||||||
|
baseMixin.methods.filterOutCertainPartTypesOrNumbers(lineItems.supportingItems, {
|
||||||
|
partNumbersToRemove: [partNumberStrings.PRICING_BY_DAY_UPCHARGE],
|
||||||
|
});
|
||||||
|
baseMixin.methods.dispatchStoreAction(
|
||||||
|
storeActions.SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING,
|
||||||
|
supportingItemsWithoutPricingByDay,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Call APIs
|
// Call APIs
|
||||||
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
||||||
const insuranceCompanyListPromise = baseMixin.methods.dispatchStoreActionWithLogging(
|
const insuranceCompanyListPromise = baseMixin.methods.dispatchStoreActionWithLogging(
|
||||||
|
|
@ -181,3 +203,9 @@ export default {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.ui-autocomplete {
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -716,6 +716,5 @@ function setupMocks({
|
||||||
wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => "");
|
wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => "");
|
||||||
wrapper.vm.$refs.navbar.updateButtonText = jest.fn();
|
wrapper.vm.$refs.navbar.updateButtonText = jest.fn();
|
||||||
wrapper.vm.$refs.navbar.removeLoader = jest.fn();
|
wrapper.vm.$refs.navbar.removeLoader = jest.fn();
|
||||||
|
|
||||||
return { wrapper, apiPromise };
|
return { wrapper, apiPromise };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,13 +76,20 @@
|
||||||
:manualCopy="AlertMatchedDifferentVehicleBody"
|
:manualCopy="AlertMatchedDifferentVehicleBody"
|
||||||
alertClass="alert-warning"
|
alertClass="alert-warning"
|
||||||
v-bind:isDismissible="false" />
|
v-bind:isDismissible="false" />
|
||||||
|
<alert
|
||||||
|
ref="AlertNoService"
|
||||||
|
v-if="displayNoServiceAlert"
|
||||||
|
class="my-4"
|
||||||
|
cmsWidgetName="AlertNoServiceWidget"
|
||||||
|
alertClass="alert-danger"
|
||||||
|
v-bind:isDismissible="false" />
|
||||||
|
|
||||||
<navbar
|
<navbar
|
||||||
ref="navbar"
|
ref="navbar"
|
||||||
cmsWidgetName="FunnelFooterWidget"
|
cmsWidgetName="FunnelFooterWidget"
|
||||||
:isForwardActionDisabled="!meta.valid"
|
:isForwardActionDisabled="!meta.valid"
|
||||||
@back-clicked="backButtonAction"
|
@back-clicked="backButtonAction"
|
||||||
@ForwardClicked="forwardButtonAction" />
|
@ForwardClicked="forwardButtonAction || displayNoServiceAlert" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -158,6 +165,8 @@ export default {
|
||||||
registrationZipCode: this.getRegistrationZipFromStore() ?? this.$route.query.zipcode,
|
registrationZipCode: this.getRegistrationZipFromStore() ?? this.$route.query.zipcode,
|
||||||
emailOrSms: this.getEmailOrSmsFromStore(),
|
emailOrSms: this.getEmailOrSmsFromStore(),
|
||||||
serviceZipCode: this.getServiceZipFromStore(),
|
serviceZipCode: this.getServiceZipFromStore(),
|
||||||
|
carId: this.getCarIdfromStore(),
|
||||||
|
isHeavyTruck: this.getIsVehicleHeavyTruckServiceableFromStore(),
|
||||||
displayNonServiceableZipAlert: false,
|
displayNonServiceableZipAlert: false,
|
||||||
displayVinNotFoundAlert: false,
|
displayVinNotFoundAlert: false,
|
||||||
displayMatchedDifferentVehicleAlert: false,
|
displayMatchedDifferentVehicleAlert: false,
|
||||||
|
|
@ -200,6 +209,19 @@ export default {
|
||||||
getServiceZipFromStore() {
|
getServiceZipFromStore() {
|
||||||
return this.$store.getters.order.serviceLocation.zipCode;
|
return this.$store.getters.order.serviceLocation.zipCode;
|
||||||
},
|
},
|
||||||
|
getCarIdfromStore() {
|
||||||
|
return store.getters.vehicle.carId;
|
||||||
|
},
|
||||||
|
getIsVehicleHeavyTruckServiceableFromStore() {
|
||||||
|
return store.getters.vehicle.isBigTruck && store.getters.vehicle.canSafeliteService;
|
||||||
|
},
|
||||||
|
async findClosestApplicableShops() {
|
||||||
|
return await this.dispatchStoreActionWithLogging(
|
||||||
|
storeActions.GET_CLOSEST_APPLICABLE_SHOPS,
|
||||||
|
{ zip: this.serviceZipCode, carId: this.carId },
|
||||||
|
"license-plate-lookup"
|
||||||
|
);
|
||||||
|
},
|
||||||
async forwardButtonAction() {
|
async forwardButtonAction() {
|
||||||
this.resetWarningsAndErrors();
|
this.resetWarningsAndErrors();
|
||||||
|
|
||||||
|
|
@ -257,7 +279,6 @@ export default {
|
||||||
this.displayInvalidZipAlert = true;
|
this.displayInvalidZipAlert = true;
|
||||||
return this.$refs.navbar.removeLoader();
|
return this.$refs.navbar.removeLoader();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the CarId has changed.
|
// Check if the CarId has changed.
|
||||||
this.isCarIdDifferent =
|
this.isCarIdDifferent =
|
||||||
vinLookup.data.vehicle.carId !== this.$store.getters.vehicle.carId;
|
vinLookup.data.vehicle.carId !== this.$store.getters.vehicle.carId;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,259 @@
|
||||||
|
<template>
|
||||||
|
<div class="alert fade show my-2 py-2 border-0 alert-info" role="alert">
|
||||||
|
<component :is="'script'" src="https://js.afterpay.com/afterpay-1.x.js" async></component>
|
||||||
|
<div class="my-4 alert-heading text-center">
|
||||||
|
<span v-html="afterpayHeaderCopy" />
|
||||||
|
|
||||||
|
<span class="afterpay-amount">{{ this.afterpayPrice }}</span>
|
||||||
|
<br />
|
||||||
|
<span class="alert-sub-heading" v-html="afterpayHeaderSubHeaderCopy" />
|
||||||
|
|
||||||
|
<img :src="afterPayImageUrl" />
|
||||||
|
|
||||||
|
<a
|
||||||
|
id="afterpay-learnmore"
|
||||||
|
href="#"
|
||||||
|
data-afterpay-modal="en_US-safelite"
|
||||||
|
data-bind="click:afterpayLearnMore">
|
||||||
|
<img :src="infoIcon" alt="Info Icon" class="info-icon" />
|
||||||
|
</a>
|
||||||
|
<div class="after-pay">
|
||||||
|
<div
|
||||||
|
class="after-pay-toggle"
|
||||||
|
:class="[isExpanded ? 'expanded' : '']"
|
||||||
|
@click="toggleIsExpanded">
|
||||||
|
<a href="#!" v-html="paymentPlanToggleHeading" />
|
||||||
|
</div>
|
||||||
|
<div class="after-pay-details">
|
||||||
|
<div class="payment-section">
|
||||||
|
<div class="payment-card">
|
||||||
|
<div class="calendar-section">
|
||||||
|
<div class="icon"><img :src="calendarIcon" /></div>
|
||||||
|
<div>Today</div>
|
||||||
|
</div>
|
||||||
|
<div class="payment-amount-section">
|
||||||
|
<div class="icon"><img :src="paymentIcon" /></div>
|
||||||
|
<div class="amount-due">{{ this.afterpayPrice }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="payment-card">
|
||||||
|
<div class="calendar-section">
|
||||||
|
<div class="icon"><img :src="calendarIcon" /></div>
|
||||||
|
<div>2 weeks</div>
|
||||||
|
</div>
|
||||||
|
<div class="payment-amount-section">
|
||||||
|
<div class="icon"><img :src="paymentIcon" /></div>
|
||||||
|
<div class="amount-due">{{ this.afterpayPrice }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="payment-card">
|
||||||
|
<div class="calendar-section">
|
||||||
|
<div class="icon"><img :src="calendarIcon" /></div>
|
||||||
|
<div>4 weeks</div>
|
||||||
|
</div>
|
||||||
|
<div class="payment-amount-section">
|
||||||
|
<div class="icon"><img :src="paymentIcon" /></div>
|
||||||
|
<div class="amount-due">{{ this.afterpayPrice }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="payment-card">
|
||||||
|
<div class="calendar-section">
|
||||||
|
<div class="icon"><img :src="calendarIcon" /></div>
|
||||||
|
<div>6 weeks</div>
|
||||||
|
</div>
|
||||||
|
<div class="payment-amount-section">
|
||||||
|
<div class="icon"><img :src="paymentIcon" /></div>
|
||||||
|
<div class="amount-due">{{ this.afterpayPrice }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="legend">
|
||||||
|
<div class="legend-item">
|
||||||
|
<img :src="calendarKeyIcon" />
|
||||||
|
<span>Payment Schedule</span>
|
||||||
|
</div>
|
||||||
|
<div class="legend-item">
|
||||||
|
<img :src="paymentKeyIcon" />
|
||||||
|
<span>Amount Due</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "afterpay-breakout",
|
||||||
|
|
||||||
|
props: {
|
||||||
|
cmsWidgetName: String,
|
||||||
|
totalAmount: Number,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return { isExpanded: false };
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleIsExpanded() {
|
||||||
|
this.isExpanded = !this.isExpanded;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
afterPayImageUrl() {
|
||||||
|
return this.getCmsContent(this.cmsWidgetName, "Image");
|
||||||
|
},
|
||||||
|
hasImage() {
|
||||||
|
return !!this.imageUrl;
|
||||||
|
},
|
||||||
|
afterpayHeaderCopy() {
|
||||||
|
// Text for Afterpay Breakout experiment
|
||||||
|
|
||||||
|
return this.getCmsContent(this.cmsWidgetName, "HeaderText");
|
||||||
|
},
|
||||||
|
afterpayHeaderSubHeaderCopy() {
|
||||||
|
// Text for Afterpay Breakout experiment
|
||||||
|
|
||||||
|
return this.getCmsContent(this.cmsWidgetName, "SubheaderText");
|
||||||
|
},
|
||||||
|
afterpayPrice() {
|
||||||
|
const adjusted = (this.totalAmount / 4).toFixed(2);
|
||||||
|
return `$${adjusted}`;
|
||||||
|
},
|
||||||
|
infoIcon() {
|
||||||
|
return require(`@/assets/img/icons/info-circle.svg`);
|
||||||
|
},
|
||||||
|
calendarIcon() {
|
||||||
|
return require(`@/assets/img/icons/calendar-icon-black.svg`);
|
||||||
|
},
|
||||||
|
paymentIcon() {
|
||||||
|
return require(`@/assets/img/icons/payment-icon-green.svg`);
|
||||||
|
},
|
||||||
|
calendarKeyIcon() {
|
||||||
|
return require(`@/assets/img/icons/calendar-icon-grey.svg`);
|
||||||
|
},
|
||||||
|
paymentKeyIcon() {
|
||||||
|
return require(`@/assets/img/icons/payment-icon-grey.svg`);
|
||||||
|
},
|
||||||
|
paymentPlanToggleHeading() {
|
||||||
|
return this.getCmsContent(this.cmsWidgetName, "BodyText");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.alert {
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-top: 1rem;
|
||||||
|
|
||||||
|
&.alert-info {
|
||||||
|
.alert-heading {
|
||||||
|
color: #000000;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
margin-left: 1rem;
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.afterpay-amount {
|
||||||
|
color: #0c7e47;
|
||||||
|
}
|
||||||
|
.alert-sub-heading {
|
||||||
|
color: #4b4c4e;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
.after-pay {
|
||||||
|
.after-pay-toggle {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: "";
|
||||||
|
transition: all 0.5s ease;
|
||||||
|
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 8.9' xml:space='preserve'%3e%3cpath d='M8 8.9c-.2 0-.5-.1-.6-.3L.3 1.5C.1 1.4 0 1.1 0 .9 0 .7.1.4.3.3.4.1.7 0 .9 0c.2 0 .5.1.6.3L8 6.7 14.5.2c.1-.1.4-.2.6-.2.2 0 .5.1.6.3s.3.4.3.6c0 .2-.1.5-.3.6L8.6 8.6c-.1.2-.4.3-.6.3z' fill='%231474a2'/%3e%3c/svg%3e");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right center;
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
width: 1rem;
|
||||||
|
height: 9px;
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
&.expanded:after {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
&.expanded + .after-pay-details {
|
||||||
|
max-height: 500px;
|
||||||
|
transition: all 250ms ease-in;
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.after-pay-toggle a {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #0070d1;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.after-pay-details {
|
||||||
|
max-height: 0;
|
||||||
|
transition: all 250ms ease-out;
|
||||||
|
overflow: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.payment-section {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
gap: 0rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.payment-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 0.25px #b3b4b5;
|
||||||
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
.calendar-section {
|
||||||
|
background-color: #fff; /* White background for calendar area */
|
||||||
|
padding: 0.5rem;
|
||||||
|
text-align: center;
|
||||||
|
gap: 2px;
|
||||||
|
font-weight: 700;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.payment-amount-section {
|
||||||
|
background-color: #e0f7e9; /* Light green background for payment area */
|
||||||
|
padding: 0.5rem;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount-due {
|
||||||
|
color: #00723e; /* Green text */
|
||||||
|
}
|
||||||
|
.legend {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 1rem;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
.legend-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.legend-item span {
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -117,6 +117,12 @@ function setupMocks() {
|
||||||
|
|
||||||
return "false";
|
return "false";
|
||||||
}),
|
}),
|
||||||
|
hasSettingEqualTo: jest.fn((settingName) => {
|
||||||
|
if (settingName === experimentSettings.DISPLAY_AFTERPAY_BREAKOUT_DISPLAY) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}),
|
||||||
getCmsContent: jest.fn().mockImplementation(() => {
|
getCmsContent: jest.fn().mockImplementation(() => {
|
||||||
return "test text";
|
return "test text";
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,10 @@
|
||||||
:isExpandedOnLoad="false"
|
:isExpandedOnLoad="false"
|
||||||
:isMSRFeeApplicable="isMSRFeeApplicable" />
|
:isMSRFeeApplicable="isMSRFeeApplicable" />
|
||||||
|
|
||||||
|
<afterpayBreakout
|
||||||
|
v-if="isAfterpayBreakoutDisplay"
|
||||||
|
cmsWidgetName="AfterpayBreakoutAlertWidget"
|
||||||
|
:totalAmount="totalAmountDue" />
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -118,6 +122,7 @@ import alert from "@/ux-components/alert/alert";
|
||||||
import textBlock from "@/digital-components/text-block/text-block";
|
import textBlock from "@/digital-components/text-block/text-block";
|
||||||
import checkboxQuestion from "@/digital-components/checkbox-question/checkbox-question";
|
import checkboxQuestion from "@/digital-components/checkbox-question/checkbox-question";
|
||||||
import contentGroupModal from "@/fmg-components/content-group-modal/content-group-modal";
|
import contentGroupModal from "@/fmg-components/content-group-modal/content-group-modal";
|
||||||
|
import afterpayBreakout from "@/layouts/payment-method/afterpay-breakout/afterpay-breakout";
|
||||||
|
|
||||||
// Supporting Items
|
// Supporting Items
|
||||||
import baseMixin from "@/mixins/base-mixin.js";
|
import baseMixin from "@/mixins/base-mixin.js";
|
||||||
|
|
@ -131,7 +136,6 @@ import {
|
||||||
} from "@/helpers/cms-content-helper";
|
} from "@/helpers/cms-content-helper";
|
||||||
import { paymentMethods } from "@/constants/payment-method-constants";
|
import { paymentMethods } from "@/constants/payment-method-constants";
|
||||||
import { experimentSettings } from "@/constants/experiments";
|
import { experimentSettings } from "@/constants/experiments";
|
||||||
import experimentMixin from "@/mixins/experiment-mixin.js";
|
|
||||||
import {
|
import {
|
||||||
revalidatePromosAndValidateQueryStringPromo,
|
revalidatePromosAndValidateQueryStringPromo,
|
||||||
buildToastMessagesFromRevalidateOrValidatePromoResponse,
|
buildToastMessagesFromRevalidateOrValidatePromoResponse,
|
||||||
|
|
@ -805,6 +809,18 @@ export default {
|
||||||
lineItemsCloneForWatcher() {
|
lineItemsCloneForWatcher() {
|
||||||
return Object.assign({}, this.lineItems);
|
return Object.assign({}, this.lineItems);
|
||||||
},
|
},
|
||||||
|
isRecalPriceRemove() {
|
||||||
|
return (
|
||||||
|
this.getSettingValue(experimentSettings.RECAL_PRICE_REMOVE)?.toLowerCase() ===
|
||||||
|
"true"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
isAfterpayBreakoutDisplay() {
|
||||||
|
return (
|
||||||
|
(!this.isRecalPriceRemove || !this.isRecalibrationOnOrder) &&
|
||||||
|
this.hasSettingEqualTo(experimentSettings.DISPLAY_AFTERPAY_BREAKOUT_DISPLAY, "true")
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
customCtaCopy(newValue) {
|
customCtaCopy(newValue) {
|
||||||
|
|
@ -856,6 +872,7 @@ export default {
|
||||||
textBlock,
|
textBlock,
|
||||||
checkboxQuestion,
|
checkboxQuestion,
|
||||||
contentGroupModal,
|
contentGroupModal,
|
||||||
|
afterpayBreakout,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,16 @@
|
||||||
<div class="alert fade show my-2 py-2 border-0 alert-info" role="alert">
|
<div class="alert fade show my-2 py-2 border-0 alert-info" role="alert">
|
||||||
<component :is="'script'" src="https://js.afterpay.com/afterpay-1.x.js" async></component>
|
<component :is="'script'" src="https://js.afterpay.com/afterpay-1.x.js" async></component>
|
||||||
|
|
||||||
<div class="mx-4 my-0 fw-bold alert-heading text-center">
|
<div
|
||||||
|
class="mx-4 my-0 alert-heading text-center"
|
||||||
|
:class="[isAfterpayBreakoutDisplay ? 'fw-bolder' : 'fw-bold']">
|
||||||
<span v-for="token in afterpayCopyTokens" :key="token">
|
<span v-for="token in afterpayCopyTokens" :key="token">
|
||||||
<span v-if="isAfterpayPriceToken(token)">{{ afterpayPrice }}</span>
|
<span v-if="isAfterpayPriceToken(token)">{{ afterpayPrice }}</span>
|
||||||
<span v-else-if="isInlineImageToken(token)">
|
<span v-else-if="isInlineImageToken(token)">
|
||||||
<img v-if="hasImage" :src="imageUrl" :alt="getInlineAltText(token)" />
|
<img v-if="hasImage" :src="imageUrl" :alt="getInlineAltText(token)" />
|
||||||
<span v-else>{{ getInlineAltText(token) }}</span>
|
<span v-else>{{ getInlineAltText(token) }}</span>
|
||||||
</span>
|
</span>
|
||||||
<span v-else>
|
<span v-else v-html="token"></span>
|
||||||
{{ token }}
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
|
|
@ -52,6 +52,7 @@ export default {
|
||||||
props: {
|
props: {
|
||||||
cmsWidgetName: String,
|
cmsWidgetName: String,
|
||||||
lineItems: Array,
|
lineItems: Array,
|
||||||
|
isAfterpayBreakoutDisplay: Boolean,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {};
|
return {};
|
||||||
|
|
@ -69,7 +70,16 @@ export default {
|
||||||
return !!this.imageUrl;
|
return !!this.imageUrl;
|
||||||
},
|
},
|
||||||
afterpayCopyTokens() {
|
afterpayCopyTokens() {
|
||||||
return splitCopyOnCMSPlaceHolder(this.getCmsContent(this.cmsWidgetName, "HeaderText"));
|
if (this.isAfterpayBreakoutDisplay) {
|
||||||
|
// Text for Afterpay Breakout experiment
|
||||||
|
return splitCopyOnCMSPlaceHolder(
|
||||||
|
this.getCmsContent(this.cmsWidgetName, "BodyText2")
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return splitCopyOnCMSPlaceHolder(
|
||||||
|
this.getCmsContent(this.cmsWidgetName, "HeaderText")
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
modalCopy() {
|
modalCopy() {
|
||||||
return this.getCmsContent(this.cmsWidgetName, "BodyText");
|
return this.getCmsContent(this.cmsWidgetName, "BodyText");
|
||||||
|
|
@ -117,6 +127,12 @@ export default {
|
||||||
.alert-heading {
|
.alert-heading {
|
||||||
color: $blue-700;
|
color: $blue-700;
|
||||||
}
|
}
|
||||||
|
&.fw-bolder {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
vertical-align: text-top;
|
||||||
|
}
|
||||||
svg {
|
svg {
|
||||||
fill: $blue-700;
|
fill: $blue-700;
|
||||||
width: 1rem;
|
width: 1rem;
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,12 @@ jest.mock("@/mixins/experiment-mixin.js", () => ({
|
||||||
}
|
}
|
||||||
return "test";
|
return "test";
|
||||||
},
|
},
|
||||||
|
hasSettingEqualTo(settingName) {
|
||||||
|
if (settingName === mockExperimentSettings.DISPLAY_AFTERPAY_BREAKOUT_DISPLAY) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return "test";
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -264,6 +270,12 @@ describe("quote.vue", () => {
|
||||||
lineItems: {
|
lineItems: {
|
||||||
glassParts: ["item", "item2"],
|
glassParts: ["item", "item2"],
|
||||||
},
|
},
|
||||||
|
pageData: jest.fn((page) => {
|
||||||
|
if (page === "quote") {
|
||||||
|
return { saveProgressPopupSkipped: true };
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}),
|
||||||
applicationUser: {
|
applicationUser: {
|
||||||
experiments: [],
|
experiments: [],
|
||||||
},
|
},
|
||||||
|
|
@ -313,6 +325,12 @@ describe("quote.vue", () => {
|
||||||
lineItems: {
|
lineItems: {
|
||||||
glassParts: ["item", "item2"],
|
glassParts: ["item", "item2"],
|
||||||
},
|
},
|
||||||
|
pageData: jest.fn((page) => {
|
||||||
|
if (page === "quote") {
|
||||||
|
return { saveProgressPopupSkipped: true };
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}),
|
||||||
applicationUser: {
|
applicationUser: {
|
||||||
experiments: [],
|
experiments: [],
|
||||||
},
|
},
|
||||||
|
|
@ -360,6 +378,12 @@ describe("quote.vue", () => {
|
||||||
lineItems: {
|
lineItems: {
|
||||||
glassParts: ["item", "item2"],
|
glassParts: ["item", "item2"],
|
||||||
},
|
},
|
||||||
|
pageData: jest.fn((page) => {
|
||||||
|
if (page === "quote") {
|
||||||
|
return { saveProgressPopupSkipped: true };
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}),
|
||||||
applicationUser: {
|
applicationUser: {
|
||||||
experiments: [],
|
experiments: [],
|
||||||
},
|
},
|
||||||
|
|
@ -404,6 +428,12 @@ describe("quote.vue", () => {
|
||||||
lineItems: {
|
lineItems: {
|
||||||
glassParts: ["item", "item2"],
|
glassParts: ["item", "item2"],
|
||||||
},
|
},
|
||||||
|
pageData: jest.fn((page) => {
|
||||||
|
if (page === "quote") {
|
||||||
|
return { saveProgressPopupSkipped: true };
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}),
|
||||||
applicationUser: {
|
applicationUser: {
|
||||||
experiments: [],
|
experiments: [],
|
||||||
},
|
},
|
||||||
|
|
@ -449,6 +479,12 @@ describe("quote.vue", () => {
|
||||||
lineItems: {
|
lineItems: {
|
||||||
glassParts: ["item", "item2"],
|
glassParts: ["item", "item2"],
|
||||||
},
|
},
|
||||||
|
pageData: jest.fn((page) => {
|
||||||
|
if (page === "quote") {
|
||||||
|
return { saveProgressPopupSkipped: true };
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}),
|
||||||
applicationUser: {
|
applicationUser: {
|
||||||
experiments: [],
|
experiments: [],
|
||||||
},
|
},
|
||||||
|
|
@ -494,6 +530,12 @@ describe("quote.vue", () => {
|
||||||
lineItems: {
|
lineItems: {
|
||||||
glassParts: ["item", "item2"],
|
glassParts: ["item", "item2"],
|
||||||
},
|
},
|
||||||
|
pageData: jest.fn((page) => {
|
||||||
|
if (page === "quote") {
|
||||||
|
return { saveProgressPopupSkipped: true };
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}),
|
||||||
applicationUser: {
|
applicationUser: {
|
||||||
experiments: [],
|
experiments: [],
|
||||||
},
|
},
|
||||||
|
|
@ -540,6 +582,12 @@ describe("quote.vue", () => {
|
||||||
lineItems: {
|
lineItems: {
|
||||||
glassParts: ["item", "item2"],
|
glassParts: ["item", "item2"],
|
||||||
},
|
},
|
||||||
|
pageData: jest.fn((page) => {
|
||||||
|
if (page === "quote") {
|
||||||
|
return { saveProgressPopupSkipped: true };
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}),
|
||||||
applicationUser: {
|
applicationUser: {
|
||||||
experiments: [],
|
experiments: [],
|
||||||
},
|
},
|
||||||
|
|
@ -588,6 +636,12 @@ describe("quote.vue", () => {
|
||||||
lineItems: {
|
lineItems: {
|
||||||
glassParts: ["item", "item2"],
|
glassParts: ["item", "item2"],
|
||||||
},
|
},
|
||||||
|
pageData: jest.fn((page) => {
|
||||||
|
if (page === "quote") {
|
||||||
|
return { saveProgressPopupSkipped: true };
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}),
|
||||||
applicationUser: {
|
applicationUser: {
|
||||||
experiments: [],
|
experiments: [],
|
||||||
},
|
},
|
||||||
|
|
@ -634,6 +688,12 @@ describe("quote.vue", () => {
|
||||||
lineItems: {
|
lineItems: {
|
||||||
glassParts: ["item", "item2"],
|
glassParts: ["item", "item2"],
|
||||||
},
|
},
|
||||||
|
pageData: jest.fn((page) => {
|
||||||
|
if (page === "quote") {
|
||||||
|
return { saveProgressPopupSkipped: true };
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}),
|
||||||
applicationUser: {
|
applicationUser: {
|
||||||
experiments: [],
|
experiments: [],
|
||||||
},
|
},
|
||||||
|
|
@ -681,6 +741,12 @@ describe("quote.vue", () => {
|
||||||
lineItems: {
|
lineItems: {
|
||||||
glassParts: ["item", "item2"],
|
glassParts: ["item", "item2"],
|
||||||
},
|
},
|
||||||
|
pageData: jest.fn((page) => {
|
||||||
|
if (page === "quote") {
|
||||||
|
return { saveProgressPopupSkipped: true };
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}),
|
||||||
applicationUser: {
|
applicationUser: {
|
||||||
experiments: [],
|
experiments: [],
|
||||||
},
|
},
|
||||||
|
|
@ -780,6 +846,12 @@ describe("quote.vue", () => {
|
||||||
lineItems: {
|
lineItems: {
|
||||||
glassParts: ["item", "item2"],
|
glassParts: ["item", "item2"],
|
||||||
},
|
},
|
||||||
|
pageData: jest.fn((page) => {
|
||||||
|
if (page === "quote") {
|
||||||
|
return { saveProgressPopupSkipped: true };
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}),
|
||||||
applicationUser: {
|
applicationUser: {
|
||||||
experiments: [],
|
experiments: [],
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@
|
||||||
<afterpayModalBanner
|
<afterpayModalBanner
|
||||||
v-if="showAfterpayBanner"
|
v-if="showAfterpayBanner"
|
||||||
cmsWidgetName="AfterpayModalWidget"
|
cmsWidgetName="AfterpayModalWidget"
|
||||||
|
:isAfterpayBreakoutDisplay="isAfterpayBreakoutDisplay"
|
||||||
:lineItems="lineItems" />
|
:lineItems="lineItems" />
|
||||||
|
|
||||||
<!-- If vehicle requires recal AND we are hiding recal pricing info, then show recal disclaimer banner. -->
|
<!-- If vehicle requires recal AND we are hiding recal pricing info, then show recal disclaimer banner. -->
|
||||||
|
|
@ -166,6 +167,7 @@ import {
|
||||||
getDeviceIdValue,
|
getDeviceIdValue,
|
||||||
} from "@/helpers/heritage-integration/cookie-helper";
|
} from "@/helpers/heritage-integration/cookie-helper";
|
||||||
import { showFmgLoadingModal } from "@/helpers/loading-modal-helper";
|
import { showFmgLoadingModal } from "@/helpers/loading-modal-helper";
|
||||||
|
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||||
|
|
||||||
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
|
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
|
||||||
|
|
||||||
|
|
@ -220,6 +222,36 @@ export default {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AfterpayBreakoutDisplay Custom Exposure
|
||||||
|
const IGQExperiment = store.getters.applicationUser.experiments.find(
|
||||||
|
(e) => e.universeName === experimentUniverses.RECAL_PRICE_REMOVAL
|
||||||
|
);
|
||||||
|
const hasExposedIGQExperiment = IGQExperiment?.isExposed;
|
||||||
|
|
||||||
|
const afterpayBreakoutDisplayExperiment = store.getters.applicationUser.experiments.find(
|
||||||
|
(e) => e.universeName === experimentUniverses.AFTERPAY_BREAKOUT_DISPLAY
|
||||||
|
);
|
||||||
|
const hasExposedAfterpayBreakoutDisplay = afterpayBreakoutDisplayExperiment?.isExposed;
|
||||||
|
const shouldSendAfterpayBreakoutDisplayExposeRequest =
|
||||||
|
afterpayBreakoutDisplayExperiment &&
|
||||||
|
!hasExposedAfterpayBreakoutDisplay &&
|
||||||
|
!hasExposedIGQExperiment;
|
||||||
|
|
||||||
|
if (shouldSendAfterpayBreakoutDisplayExposeRequest) {
|
||||||
|
baseMixin.methods.dispatchStoreActionWithLogging(
|
||||||
|
storeActions.LOG_EXPERIMENT_EXPOSURE_AND_UPDATE_STORE,
|
||||||
|
{
|
||||||
|
userId: getUserIdValue(),
|
||||||
|
deviceId: getDeviceIdValue(),
|
||||||
|
sessionKey: getSessionKeyValue(),
|
||||||
|
pageName: to.query.fmgPage,
|
||||||
|
experiment: afterpayBreakoutDisplayExperiment,
|
||||||
|
},
|
||||||
|
"quote",
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const promiseResultMap = [
|
const promiseResultMap = [
|
||||||
{
|
{
|
||||||
resultKey: "cmsContent",
|
resultKey: "cmsContent",
|
||||||
|
|
@ -242,7 +274,9 @@ export default {
|
||||||
const resultMap = await settleAllPromises(promiseResultMap);
|
const resultMap = await settleAllPromises(promiseResultMap);
|
||||||
const emailFromStore = store.getters.order.customer.emailAddress;
|
const emailFromStore = store.getters.order.customer.emailAddress;
|
||||||
const isEmailInStoreOnPageLoad = emailFromStore?.length > 0;
|
const isEmailInStoreOnPageLoad = emailFromStore?.length > 0;
|
||||||
const showSaveProgressPopup = isEmailInStoreOnPageLoad ? false : true;
|
const isPopupSkipped =
|
||||||
|
store.getters.pageData(fmgPageValues.QUOTE)?.saveProgressPopupSkipped === true;
|
||||||
|
const showSaveProgressPopup = !(isEmailInStoreOnPageLoad || isPopupSkipped);
|
||||||
const showSaveProgressModal = isEmailInStoreOnPageLoad ? false : true;
|
const showSaveProgressModal = isEmailInStoreOnPageLoad ? false : true;
|
||||||
const shouldSkipToInsurance =
|
const shouldSkipToInsurance =
|
||||||
getBoolFromString(
|
getBoolFromString(
|
||||||
|
|
@ -568,6 +602,15 @@ export default {
|
||||||
showRecalDisclaimer() {
|
showRecalDisclaimer() {
|
||||||
return this.isRecalibrationOnOrder && this.isRecalPriceRemove;
|
return this.isRecalibrationOnOrder && this.isRecalPriceRemove;
|
||||||
},
|
},
|
||||||
|
isAfterpayBreakoutDisplay() {
|
||||||
|
return (
|
||||||
|
(!this.isRecalPriceRemove || !this.isRecalibrationOnOrder) &&
|
||||||
|
experimentMixin.methods.hasSettingEqualTo(
|
||||||
|
experimentSettings.DISPLAY_AFTERPAY_BREAKOUT_DISPLAY,
|
||||||
|
"true"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getColCount() {
|
getColCount() {
|
||||||
|
|
@ -626,6 +669,15 @@ export default {
|
||||||
async closeSaveProgressPopup() {
|
async closeSaveProgressPopup() {
|
||||||
this.showSaveProgressPopup = false;
|
this.showSaveProgressPopup = false;
|
||||||
|
|
||||||
|
await this.dispatchStoreAction(
|
||||||
|
storeActions.SAVE_PAGE_DATA,
|
||||||
|
{
|
||||||
|
page: fmgPageValues.QUOTE,
|
||||||
|
data: { saveProgressPopupSkipped: true },
|
||||||
|
},
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
if (this.skipToInsurance) {
|
if (this.skipToInsurance) {
|
||||||
// skip ahead now if in IGQ skip experiment
|
// skip ahead now if in IGQ skip experiment
|
||||||
await this.skip(true, externalParamPackageLabels.GLASS_ONLY);
|
await this.skip(true, externalParamPackageLabels.GLASS_ONLY);
|
||||||
|
|
@ -839,7 +891,7 @@ export default {
|
||||||
|
|
||||||
&.funnel-header-wrapper,
|
&.funnel-header-wrapper,
|
||||||
&.save-progress-popup {
|
&.save-progress-popup {
|
||||||
display: block;
|
display: flex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
:answers="servicePackageAnswers"
|
:answers="servicePackageAnswers"
|
||||||
:groupName="groupName"
|
:groupName="groupName"
|
||||||
buttonTypeString="servicePackageRadio"
|
buttonTypeString="servicePackageRadio"
|
||||||
:buttonTypeObject="hasPricingByDay() ? servicePackageRadioExperiment : servicePackageRadio"
|
:buttonTypeObject="getButtonTypeObject()"
|
||||||
v-model="selectedPackageName"
|
v-model="selectedPackageName"
|
||||||
:validationRules="validationRules"
|
:validationRules="validationRules"
|
||||||
:isRequired="isRequired"
|
:isRequired="isRequired"
|
||||||
|
|
@ -18,6 +18,7 @@ import baseMixin from "@/mixins/base-mixin.js";
|
||||||
import { processIfStatements } from "@/helpers/cms-content-helper";
|
import { processIfStatements } from "@/helpers/cms-content-helper";
|
||||||
import servicePackageRadio from "./service-package-radio/service-package-radio";
|
import servicePackageRadio from "./service-package-radio/service-package-radio";
|
||||||
import servicePackageRadioExperiment from "@/experiment-components/service-package-radio";
|
import servicePackageRadioExperiment from "@/experiment-components/service-package-radio";
|
||||||
|
import servicePackageRadioAfterpayExperiment from "@/experiment-components/service-package-radio-for-afterpay";
|
||||||
import { partTypeStrings } from "@/constants/part-type-strings";
|
import { partTypeStrings } from "@/constants/part-type-strings";
|
||||||
import { packageNames } from "@/constants/package-names";
|
import { packageNames } from "@/constants/package-names";
|
||||||
import {
|
import {
|
||||||
|
|
@ -61,6 +62,7 @@ export default {
|
||||||
return {
|
return {
|
||||||
servicePackageRadio: servicePackageRadio,
|
servicePackageRadio: servicePackageRadio,
|
||||||
servicePackageRadioExperiment: servicePackageRadioExperiment,
|
servicePackageRadioExperiment: servicePackageRadioExperiment,
|
||||||
|
servicePackageRadioAfterpayExperiment: servicePackageRadioAfterpayExperiment,
|
||||||
selectedPackageName: null,
|
selectedPackageName: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
@ -156,6 +158,7 @@ export default {
|
||||||
Text: this.isInsuranceSelected
|
Text: this.isInsuranceSelected
|
||||||
? false
|
? false
|
||||||
: "Special: Save $" + this.getServicePackageDiscountPrice(),
|
: "Special: Save $" + this.getServicePackageDiscountPrice(),
|
||||||
|
isInsuranceSelected: this.isInsuranceSelected,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
this.$emit("currentNumberOfPackages", modifiedAnswers.length);
|
this.$emit("currentNumberOfPackages", modifiedAnswers.length);
|
||||||
|
|
@ -237,6 +240,24 @@ export default {
|
||||||
"true"
|
"true"
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
isAfterpayBreakoutDisplay() {
|
||||||
|
return (
|
||||||
|
(!this.shouldHideRecalibration || !this.isRecalibrationOnOrder) &&
|
||||||
|
experimentMixin.methods.hasSettingEqualTo(
|
||||||
|
experimentSettings.DISPLAY_AFTERPAY_BREAKOUT_DISPLAY,
|
||||||
|
"true"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
getButtonTypeObject() {
|
||||||
|
if (this.isAfterpayBreakoutDisplay()) {
|
||||||
|
return this.servicePackageRadioAfterpayExperiment;
|
||||||
|
}
|
||||||
|
if (this.hasPricingByDay()) {
|
||||||
|
return this.servicePackageRadioExperiment;
|
||||||
|
}
|
||||||
|
return this.servicePackageRadio;
|
||||||
|
},
|
||||||
getHeaderTextFromCms(cmsWidgetName) {
|
getHeaderTextFromCms(cmsWidgetName) {
|
||||||
return this.getCmsContent(cmsWidgetName, "HeaderText");
|
return this.getCmsContent(cmsWidgetName, "HeaderText");
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
marginTopSizeOverride="1" />
|
marginTopSizeOverride="1" />
|
||||||
</template>
|
</template>
|
||||||
<locationAlerts cmsWidgetPrefix="LocationAlert-" ref="locationAlerts" />
|
<locationAlerts cmsWidgetPrefix="LocationAlert-" ref="locationAlerts" />
|
||||||
<datePickerForPricingByDay
|
<datePicker
|
||||||
customComponentId="dateQuestion"
|
customComponentId="dateQuestion"
|
||||||
selectableDatesSetting="custom"
|
selectableDatesSetting="custom"
|
||||||
ref="datePicker"
|
ref="datePicker"
|
||||||
|
|
@ -77,7 +77,7 @@ import navbar from "@/fmg-components/nav-bar/nav-bar";
|
||||||
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
|
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
|
||||||
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
|
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
|
||||||
import { Form, defineRule } from "vee-validate";
|
import { Form, defineRule } from "vee-validate";
|
||||||
import datePickerForPricingByDay from "@/experiment-components/date-picker-for-pricing-by-day";
|
import datePicker from "@/digital-components/date-picker/date-picker";
|
||||||
import locationAlerts from "@/layouts/schedule/location-alerts/location-alerts";
|
import locationAlerts from "@/layouts/schedule/location-alerts/location-alerts";
|
||||||
import timeSlotModalQuestion from "./time-slot-modal-question/time-slot-modal-question";
|
import timeSlotModalQuestion from "./time-slot-modal-question/time-slot-modal-question";
|
||||||
import textBlock from "@/digital-components/text-block/text-block";
|
import textBlock from "@/digital-components/text-block/text-block";
|
||||||
|
|
@ -298,14 +298,13 @@ export default {
|
||||||
);
|
);
|
||||||
|
|
||||||
// While Pricing By Day Experiment is active, using the updated datePicker
|
// While Pricing By Day Experiment is active, using the updated datePicker
|
||||||
const datePickerInitialDataPromise =
|
const datePickerInitialDataPromise = await datePicker.methods.loadInitialData({
|
||||||
await datePickerForPricingByDay.methods.loadInitialData({
|
// setup config options for date-picker
|
||||||
// setup config options for date-picker
|
selectableDatesSetting: "custom",
|
||||||
selectableDatesSetting: "custom",
|
initialViewRowsToShow: 5,
|
||||||
initialViewRowsToShow: 5,
|
customSelectableDatesCallback: getAvailableDates,
|
||||||
customSelectableDatesCallback: getAvailableDates,
|
preSelectedDate: preSelectedDate,
|
||||||
preSelectedDate: preSelectedDate,
|
});
|
||||||
});
|
|
||||||
|
|
||||||
// Get pricingByDayUpcharge needed for Pricing By Day
|
// Get pricingByDayUpcharge needed for Pricing By Day
|
||||||
const pricingByDayUpchargePartPromise = showPricingByDay
|
const pricingByDayUpchargePartPromise = showPricingByDay
|
||||||
|
|
@ -735,7 +734,7 @@ export default {
|
||||||
funnelSubHeader,
|
funnelSubHeader,
|
||||||
Form,
|
Form,
|
||||||
loadingModal,
|
loadingModal,
|
||||||
datePickerForPricingByDay,
|
datePicker,
|
||||||
locationAlerts,
|
locationAlerts,
|
||||||
timeSlotModalQuestion,
|
timeSlotModalQuestion,
|
||||||
textBlock,
|
textBlock,
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ afterEach(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("appointment-type-question.vue", () => {
|
describe("appointment-type-question.vue", () => {
|
||||||
it("Should display all options if both in-shop and mobile are available", async () => {
|
it("Should display all options if in-shop, mobile, and dropoff are available", async () => {
|
||||||
// Arrange/Act
|
// Arrange/Act
|
||||||
const { wrapper } = setupMocks({
|
const { wrapper } = setupMocks({
|
||||||
mixins: [mockMixin],
|
mixins: [mockMixin],
|
||||||
|
|
@ -67,6 +67,7 @@ describe("appointment-type-question.vue", () => {
|
||||||
cmsWidgetName: cmsWidgetName,
|
cmsWidgetName: cmsWidgetName,
|
||||||
isServiceableInshop: true,
|
isServiceableInshop: true,
|
||||||
isServiceableMobile: true,
|
isServiceableMobile: true,
|
||||||
|
isServiceableDropoff: true,
|
||||||
},
|
},
|
||||||
mountOptions: {
|
mountOptions: {
|
||||||
attachTo: document.body,
|
attachTo: document.body,
|
||||||
|
|
@ -132,6 +133,40 @@ describe("appointment-type-question.vue", () => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Should display only the In-Shop and Drop-Off answers when mobile service is not available", async () => {
|
||||||
|
// Arrange/Act
|
||||||
|
const { wrapper } = setupMocks({
|
||||||
|
mixins: [mockMixin],
|
||||||
|
props: {
|
||||||
|
cmsWidgetName: cmsWidgetName,
|
||||||
|
isServiceableInshop: true,
|
||||||
|
isServiceableMobile: false,
|
||||||
|
isServiceableDropoff: true,
|
||||||
|
},
|
||||||
|
mountOptions: {
|
||||||
|
attachTo: document.body,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(wrapper.vm.answersToDisplay).toEqual([
|
||||||
|
{
|
||||||
|
AnswerImageUrl: "",
|
||||||
|
Name: "Inshop",
|
||||||
|
SubText: "",
|
||||||
|
SubWidgetName: "",
|
||||||
|
Text: "In-shop",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AnswerImageUrl: "",
|
||||||
|
Name: "Dropoff",
|
||||||
|
SubText: "",
|
||||||
|
SubWidgetName: "",
|
||||||
|
Text: "Drop-off",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
it("Should display only the Mobile answer when only mobile service is available", async () => {
|
it("Should display only the Mobile answer when only mobile service is available", async () => {
|
||||||
// Arrange/Act
|
// Arrange/Act
|
||||||
const { wrapper } = setupMocks({
|
const { wrapper } = setupMocks({
|
||||||
|
|
@ -140,6 +175,7 @@ describe("appointment-type-question.vue", () => {
|
||||||
cmsWidgetName: cmsWidgetName,
|
cmsWidgetName: cmsWidgetName,
|
||||||
isServiceableInshop: false,
|
isServiceableInshop: false,
|
||||||
isServiceableMobile: true,
|
isServiceableMobile: true,
|
||||||
|
isServiceableDropoff: false,
|
||||||
},
|
},
|
||||||
mountOptions: {
|
mountOptions: {
|
||||||
attachTo: document.body,
|
attachTo: document.body,
|
||||||
|
|
@ -172,6 +208,7 @@ describe("appointment-type-question.vue", () => {
|
||||||
cmsWidgetName: cmsWidgetName,
|
cmsWidgetName: cmsWidgetName,
|
||||||
isServiceableInshop: true,
|
isServiceableInshop: true,
|
||||||
isServiceableMobile: true,
|
isServiceableMobile: true,
|
||||||
|
isServiceableDropoff: true,
|
||||||
},
|
},
|
||||||
mountOptions: {
|
mountOptions: {
|
||||||
attachTo: document.body,
|
attachTo: document.body,
|
||||||
|
|
@ -205,6 +242,7 @@ describe("appointment-type-question.vue", () => {
|
||||||
cmsWidgetName: cmsWidgetName,
|
cmsWidgetName: cmsWidgetName,
|
||||||
isServiceableInshop: false,
|
isServiceableInshop: false,
|
||||||
isServiceableMobile: false,
|
isServiceableMobile: false,
|
||||||
|
isServiceableDropoff: false,
|
||||||
},
|
},
|
||||||
mountOptions: {
|
mountOptions: {
|
||||||
attachTo: document.body,
|
attachTo: document.body,
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ function resetMockStoreData() {
|
||||||
registration: {
|
registration: {
|
||||||
licensePlate: null,
|
licensePlate: null,
|
||||||
},
|
},
|
||||||
|
isServiceable: true,
|
||||||
},
|
},
|
||||||
serviceLocation: {
|
serviceLocation: {
|
||||||
address: null,
|
address: null,
|
||||||
|
|
@ -187,6 +188,7 @@ function applyMockStoreDataToGetters() {
|
||||||
policy: mockStoreData.policy,
|
policy: mockStoreData.policy,
|
||||||
externalParameterServiceZip: mockStoreData.externalParameterServiceZip,
|
externalParameterServiceZip: mockStoreData.externalParameterServiceZip,
|
||||||
emailOrSms: mockStoreData.customer.emailAddress,
|
emailOrSms: mockStoreData.customer.emailAddress,
|
||||||
|
vehicle: mockStoreData.vehicle,
|
||||||
};
|
};
|
||||||
store.state.order = mockStoreData;
|
store.state.order = mockStoreData;
|
||||||
store.state.applicationUser.experiments = mockExperimentSettings;
|
store.state.applicationUser.experiments = mockExperimentSettings;
|
||||||
|
|
@ -509,6 +511,5 @@ function setupMocks({ customMountOptions, customZipQuery, customZipDataResponse
|
||||||
];
|
];
|
||||||
|
|
||||||
const wrapper = shallowMount(serviceZip, mountOptions);
|
const wrapper = shallowMount(serviceZip, mountOptions);
|
||||||
|
|
||||||
return wrapper;
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,8 @@ export default {
|
||||||
return {
|
return {
|
||||||
serviceZipCode: this.getZipFromStore() ?? this.$route.query.zipcode,
|
serviceZipCode: this.getZipFromStore() ?? this.$route.query.zipcode,
|
||||||
emailOrSms: this.getEmailOrSmsFromStore(),
|
emailOrSms: this.getEmailOrSmsFromStore(),
|
||||||
|
carId: this.getCarIdfromStore(),
|
||||||
|
isHeavyTruck: this.getIsVehicleHeavyTruckServiceableFromStore(),
|
||||||
displayInvalidZipAlert: false,
|
displayInvalidZipAlert: false,
|
||||||
displayNonServiceableZipAlert: false,
|
displayNonServiceableZipAlert: false,
|
||||||
};
|
};
|
||||||
|
|
@ -184,6 +186,20 @@ export default {
|
||||||
arePagePrerequisitesValid() {
|
arePagePrerequisitesValid() {
|
||||||
return store.getters.damage.isRepair || store.getters.damage.glassToReplace?.length > 0;
|
return store.getters.damage.isRepair || store.getters.damage.glassToReplace?.length > 0;
|
||||||
},
|
},
|
||||||
|
getCarIdfromStore() {
|
||||||
|
return store.getters.vehicle.carId;
|
||||||
|
},
|
||||||
|
getIsVehicleHeavyTruckServiceableFromStore() {
|
||||||
|
return store.getters.vehicle.isBigTruck && store.getters.vehicle.canSafeliteService;
|
||||||
|
},
|
||||||
|
async findClosestApplicableShops() {
|
||||||
|
return await this.dispatchStoreActionWithLogging(
|
||||||
|
storeActions.GET_CLOSEST_APPLICABLE_SHOPS,
|
||||||
|
{ zip: this.serviceZipCode, carId: this.carId },
|
||||||
|
"service-zip"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
async backButtonAction() {
|
async backButtonAction() {
|
||||||
const skipVin = await skipVinLookup();
|
const skipVin = await skipVinLookup();
|
||||||
// route to move backwards
|
// route to move backwards
|
||||||
|
|
@ -252,6 +268,26 @@ export default {
|
||||||
return this.$refs.navbar.removeLoader();
|
return this.$refs.navbar.removeLoader();
|
||||||
}
|
}
|
||||||
this.displayNonServiceableZipAlert = false;
|
this.displayNonServiceableZipAlert = false;
|
||||||
|
if (this.isHeavyTruck) {
|
||||||
|
const closestShops = await this.findClosestApplicableShops(
|
||||||
|
this.serviceZipCode,
|
||||||
|
this.carId
|
||||||
|
);
|
||||||
|
this.displayNoServiceAlert = !closestShops?.data?.providers?.length;
|
||||||
|
if (this.displayNoServiceAlert) {
|
||||||
|
return this.$refs.navbar.removeLoader();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.isHeavyTruck) {
|
||||||
|
const closestShops = await this.findClosestApplicableShops(
|
||||||
|
this.serviceZipCode,
|
||||||
|
this.carId
|
||||||
|
);
|
||||||
|
this.displayNoServiceAlert = !closestShops?.data?.providers?.length;
|
||||||
|
if (this.displayNoServiceAlert) {
|
||||||
|
return this.$refs.navbar.removeLoader();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const payment = this.$store.getters.payment;
|
const payment = this.$store.getters.payment;
|
||||||
const policy = this.$store.getters.policy;
|
const policy = this.$store.getters.policy;
|
||||||
|
|
@ -309,6 +345,8 @@ export default {
|
||||||
watch: {
|
watch: {
|
||||||
serviceZipCode() {
|
serviceZipCode() {
|
||||||
this.displayNonServiceableZipAlert = false;
|
this.displayNonServiceableZipAlert = false;
|
||||||
|
this.displayNoServiceAlert = false;
|
||||||
|
this.displayNoServiceAlert = false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ jest.mock("@/store", () => ({
|
||||||
damage: {
|
damage: {
|
||||||
glassToReplace: "windshield",
|
glassToReplace: "windshield",
|
||||||
},
|
},
|
||||||
|
isHeavyTruckVehicle: true,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -197,6 +197,8 @@ export default {
|
||||||
vin: this.getVinFromStore(),
|
vin: this.getVinFromStore(),
|
||||||
serviceZipCode: this.getZipFromStore() ?? this.$route.query.zipcode,
|
serviceZipCode: this.getZipFromStore() ?? this.$route.query.zipcode,
|
||||||
emailOrSms: this.getEmailOrSmsFromStore(),
|
emailOrSms: this.getEmailOrSmsFromStore(),
|
||||||
|
carId: this.getCarIdfromStore(),
|
||||||
|
isHeavyTruck: this.getIsVehicleHeavyTruckServiceableFromStore(),
|
||||||
isCarIdDifferent: false,
|
isCarIdDifferent: false,
|
||||||
customAlertData: {},
|
customAlertData: {},
|
||||||
previouslyEnteredCarId: "",
|
previouslyEnteredCarId: "",
|
||||||
|
|
@ -245,6 +247,16 @@ export default {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getIsVehicleHeavyTruckServiceableFromStore() {
|
||||||
|
return store.getters.vehicle.isBigTruck && store.getters.vehicle.canSafeliteService;
|
||||||
|
},
|
||||||
|
async findClosestApplicableShops() {
|
||||||
|
return await this.dispatchStoreActionWithLogging(
|
||||||
|
storeActions.GET_CLOSEST_APPLICABLE_SHOPS,
|
||||||
|
{ zip: this.serviceZipCode, carId: this.carId },
|
||||||
|
"vin-lookup"
|
||||||
|
);
|
||||||
|
},
|
||||||
async forwardButtonAction() {
|
async forwardButtonAction() {
|
||||||
this.resetAlerts();
|
this.resetAlerts();
|
||||||
|
|
||||||
|
|
@ -277,7 +289,6 @@ export default {
|
||||||
return this.$refs.navbar.removeLoader();
|
return this.$refs.navbar.removeLoader();
|
||||||
}
|
}
|
||||||
this.displayInvalidZipAlert = false;
|
this.displayInvalidZipAlert = false;
|
||||||
|
|
||||||
// If either lookup fails, remove the loader and stop processing the page.
|
// If either lookup fails, remove the loader and stop processing the page.
|
||||||
if (!resultMap.vehicleLookupResponse || !resultMap.zipCodeData.isServiceable) {
|
if (!resultMap.vehicleLookupResponse || !resultMap.zipCodeData.isServiceable) {
|
||||||
// If the vehicle result is undefined, the vin entered was invalid.
|
// If the vehicle result is undefined, the vin entered was invalid.
|
||||||
|
|
@ -329,6 +340,18 @@ export default {
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
if (this.isHeavyTruck) {
|
||||||
|
closestShops = await this.findClosestApplicableShops(
|
||||||
|
this.serviceZipCode,
|
||||||
|
this.carId
|
||||||
|
);
|
||||||
|
this.displayNoServiceAlert = !closestShops?.data?.providers?.length;
|
||||||
|
if (this.displayNoServiceAlert) {
|
||||||
|
return this.$refs.navbar.removeLoader();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no value due to field being optional, blank both phone and email address
|
||||||
|
|
||||||
// if no value due to field being optional, blank both phone and email address
|
// if no value due to field being optional, blank both phone and email address
|
||||||
if (!this.emailOrSms) {
|
if (!this.emailOrSms) {
|
||||||
|
|
@ -393,7 +416,6 @@ export default {
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if no value due to field being optional, blank both phone and email address
|
// if no value due to field being optional, blank both phone and email address
|
||||||
if (!this.emailOrSms) {
|
if (!this.emailOrSms) {
|
||||||
await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, "", false);
|
await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, "", false);
|
||||||
|
|
@ -476,6 +498,8 @@ export default {
|
||||||
this.displayInvalidZipAlert = false;
|
this.displayInvalidZipAlert = false;
|
||||||
this.displayVinNotFoundAlert = false;
|
this.displayVinNotFoundAlert = false;
|
||||||
this.displayVinScanFailedAlert = false;
|
this.displayVinScanFailedAlert = false;
|
||||||
|
this.displayNoServiceAlert = false;
|
||||||
|
this.displayNoServiceAlert = false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ const routes = [
|
||||||
log(` --from.redirectedFrom:>${JSON.stringify(from.redirectedFrom)}<`, "");
|
log(` --from.redirectedFrom:>${JSON.stringify(from.redirectedFrom)}<`, "");
|
||||||
|
|
||||||
await analyticsMixin.methods.validateSession();
|
await analyticsMixin.methods.validateSession();
|
||||||
|
var fromHeritage = getQuerystringParameter(queryStrings.FROM_HERITAGE);
|
||||||
var offerQuote = getQuerystringParameter(queryStrings.OFFER_QUOTE);
|
var offerQuote = getQuerystringParameter(queryStrings.OFFER_QUOTE);
|
||||||
log(" --offerQuote: ", offerQuote);
|
log(" --offerQuote: ", offerQuote);
|
||||||
// after session is validated, remove the fromHeritage querystring if it exists so session expiration works
|
// after session is validated, remove the fromHeritage querystring if it exists so session expiration works
|
||||||
|
|
@ -185,7 +186,7 @@ const routes = [
|
||||||
}
|
}
|
||||||
|
|
||||||
//Update CASH tab when return from heritage and have offerQuote param
|
//Update CASH tab when return from heritage and have offerQuote param
|
||||||
if (to.query.fmgPage !== fmgPageValues.QUOTE && offerQuote) {
|
if (offerQuote === "true" && fromHeritage === "true") {
|
||||||
store.commit(storeMutations.UPDATE_IS_INSURANCE, false);
|
store.commit(storeMutations.UPDATE_IS_INSURANCE, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -678,6 +678,9 @@ export const mutations = {
|
||||||
imageUrl: sessionInformation.order.vehicle?.imageUrl,
|
imageUrl: sessionInformation.order.vehicle?.imageUrl,
|
||||||
imageVifNumber: sessionInformation.order.vehicle?.imageVifNumber,
|
imageVifNumber: sessionInformation.order.vehicle?.imageVifNumber,
|
||||||
imageColor: sessionInformation.order.vehicle?.imageVifColor,
|
imageColor: sessionInformation.order.vehicle?.imageVifColor,
|
||||||
|
vehicleSubType: sessionInformation.order.vehicle?.vehicleSubType,
|
||||||
|
vehicleSpecialClass: sessionInformation.order.vehicle?.vehicleSpecialClass,
|
||||||
|
isBigTruck: sessionInformation.order.vehicle?.isBigTruck,
|
||||||
});
|
});
|
||||||
|
|
||||||
state.order.damage.glassToReplace = sessionInformation.order.damage.glassToReplace;
|
state.order.damage.glassToReplace = sessionInformation.order.damage.glassToReplace;
|
||||||
|
|
@ -3214,8 +3217,8 @@ export const actions = {
|
||||||
context.commit(storeMutations.UPDATE_CUSTOMER_WAITLIST_REQUESTED, waitListRequested);
|
context.commit(storeMutations.UPDATE_CUSTOMER_WAITLIST_REQUESTED, waitListRequested);
|
||||||
},
|
},
|
||||||
|
|
||||||
savePageData(context, emailOrSms) {
|
savePageData(context, pageData) {
|
||||||
context.commit(storeMutations.UPDATE_PAGE_DATA, emailOrSms);
|
context.commit(storeMutations.UPDATE_PAGE_DATA, pageData);
|
||||||
},
|
},
|
||||||
|
|
||||||
saveVin(context, { isSelectedGlassAvailableForVehicle, vehicleInfo }) {
|
saveVin(context, { isSelectedGlassAvailableForVehicle, vehicleInfo }) {
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ export default {
|
||||||
color: $white;
|
color: $white;
|
||||||
background: linear-gradient(84.45deg, #125b7e 0%, #3b8fb8 100%);
|
background: linear-gradient(84.45deg, #125b7e 0%, #3b8fb8 100%);
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
z-index: 5;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:checked:focus + .list-button-horizontal-content {
|
&:checked:focus + .list-button-horizontal-content {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue