Merge branch 'develop' into feature/CASH-185
This commit is contained in:
commit
ea03889bb1
51 changed files with 2624 additions and 235 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"tabWidth": 4,
|
"tabWidth": 4,
|
||||||
|
"htmlWhitespaceSensitivity": "ignore",
|
||||||
"bracketSameLine": true,
|
"bracketSameLine": true,
|
||||||
"printWidth": 100,
|
"printWidth": 100,
|
||||||
"trailingComma" : "es5"
|
"trailingComma" : "es5"
|
||||||
|
|
|
||||||
|
|
@ -28,12 +28,13 @@ module.exports = {
|
||||||
"!src/layouts/insurance/*.vue", // Temp test exclusion while in development
|
"!src/layouts/insurance/*.vue", // Temp test exclusion while in development
|
||||||
"!src/layouts/insurance-company/*.vue", // Temp test exclusion while in development
|
"!src/layouts/insurance-company/*.vue", // Temp test exclusion while in development
|
||||||
"!src/layouts/insurance-company/insurance-company-question/*.vue", // Temp test exclusion while in development
|
"!src/layouts/insurance-company/insurance-company-question/*.vue", // Temp test exclusion while in development
|
||||||
|
"!src/experiment-components/*.vue",
|
||||||
// END
|
// END
|
||||||
], // ! means exclude from coverage.
|
], // ! means exclude from coverage.
|
||||||
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: 75,
|
statements: 74,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// 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
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ const experimentSettings = {
|
||||||
DISPLAY_MSR: "DisplayMSR",
|
DISPLAY_MSR: "DisplayMSR",
|
||||||
DISPLAY_WAITLIST: "DisplayWaitlist2.0",
|
DISPLAY_WAITLIST: "DisplayWaitlist2.0",
|
||||||
WAITLIST_THRESHOLD_DAYS: "Waitlist_Threshold_Days",
|
WAITLIST_THRESHOLD_DAYS: "Waitlist_Threshold_Days",
|
||||||
|
PRICING_BY_DAY: "DisplayPricingByDay",
|
||||||
|
FOSTER_LOVE: "DisplayFosterLove",
|
||||||
};
|
};
|
||||||
|
|
||||||
const experimentTriggers = {
|
const experimentTriggers = {
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,7 @@ const storeActions = {
|
||||||
|
|
||||||
CREATE_SUBMITTED_ORDER: "createSubmittedOrder",
|
CREATE_SUBMITTED_ORDER: "createSubmittedOrder",
|
||||||
RESET_SUBMITTED_ORDER: "resetSubmittedOrder",
|
RESET_SUBMITTED_ORDER: "resetSubmittedOrder",
|
||||||
|
ADD_DONATION_TO_SUBMITTED_ORDER: "addDonationToSubmittedOrder",
|
||||||
RESET_EXTERNAL_PARAMETER_STATE: "resetExternalParameterState",
|
RESET_EXTERNAL_PARAMETER_STATE: "resetExternalParameterState",
|
||||||
UPDATE_EXTERNAL_PARAMETER_MMS: "updateExternalParameterMMS",
|
UPDATE_EXTERNAL_PARAMETER_MMS: "updateExternalParameterMMS",
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,6 @@ const storeMutations = {
|
||||||
UPDATE_VEHICLE_IMAGE_COLOR: "updateVehicleImageColor",
|
UPDATE_VEHICLE_IMAGE_COLOR: "updateVehicleImageColor",
|
||||||
UPDATE_VEHICLE_VIN: "updateVehicleVin",
|
UPDATE_VEHICLE_VIN: "updateVehicleVin",
|
||||||
UPDATE_VEHICLE: "updateVehicle",
|
UPDATE_VEHICLE: "updateVehicle",
|
||||||
UPDATE_VEHICLE_MOBILE_STATIC_RECALIBRATION_APPLICABLE:
|
|
||||||
"updateIsMobileStaticRecalibrationApplicable",
|
|
||||||
|
|
||||||
UPDATE_IS_REPAIR: "updateIsRepair",
|
UPDATE_IS_REPAIR: "updateIsRepair",
|
||||||
UPDATE_NUMBER_OF_CHIPS: "updateNumberOfChips",
|
UPDATE_NUMBER_OF_CHIPS: "updateNumberOfChips",
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ https://safelite.atlassian.net/wiki/spaces/DC/pages/76644418/Button+Question+Com
|
||||||
:additionalButtonData="answer.additionalButtonData"
|
:additionalButtonData="answer.additionalButtonData"
|
||||||
:lastValuePushedToGa="lastValuePushedToGa"
|
:lastValuePushedToGa="lastValuePushedToGa"
|
||||||
:setLastValuePushedToGa="setLastValuePushedToGa"
|
:setLastValuePushedToGa="setLastValuePushedToGa"
|
||||||
|
:isInsuranceSelected="isInsuranceSelected"
|
||||||
:suppressError="suppressError"
|
:suppressError="suppressError"
|
||||||
@buttonEvent="handleButtonEvent"
|
@buttonEvent="handleButtonEvent"
|
||||||
v-model="selectedValues" />
|
v-model="selectedValues" />
|
||||||
|
|
@ -139,6 +140,7 @@ export default {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
isInsuranceSelected: Boolean,
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const propsClone = Object.assign({}, props);
|
const propsClone = Object.assign({}, props);
|
||||||
|
|
|
||||||
|
|
@ -17,18 +17,40 @@
|
||||||
<div
|
<div
|
||||||
v-if="calendarViewDirection === 'future'"
|
v-if="calendarViewDirection === 'future'"
|
||||||
class="legend caption d-flex align-items-center justify-content-end">
|
class="legend caption d-flex align-items-center justify-content-end">
|
||||||
<span class="legend-circle me-1"></span> = Available
|
<span class="legend-circle me-1"></span>
|
||||||
|
= Available
|
||||||
</div>
|
</div>
|
||||||
<div class="separator-line"></div>
|
<div class="separator-line"></div>
|
||||||
<div class="nav-back ps-3"><button></button></div>
|
<div class="nav-back ps-3"><button></button></div>
|
||||||
<div class="nav-forward pe-3"><button></button></div>
|
<div class="nav-forward pe-3"><button></button></div>
|
||||||
<div class="grid-item caption"><span class="sr-only">Sunday</span>S</div>
|
<div class="grid-item caption">
|
||||||
<div class="grid-item caption"><span class="sr-only">Monday</span>M</div>
|
<span class="sr-only">Sunday</span>
|
||||||
<div class="grid-item caption"><span class="sr-only">Tuesday</span>T</div>
|
S
|
||||||
<div class="grid-item caption"><span class="sr-only">Wednesday</span>W</div>
|
</div>
|
||||||
<div class="grid-item caption"><span class="sr-only">Thursday</span>T</div>
|
<div class="grid-item caption">
|
||||||
<div class="grid-item caption"><span class="sr-only">Friday</span>F</div>
|
<span class="sr-only">Monday</span>
|
||||||
<div class="grid-item caption"><span class="sr-only">Saturday</span>S</div>
|
M
|
||||||
|
</div>
|
||||||
|
<div class="grid-item caption">
|
||||||
|
<span class="sr-only">Tuesday</span>
|
||||||
|
T
|
||||||
|
</div>
|
||||||
|
<div class="grid-item caption">
|
||||||
|
<span class="sr-only">Wednesday</span>
|
||||||
|
W
|
||||||
|
</div>
|
||||||
|
<div class="grid-item caption">
|
||||||
|
<span class="sr-only">Thursday</span>
|
||||||
|
T
|
||||||
|
</div>
|
||||||
|
<div class="grid-item caption">
|
||||||
|
<span class="sr-only">Friday</span>
|
||||||
|
F
|
||||||
|
</div>
|
||||||
|
<div class="grid-item caption">
|
||||||
|
<span class="sr-only">Saturday</span>
|
||||||
|
S
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
v-for="date in month.dates"
|
v-for="date in month.dates"
|
||||||
:key="date.inputValue"
|
:key="date.inputValue"
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,7 @@
|
||||||
:maxlength="maxLength"
|
:maxlength="maxLength"
|
||||||
role="textbox"
|
role="textbox"
|
||||||
aria-multiline="true"
|
aria-multiline="true"
|
||||||
:aria-required="isRequired">
|
:aria-required="isRequired"></textarea>
|
||||||
</textarea>
|
|
||||||
<p
|
<p
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
class="caption mt-2 mb-0 w-100"
|
class="caption mt-2 mb-0 w-100"
|
||||||
|
|
|
||||||
|
|
@ -75,9 +75,9 @@
|
||||||
class="d-inline-flex small mt-1"
|
class="d-inline-flex small mt-1"
|
||||||
aria-atomic="true"
|
aria-atomic="true"
|
||||||
aria-live="polite"
|
aria-live="polite"
|
||||||
:class="[centerErrorMessage ? 'center-error-message' : '']"
|
:class="[centerErrorMessage ? 'center-error-message' : '']">
|
||||||
>{{ errorMessage }}</span
|
{{ errorMessage }}
|
||||||
>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
1079
src/experiment-components/date-picker-for-pricing-by-day.vue
Normal file
1079
src/experiment-components/date-picker-for-pricing-by-day.vue
Normal file
File diff suppressed because it is too large
Load diff
284
src/experiment-components/donation-block.vue
Normal file
284
src/experiment-components/donation-block.vue
Normal file
|
|
@ -0,0 +1,284 @@
|
||||||
|
<template>
|
||||||
|
<div class="donation-block">
|
||||||
|
<form ref="donationForm" id="donation-form" @submit.prevent="handleDonationAction">
|
||||||
|
<div class="row g-0">
|
||||||
|
<div class="col-12">
|
||||||
|
<p class="headline text-center" v-html="DonationHeadline"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row-align d-flex px-4">
|
||||||
|
<div>
|
||||||
|
<img class="w-100 mt-1 me-4" :src="LogoImage" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="small subheadline mb-2" v-html="DonationSubHeadline"></p>
|
||||||
|
<p class="caption mb-0" v-html="DonationBodyText"></p>
|
||||||
|
<textLink
|
||||||
|
linkType="newWindowLink"
|
||||||
|
text="Learn more"
|
||||||
|
:href="DonationBodySubText"
|
||||||
|
target="_blank" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="px-4"
|
||||||
|
:class="[showDonationSuccess ? 'hide-donation-block' : 'show-donation-block']">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="donation-slider my-4">
|
||||||
|
<input
|
||||||
|
name="amount"
|
||||||
|
id="one"
|
||||||
|
type="radio"
|
||||||
|
:value="donationValues[0]"
|
||||||
|
checked />
|
||||||
|
<label for="one" class="label">$1</label>
|
||||||
|
<input
|
||||||
|
name="amount"
|
||||||
|
id="three"
|
||||||
|
type="radio"
|
||||||
|
:value="donationValues[1]" />
|
||||||
|
<label for="three" class="label">$3</label>
|
||||||
|
<input
|
||||||
|
name="amount"
|
||||||
|
id="five"
|
||||||
|
type="radio"
|
||||||
|
:value="donationValues[2]" />
|
||||||
|
<label for="five" class="label">$5</label>
|
||||||
|
<div class="indicator-bar"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<button
|
||||||
|
class="btn btn-secondary w-100 mb-2"
|
||||||
|
v-html="DonationAddButtonWidget"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row px-4">
|
||||||
|
<div class="col-12">
|
||||||
|
<div
|
||||||
|
v-if="showDonationSuccess"
|
||||||
|
class="d-flex justify-content-center align-items-center btn-success w-100 mb-2"
|
||||||
|
v-html="DonationThanksAlertWidget"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row px-4">
|
||||||
|
<div class="col">
|
||||||
|
<alert
|
||||||
|
v-if="showDonationError"
|
||||||
|
cmsWidgetName="DonationErrorWidget"
|
||||||
|
alertClass="alert-danger" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<p class="my-4 mx-0 text-center caption" v-html="DonationFooterText"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import textLink from "@/ux-components/text-link/text-link";
|
||||||
|
import alert from "@/ux-components/alert/alert";
|
||||||
|
export default {
|
||||||
|
name: "donationBlock",
|
||||||
|
props: {
|
||||||
|
cmsWidgetName: String,
|
||||||
|
modelValue: String,
|
||||||
|
showDonationSuccess: Boolean,
|
||||||
|
showDonationError: Boolean,
|
||||||
|
donationValues: Array,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
DonationHeadline() {
|
||||||
|
return this.getCmsContent(this.cmsWidgetName, "HeaderText");
|
||||||
|
},
|
||||||
|
DonationSubHeadline() {
|
||||||
|
return this.getCmsContent(this.cmsWidgetName, "SubheaderText");
|
||||||
|
},
|
||||||
|
DonationBodyText() {
|
||||||
|
return this.getCmsContent(this.cmsWidgetName, "BodyText");
|
||||||
|
},
|
||||||
|
DonationBodySubText() {
|
||||||
|
return this.getCmsContent(this.cmsWidgetName, "BodyText2");
|
||||||
|
},
|
||||||
|
DonationAddButtonWidget() {
|
||||||
|
return this.getCmsContent("DonationAddButtonWidget", "Text");
|
||||||
|
},
|
||||||
|
DonationThanksAlertWidget() {
|
||||||
|
return this.getCmsContent("DonationThanksAlertWidget", "Text");
|
||||||
|
},
|
||||||
|
DonationFooterText() {
|
||||||
|
return this.getCmsContent(this.cmsWidgetName, "FooterText");
|
||||||
|
},
|
||||||
|
LogoImage() {
|
||||||
|
return this.getCmsContent("DonationWidget", "Image");
|
||||||
|
},
|
||||||
|
selectedValue: {
|
||||||
|
get: function () {
|
||||||
|
return this.modelValue?.toString();
|
||||||
|
},
|
||||||
|
set: function (newValue) {
|
||||||
|
this.$emit("update:modelValue", newValue);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleDonationAction(submitEvent) {
|
||||||
|
console.log(
|
||||||
|
"running handleDonationAction(), submitEvent.target.elements.amount.value: ",
|
||||||
|
submitEvent.target.elements.amount.value
|
||||||
|
);
|
||||||
|
this.selectedValue = submitEvent.target.elements.amount.value;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
textLink,
|
||||||
|
alert,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.donation-block {
|
||||||
|
padding: 1rem 0;
|
||||||
|
background-color: $gray-100;
|
||||||
|
border-radius: 0;
|
||||||
|
|
||||||
|
.hide-donation-block {
|
||||||
|
max-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-top: 1rem;
|
||||||
|
opacity: 0;
|
||||||
|
transition: all 250ms ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-donation-block {
|
||||||
|
max-height: 136px;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-top: 0;
|
||||||
|
opacity: 1;
|
||||||
|
transition: all 250ms ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
&.headline {
|
||||||
|
font-family: AvertaBold;
|
||||||
|
color: $red;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.subheadline {
|
||||||
|
font-family: AvertaBold;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(a) {
|
||||||
|
&.new-window-link {
|
||||||
|
color: $blue;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.donation-slider {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
background: $blue-100;
|
||||||
|
border-radius: 3rem;
|
||||||
|
box-shadow: 0px 0px 4px 1px rgba(0, 112, 209, 1) inset;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
.label {
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
width: 33.3333%;
|
||||||
|
height: 3rem;
|
||||||
|
padding: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color 200ms ease-out;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: $blue;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.indicator-bar {
|
||||||
|
width: 33.3333%;
|
||||||
|
height: 3rem;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -2rem;
|
||||||
|
background: $blue;
|
||||||
|
border-radius: 3rem;
|
||||||
|
box-shadow: 0px 0px 0px 1px rgba(0, 112, 209, 1) inset;
|
||||||
|
transition: transform 750ms cubic-bezier(0.02, 0.94, 0.09, 0.97);
|
||||||
|
transform: translate3d(2rem, 0, 0);
|
||||||
|
}
|
||||||
|
.indicator-bar {
|
||||||
|
background: $blue;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
&#one:checked {
|
||||||
|
~ .indicator-bar {
|
||||||
|
transform: translateX(2rem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&#three:checked {
|
||||||
|
~ .indicator-bar {
|
||||||
|
transform: translateX(calc(100% + 2rem));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&#five:checked {
|
||||||
|
~ .indicator-bar {
|
||||||
|
transform: translateX(calc(200% + 2rem));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked {
|
||||||
|
+ label {
|
||||||
|
color: $white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
input[type="radio"] {
|
||||||
|
&:not(:checked),
|
||||||
|
&:checked {
|
||||||
|
position: absolute;
|
||||||
|
left: -999999px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.alert {
|
||||||
|
border: 1px solid $red;
|
||||||
|
}
|
||||||
|
.btn-success {
|
||||||
|
background: $green-100;
|
||||||
|
border: 1px solid $green-400;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
height: 3rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
&:before {
|
||||||
|
content: "";
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 17 16'%3E%3Cg clip-path='url(%23a)'%3E%3Cpath fill='%230C7E47' d='M8.5 0a8 8 0 1 0 0 16 8 8 0 0 0 0-16Zm3.7 6.171-4.449 4.45a.559.559 0 0 1-.8 0l-2.16-2.16a.563.563 0 0 1 .792-.8l1.76 1.76 4.066-4.042a.563.563 0 1 1 .792.8v-.008Z'/%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='a'%3E%3Cpath fill='%23fff' d='M.5 0h16v16H.5z'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E");
|
||||||
|
width: 1rem;
|
||||||
|
height: 0.95rem;
|
||||||
|
position: relative;
|
||||||
|
left: -0.25rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
493
src/experiment-components/service-package-radio.vue
Normal file
493
src/experiment-components/service-package-radio.vue
Normal file
|
|
@ -0,0 +1,493 @@
|
||||||
|
<template>
|
||||||
|
<baseInputButton v-bind="$props" @buttonClicked="handleAnswerChange" v-model="selectedValue">
|
||||||
|
<div
|
||||||
|
class="package-label"
|
||||||
|
:class="[
|
||||||
|
this.buttonLabelSubCopy ? 'has-subheader' : '',
|
||||||
|
this.isInsuranceSelected ? 'pricing-by-day-pkg-lbl-ins' : 'pricing-by-day-pkg-lbl',
|
||||||
|
!this.additionalButtonData.servicePackageDiscount ? 'adjust-top' : '',
|
||||||
|
]"
|
||||||
|
for="testradio">
|
||||||
|
<div class="package-specs">
|
||||||
|
<div v-if="this.additionalButtonData.servicePackageDiscount" class="row">
|
||||||
|
<div class="col md-6">
|
||||||
|
<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>
|
||||||
|
<span class="col md-6 service-package-discount-pricing-by-day">
|
||||||
|
<div>
|
||||||
|
<span v-if="!isInsuranceSelected" class="caption pricing-by-day">
|
||||||
|
as low as
|
||||||
|
</span>
|
||||||
|
<div
|
||||||
|
class="strikethrough-discount-price"
|
||||||
|
v-if="shouldDisplayStrikeThroughPrice"
|
||||||
|
v-html="this.additionalButtonData.strikeThroughPrice"></div>
|
||||||
|
<div class="discount-price" v-html="getDisplayPrice"></div>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<p class="m-0">
|
||||||
|
<span v-html="this.buttonLabel"></span>
|
||||||
|
<span class="pricing-info-pricing-by-day">
|
||||||
|
<span v-if="!isInsuranceSelected" class="caption pricing-by-day">
|
||||||
|
as low as
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="strikethrough-price"
|
||||||
|
v-if="shouldDisplayStrikeThroughPrice"
|
||||||
|
v-html="this.additionalButtonData.strikeThroughPrice"></span>
|
||||||
|
<span v-html="getDisplayPrice"></span>
|
||||||
|
</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="
|
||||||
|
this.additionalButtonData.servicePackageDiscount &&
|
||||||
|
this.additionalButtonData.Text
|
||||||
|
"
|
||||||
|
class="special-save-box">
|
||||||
|
<span v-html="this.additionalButtonData.Text"></span>
|
||||||
|
</div>
|
||||||
|
</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";
|
||||||
|
|
||||||
|
import experimentMixin from "@/mixins/experiment-mixin";
|
||||||
|
import { experimentSettings } from "@/constants/experiments";
|
||||||
|
import store from "@/store";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "servicePackageRadio",
|
||||||
|
mixins: [inputButtonWrapperMixin],
|
||||||
|
props: {
|
||||||
|
isInsuranceSelected: Boolean,
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
baseInputButton,
|
||||||
|
textLink,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
arrayOfListItemsFromBodyText() {
|
||||||
|
return this.getArrayOfListItemsFromRawCmsCopy(this.buttonBodyCopy);
|
||||||
|
},
|
||||||
|
shouldDisplayStrikeThroughPrice() {
|
||||||
|
const displayPrice = this.buttonAuxillaryCopy.substring(
|
||||||
|
this.buttonAuxillaryCopy.indexOf("$")
|
||||||
|
);
|
||||||
|
return displayPrice != this.additionalButtonData.strikeThroughPrice;
|
||||||
|
},
|
||||||
|
getDisplayPrice() {
|
||||||
|
return this.buttonAuxillaryCopy.split(".")[0];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.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.pricing-by-day-pkg-lbl.adjust-top {
|
||||||
|
/* Copying a component for an experiment will cause duplication of css classes.
|
||||||
|
This can cause problems if you want change the value of something only for the experiment
|
||||||
|
version of the component. So added a more specific selector for the parts I want to change
|
||||||
|
If this experiment wins, move this to package-label ::before/::after below
|
||||||
|
*/
|
||||||
|
&:before {
|
||||||
|
top: 0.9375rem;
|
||||||
|
}
|
||||||
|
&:after {
|
||||||
|
top: 1.875rem;
|
||||||
|
}
|
||||||
|
@include media-breakpoint-up(lg) {
|
||||||
|
&:before {
|
||||||
|
top: .5rem;
|
||||||
|
}
|
||||||
|
&:after {
|
||||||
|
top: 1.4375rem;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
span {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ .package-label.pricing-by-day-pkg-lbl-ins {
|
||||||
|
&:before {
|
||||||
|
top: .3125rem;
|
||||||
|
}
|
||||||
|
&:after {
|
||||||
|
top: 1.25rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ .package-label.pricing-by-day-pkg-lbl {
|
||||||
|
&:before {
|
||||||
|
top: 0.9375rem;
|
||||||
|
@include media-breakpoint-up(lg) {
|
||||||
|
top: .3125rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:after {
|
||||||
|
top: 1.25rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ .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;
|
||||||
|
}
|
||||||
|
transition: max-height 0.25s ease-in;
|
||||||
|
|
||||||
|
&.has-subheader {
|
||||||
|
min-height: 72px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: "";
|
||||||
|
position: relative;
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+ .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;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.pricing-by-day {
|
||||||
|
color: #4d5151;
|
||||||
|
display: block;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-weight: 600;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
span {
|
||||||
|
&.pricing-info-pricing-by-day {
|
||||||
|
text-align: end;
|
||||||
|
color: $green;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1;
|
||||||
|
span.strikethrough-price {
|
||||||
|
text-decoration: line-through;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 400;
|
||||||
|
color: $gray-550;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.sub-label {
|
||||||
|
color: $green;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
&.service-package-discount-pricing-by-day {
|
||||||
|
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;
|
||||||
|
line-height: 1;
|
||||||
|
div.strikethrough-discount-price {
|
||||||
|
display: inline;
|
||||||
|
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;
|
||||||
|
display: inline;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.caption {
|
||||||
|
font-size: 0.625rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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: auto;
|
||||||
|
margin-left: -1.25rem;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
span {
|
||||||
|
color: $green;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -10,9 +10,9 @@
|
||||||
href="javascript:void(0)"
|
href="javascript:void(0)"
|
||||||
class="col d-flex justify-content-between py-0">
|
class="col d-flex justify-content-between py-0">
|
||||||
<span class="label">{{ OrderDetailsText }}</span>
|
<span class="label">{{ OrderDetailsText }}</span>
|
||||||
<span class="label amount-due">{{
|
<span class="label amount-due">
|
||||||
getLineItemAmount(amountDue, showCoverageAsPending)
|
{{ getLineItemAmount(amountDue, showCoverageAsPending) }}
|
||||||
}}</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="cart-panel">
|
<div class="cart-panel">
|
||||||
|
|
@ -48,11 +48,11 @@
|
||||||
v-for="(cartItem, i) in packageCartItems"
|
v-for="(cartItem, i) in packageCartItems"
|
||||||
:key="i"
|
:key="i"
|
||||||
class="packaged-cart-item">
|
class="packaged-cart-item">
|
||||||
<span
|
<span>
|
||||||
><span :class="cartItem.category === 'promos' ? 'promo' : ''">{{
|
<span :class="cartItem.category === 'promos' ? 'promo' : ''">
|
||||||
cartItem.name
|
{{ cartItem.name }}
|
||||||
}}</span></span
|
</span>
|
||||||
>
|
</span>
|
||||||
<textLink
|
<textLink
|
||||||
v-if="allowItemRemoval"
|
v-if="allowItemRemoval"
|
||||||
ref="removeLink"
|
ref="removeLink"
|
||||||
|
|
@ -99,11 +99,9 @@
|
||||||
!showCoverageAsVerified ||
|
!showCoverageAsVerified ||
|
||||||
cartItem.name === 'Recycling' ||
|
cartItem.name === 'Recycling' ||
|
||||||
(showCoverageAsVerified && !cartItem.isCoveredByInsurance)
|
(showCoverageAsVerified && !cartItem.isCoveredByInsurance)
|
||||||
"
|
">
|
||||||
>{{
|
{{ getLineItemAmount(cartItem.subTotal, null, cartItem.category) }}
|
||||||
getLineItemAmount(cartItem.subTotal, null, cartItem.category)
|
</span>
|
||||||
}}</span
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Sub total, sales tax, total columns -->
|
<!-- Sub total, sales tax, total columns -->
|
||||||
|
|
@ -143,8 +141,10 @@
|
||||||
class="my-2 lh-1 applied-promo-tag"
|
class="my-2 lh-1 applied-promo-tag"
|
||||||
v-for="(promoCode, i) in getPromoCodeList"
|
v-for="(promoCode, i) in getPromoCodeList"
|
||||||
:key="i">
|
:key="i">
|
||||||
<span class="caption"
|
<span class="caption">
|
||||||
>Promo code <span class="promo-code">{{ promoCode }}</span> applied
|
Promo code
|
||||||
|
<span class="promo-code">{{ promoCode }}</span>
|
||||||
|
applied
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<contentGroupModal ref="RecycleModal" cmsWidgetName="RecycleModal">
|
<contentGroupModal ref="RecycleModal" cmsWidgetName="RecycleModal">
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,10 @@
|
||||||
class="mt-4 d-flex align-items-center justify-content-between"
|
class="mt-4 d-flex align-items-center justify-content-between"
|
||||||
v-for="(promoCode, i) in this.getPromoCodeList()"
|
v-for="(promoCode, i) in this.getPromoCodeList()"
|
||||||
:key="i">
|
:key="i">
|
||||||
<span class="applied-promo"
|
<span class="applied-promo">
|
||||||
>Promo code <span class="promo-code">{{ promoCode }}</span> applied
|
Promo code
|
||||||
|
<span class="promo-code">{{ promoCode }}</span>
|
||||||
|
applied
|
||||||
</span>
|
</span>
|
||||||
<textLink
|
<textLink
|
||||||
ref="removeLink"
|
ref="removeLink"
|
||||||
|
|
|
||||||
|
|
@ -149,6 +149,21 @@ export default {
|
||||||
transition: all 150ms linear;
|
transition: all 150ms linear;
|
||||||
background: none;
|
background: none;
|
||||||
|
|
||||||
|
&.btn {
|
||||||
|
&.btn-secondary {
|
||||||
|
font-weight: 900;
|
||||||
|
border: none;
|
||||||
|
&:hover,
|
||||||
|
&:active,
|
||||||
|
&:focus,
|
||||||
|
&:focus-visible {
|
||||||
|
color: $blue-700;
|
||||||
|
background: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:active,
|
&:active,
|
||||||
&:focus,
|
&:focus,
|
||||||
|
|
@ -166,6 +181,7 @@ export default {
|
||||||
transition: background 0s 0s ease-in-out;
|
transition: background 0s 0s ease-in-out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal.modal-component .modal-dialog .modal-content {
|
.modal.modal-component .modal-dialog .modal-content {
|
||||||
.modal-header {
|
.modal-header {
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
|
|
|
||||||
|
|
@ -58,3 +58,15 @@ export function getTopLevelPartsWithRecal(lineItems) {
|
||||||
|
|
||||||
return lineItems.filter((li) => isRecalPartOrHasChildRecalPart(li));
|
return lineItems.filter((li) => isRecalPartOrHasChildRecalPart(li));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getRecalPartNumbers(glassPartsArray) {
|
||||||
|
if (glassPartsArray && glassPartsArray.length > 0) {
|
||||||
|
const topLevel = glassPartsArray.filter((gp) => isRecalPart(gp)).map((gp) => gp.partNumber);
|
||||||
|
|
||||||
|
const children = glassPartsArray.map((gp) => getRecalPartNumbers(gp.childParts)).flat();
|
||||||
|
|
||||||
|
return [...topLevel, ...children];
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -357,6 +357,11 @@ export default {
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// if no value due to field being optional, blank both phone and email address
|
||||||
|
if (!this.customerQuestions.emailOrSms) {
|
||||||
|
await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, "", false);
|
||||||
|
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, "", false);
|
||||||
|
} else {
|
||||||
if (vinPagesMixin.methods.isPhoneNumber(this.customerQuestions.emailOrSms)) {
|
if (vinPagesMixin.methods.isPhoneNumber(this.customerQuestions.emailOrSms)) {
|
||||||
const phone = this.customerQuestions.emailOrSms.replace(/[()]/g, "");
|
const phone = this.customerQuestions.emailOrSms.replace(/[()]/g, "");
|
||||||
await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, phone, false);
|
await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, phone, false);
|
||||||
|
|
@ -367,6 +372,7 @@ export default {
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await this.dispatchStoreAction(
|
await this.dispatchStoreAction(
|
||||||
storeActions.SAVE_PAGE_DATA,
|
storeActions.SAVE_PAGE_DATA,
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@
|
||||||
<button v-on:click="openCalendarModal">
|
<button v-on:click="openCalendarModal">
|
||||||
<span class="add-to-calendar-span">
|
<span class="add-to-calendar-span">
|
||||||
<img src="@/assets/img/icons/add-to-calendar.svg" class="add-to-calendar-img" />
|
<img src="@/assets/img/icons/add-to-calendar.svg" class="add-to-calendar-img" />
|
||||||
<span class="add-to-calendar-text">Add to calendar</span></span
|
<span class="add-to-calendar-text">Add to calendar</span>
|
||||||
>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<calendarModalQuestion
|
<calendarModalQuestion
|
||||||
|
|
@ -14,8 +14,7 @@
|
||||||
cmsWidgetName="CalendarModalQuestion"
|
cmsWidgetName="CalendarModalQuestion"
|
||||||
@calendar-modal-closed="calendarModalClosed"
|
@calendar-modal-closed="calendarModalClosed"
|
||||||
:calendarOptionsData="calendarValues"
|
:calendarOptionsData="calendarValues"
|
||||||
v-model="selectedCalendarOption">
|
v-model="selectedCalendarOption"></calendarModalQuestion>
|
||||||
</calendarModalQuestion>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,19 @@
|
||||||
:isItac="isItac"
|
:isItac="isItac"
|
||||||
:isNoComp="isNoComp" />
|
:isNoComp="isNoComp" />
|
||||||
|
|
||||||
|
<div class="mt-3" v-if="displayFosterLove">
|
||||||
|
<donationBlock
|
||||||
|
cmsWidgetName="DonationWidget"
|
||||||
|
v-model="donationAmount"
|
||||||
|
:donationValues="donationValues"
|
||||||
|
:showDonationSuccess="showDonationSuccess"
|
||||||
|
:showDonationError="showDonationError" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <button type="button" @click="addDonation(3)">
|
||||||
|
Add Donation of $3
|
||||||
|
</button> -->
|
||||||
|
|
||||||
<hr class="mb-5" />
|
<hr class="mb-5" />
|
||||||
|
|
||||||
<div class="emailtext" v-html="ConfirmationEmailText"></div>
|
<div class="emailtext" v-html="ConfirmationEmailText"></div>
|
||||||
|
|
@ -108,6 +121,7 @@ import experimentMixin from "@/mixins/experiment-mixin.js";
|
||||||
import { partTypeStrings } from "@/constants/part-type-strings";
|
import { partTypeStrings } from "@/constants/part-type-strings";
|
||||||
import { containsLineItemWithPartType } from "@/helpers/service-package-helper";
|
import { containsLineItemWithPartType } from "@/helpers/service-package-helper";
|
||||||
import { containsRecalParts } from "@/helpers/recal-helper.js";
|
import { containsRecalParts } from "@/helpers/recal-helper.js";
|
||||||
|
import donationBlock from "@/experiment-components/donation-block.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "confirmation",
|
name: "confirmation",
|
||||||
|
|
@ -204,6 +218,11 @@ export default {
|
||||||
return hasRecalPriceRemoveExperiment && isRecalibrationOnOrder;
|
return hasRecalPriceRemoveExperiment && isRecalibrationOnOrder;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const donationItems = lineItemsFromSubmittedOrder.supportingItems?.filter(
|
||||||
|
(item) => item.partNumber === "DONATION"
|
||||||
|
);
|
||||||
|
const donationAmount = donationItems?.length > 0 ? donationItems[0].sellingPrice : 0;
|
||||||
|
|
||||||
// Call the "next" function to complete the transition to this page.
|
// Call the "next" function to complete the transition to this page.
|
||||||
next((vm) => {
|
next((vm) => {
|
||||||
vm.setCmsContent(resultMap.cmsContent);
|
vm.setCmsContent(resultMap.cmsContent);
|
||||||
|
|
@ -211,6 +230,7 @@ export default {
|
||||||
vm.vaps = availableVaps;
|
vm.vaps = availableVaps;
|
||||||
vm.submittedOrder = submittedOrder;
|
vm.submittedOrder = submittedOrder;
|
||||||
vm.shouldHideRecalibration = shouldHideRecalibration();
|
vm.shouldHideRecalibration = shouldHideRecalibration();
|
||||||
|
vm.donationAmount = donationAmount;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
|
@ -219,9 +239,18 @@ export default {
|
||||||
vaps: [],
|
vaps: [],
|
||||||
submittedOrder: null,
|
submittedOrder: null,
|
||||||
shouldHideRecalibration: null,
|
shouldHideRecalibration: null,
|
||||||
|
donationAmount: 0,
|
||||||
|
showDonationSuccess: null,
|
||||||
|
showDonationError: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
DisplayFosterLove() {
|
||||||
|
return experimentMixin.methods.hasSettingEqualTo(
|
||||||
|
experimentSettings.FOSTER_LOVE,
|
||||||
|
"true"
|
||||||
|
);
|
||||||
|
},
|
||||||
isRecalibrationOnOrder() {
|
isRecalibrationOnOrder() {
|
||||||
return containsRecalParts(this?.lineItems);
|
return containsRecalParts(this?.lineItems);
|
||||||
},
|
},
|
||||||
|
|
@ -447,6 +476,9 @@ export default {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
donationValues() {
|
||||||
|
return [1, 3, 5];
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
arePagePrerequisitesValid() {
|
arePagePrerequisitesValid() {
|
||||||
|
|
@ -489,6 +521,56 @@ export default {
|
||||||
forwardButtonAction() {
|
forwardButtonAction() {
|
||||||
window.location.assign(location.protocol + "//" + location.host);
|
window.location.assign(location.protocol + "//" + location.host);
|
||||||
},
|
},
|
||||||
|
async addDonation(amount, plateTemp) {
|
||||||
|
// try to call API to add donation to order
|
||||||
|
|
||||||
|
// TEMP DUMMY CALL TO SIMULATE SUCCESS OR ERROR FROM API CALL
|
||||||
|
// TO BE REPLACED BY REAL CALL LATER
|
||||||
|
const donationResponse = await this.dispatchStoreActionWithLogging(
|
||||||
|
storeActions.LOOKUP_VIN_BY_PLATE,
|
||||||
|
{ licensePlate: plateTemp, licenseState: "OH" },
|
||||||
|
"test",
|
||||||
|
false
|
||||||
|
).catch((error) => {
|
||||||
|
// this.$refs.navbar.removeLoader();
|
||||||
|
console.error("Error!!! error: ", error);
|
||||||
|
return error;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("ran addDonation()... (*fake call*) donationResponse: ", donationResponse);
|
||||||
|
|
||||||
|
if (donationResponse?.status == "200") {
|
||||||
|
this.showDonationSuccess = true;
|
||||||
|
this.showDonationError = false;
|
||||||
|
const donationResponse = await baseMixin.methods.dispatchStoreAction(
|
||||||
|
storeActions.ADD_DONATION_TO_SUBMITTED_ORDER,
|
||||||
|
amount,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.showDonationError = true;
|
||||||
|
this.showDonationSuccess = false;
|
||||||
|
const donationResponse = await baseMixin.methods.dispatchStoreAction(
|
||||||
|
storeActions.ADD_DONATION_TO_SUBMITTED_ORDER,
|
||||||
|
0,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
donationAmount(newValue) {
|
||||||
|
newValue = parseInt(newValue);
|
||||||
|
console.log("WATCHED donationAmount changed: ", newValue);
|
||||||
|
|
||||||
|
// this.addDonation(newValue);
|
||||||
|
// TODO - when backend code is final, then remove the below and restore the line above
|
||||||
|
if (newValue === 5) {
|
||||||
|
this.addDonation(newValue, "FAILING_PLATE"); // throws error
|
||||||
|
} else {
|
||||||
|
this.addDonation(newValue, "2fast"); // should give successful response
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
funnelHeader,
|
funnelHeader,
|
||||||
|
|
@ -497,6 +579,7 @@ export default {
|
||||||
addToCalendar,
|
addToCalendar,
|
||||||
cart,
|
cart,
|
||||||
textBlock,
|
textBlock,
|
||||||
|
donationBlock,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
describe("insurance-company.vue", () => {
|
|
||||||
it.todo("Some test for the future");
|
|
||||||
});
|
|
||||||
102
src/layouts/insurance-company/insurance-company.spec.js
Normal file
102
src/layouts/insurance-company/insurance-company.spec.js
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
import { shallowMount } from "@vue/test-utils";
|
||||||
|
import InsuranceCompany from "@/layouts/insurance-company/insurance-company.vue";
|
||||||
|
import { applicationConfig } from "@/constants/application-config.js";
|
||||||
|
import store from "@/store";
|
||||||
|
import baseMixin from "@/mixins/base-mixin.js";
|
||||||
|
|
||||||
|
jest.mock("@/store", () => ({
|
||||||
|
getters: {
|
||||||
|
order: {
|
||||||
|
payment: {
|
||||||
|
parentAccountNumber: "12345",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
var mockRouter = {
|
||||||
|
navigateWithoutSaving: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
jest.mock("@/mixins/base-mixin.js", () => ({
|
||||||
|
methods: {
|
||||||
|
navigateWithoutSaving: jest.fn(),
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
navigationScenarios: {
|
||||||
|
CLICKED_BACK: "clicked_back",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("insurance-company.vue", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
// Reset the store's parentAccountNumber before each test
|
||||||
|
store.getters.order.payment.parentAccountNumber = "12345";
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should return true when parentAccountNumber matches CASH_PARENT_ACCOUNT_NUMBER", () => {
|
||||||
|
applicationConfig.CASH_PARENT_ACCOUNT_NUMBER = "12345";
|
||||||
|
const wrapper = shallowMount(InsuranceCompany, {
|
||||||
|
global: {
|
||||||
|
mocks: {
|
||||||
|
$store: store,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.vm.isCashParentAccountNumber()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should return false when parentAccountNumber does not match CASH_PARENT_ACCOUNT_NUMBER", () => {
|
||||||
|
applicationConfig.CASH_PARENT_ACCOUNT_NUMBER = "12345";
|
||||||
|
store.getters.order.payment.parentAccountNumber = "67890";
|
||||||
|
const wrapper = shallowMount(InsuranceCompany, {
|
||||||
|
global: {
|
||||||
|
mocks: {
|
||||||
|
$store: store,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.vm.isCashParentAccountNumber()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should call navigateWithoutSaving with { isCashSelected: true } when forceCashSelection is true", () => {
|
||||||
|
const wrapper = shallowMount(InsuranceCompany, {
|
||||||
|
global: {
|
||||||
|
mocks: {
|
||||||
|
$store: store,
|
||||||
|
$router: mockRouter,
|
||||||
|
},
|
||||||
|
mixins: [baseMixin],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
wrapper.vm.backButtonAction(true);
|
||||||
|
expect(mockRouter.navigateWithoutSaving).toHaveBeenCalledWith(
|
||||||
|
wrapper.vm.navigationScenarios.CLICKED_BACK,
|
||||||
|
wrapper.vm.$route,
|
||||||
|
{ isCashSelected: true }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should call navigateWithoutSaving with no extra parameter when forceCashSelection is false", () => {
|
||||||
|
const wrapper = shallowMount(InsuranceCompany, {
|
||||||
|
global: {
|
||||||
|
mocks: {
|
||||||
|
$store: store,
|
||||||
|
$router: mockRouter,
|
||||||
|
},
|
||||||
|
mixins: [baseMixin],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
wrapper.vm.backButtonAction(false);
|
||||||
|
expect(mockRouter.navigateWithoutSaving).toHaveBeenCalledWith(
|
||||||
|
wrapper.vm.navigationScenarios.CLICKED_BACK,
|
||||||
|
wrapper.vm.$route
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -16,6 +16,13 @@
|
||||||
v-model="parentAccountNumber"
|
v-model="parentAccountNumber"
|
||||||
:originalList="originalList" />
|
:originalList="originalList" />
|
||||||
</div>
|
</div>
|
||||||
|
<textLink
|
||||||
|
id="payOnMyOwnBackButton"
|
||||||
|
useLoadingModal
|
||||||
|
linkType="text"
|
||||||
|
:text="payWithCashButtonCopy"
|
||||||
|
href="javascript:void(0)"
|
||||||
|
@click-event="backButtonAction((foreceCashSelectionOnQuote = true))" />
|
||||||
|
|
||||||
<navbar
|
<navbar
|
||||||
cmsWidgetName="FunnelFooterWidget"
|
cmsWidgetName="FunnelFooterWidget"
|
||||||
|
|
@ -36,6 +43,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 insuranceCompanyQuestion from "@/layouts/insurance-company/insurance-company-question";
|
import insuranceCompanyQuestion from "@/layouts/insurance-company/insurance-company-question";
|
||||||
|
import textLink from "../../ux-components/text-link/text-link.vue";
|
||||||
|
|
||||||
// Supporting files
|
// Supporting files
|
||||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
|
|
@ -92,6 +100,9 @@ export default {
|
||||||
isParentAccountNumber() {
|
isParentAccountNumber() {
|
||||||
return !!this.parentAccountNumber;
|
return !!this.parentAccountNumber;
|
||||||
},
|
},
|
||||||
|
payWithCashButtonCopy() {
|
||||||
|
return this.getCmsContent("PayWithCashWidget", "Text");
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
isCashParentAccountNumber() {
|
isCashParentAccountNumber() {
|
||||||
|
|
@ -109,8 +120,17 @@ export default {
|
||||||
arePagePrerequisitesValid() {
|
arePagePrerequisitesValid() {
|
||||||
return store.getters.order.payment.isInsurance === true;
|
return store.getters.order.payment.isInsurance === true;
|
||||||
},
|
},
|
||||||
backButtonAction() {
|
backButtonAction(forceCashSelectionOnQuote) {
|
||||||
// Go back to Quote page
|
// Go back to Quote page
|
||||||
|
if (forceCashSelectionOnQuote) {
|
||||||
|
this.$router.navigateWithoutSaving(
|
||||||
|
this.navigationScenarios.CLICKED_BACK,
|
||||||
|
this.$route,
|
||||||
|
{ isCashSelected: true }
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
|
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||||
},
|
},
|
||||||
async forwardButtonAction() {
|
async forwardButtonAction() {
|
||||||
|
|
@ -157,6 +177,7 @@ export default {
|
||||||
Form,
|
Form,
|
||||||
loadingModal,
|
loadingModal,
|
||||||
insuranceCompanyQuestion,
|
insuranceCompanyQuestion,
|
||||||
|
textLink,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -311,12 +311,18 @@ export default {
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// if no value due to field being optional, blank both phone and email address
|
||||||
|
if (!this.emailOrSms) {
|
||||||
|
await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, "", false);
|
||||||
|
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, "", false);
|
||||||
|
} else {
|
||||||
if (vinPagesMixin.methods.isPhoneNumber(this.emailOrSms)) {
|
if (vinPagesMixin.methods.isPhoneNumber(this.emailOrSms)) {
|
||||||
const phone = this.emailOrSms.replace(/[()]/g, "");
|
const phone = this.emailOrSms.replace(/[()]/g, "");
|
||||||
await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, phone, false);
|
await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, phone, false);
|
||||||
} else {
|
} else {
|
||||||
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, this.emailOrSms, false);
|
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, this.emailOrSms, false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await this.dispatchStoreAction(
|
await this.dispatchStoreAction(
|
||||||
storeActions.SAVE_PAGE_DATA,
|
storeActions.SAVE_PAGE_DATA,
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,20 @@ export default {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (store.getters.externalParameterCustomer?.phoneNumber) {
|
||||||
|
await baseMixin.methods.dispatchStoreAction(
|
||||||
|
storeActions.SAVE_PHONE_NUMBER,
|
||||||
|
store.getters.externalParameterCustomer.phoneNumber,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
await baseMixin.methods.dispatchStoreAction(
|
||||||
|
storeActions.SAVE_IS_SMS_OPT_IN,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
baseMixin.methods.ResetExternalParamsAndHideModal();
|
baseMixin.methods.ResetExternalParamsAndHideModal();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,21 @@ export default {
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (store.getters.externalParameterCustomer?.phoneNumber) {
|
||||||
|
await baseMixin.methods.dispatchStoreAction(
|
||||||
|
storeActions.SAVE_PHONE_NUMBER,
|
||||||
|
store.getters.externalParameterCustomer.phoneNumber,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
await baseMixin.methods.dispatchStoreAction(
|
||||||
|
storeActions.SAVE_IS_SMS_OPT_IN,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
baseMixin.methods.ResetExternalParamsAndHideModal();
|
baseMixin.methods.ResetExternalParamsAndHideModal();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,7 @@
|
||||||
id="card-frame"
|
id="card-frame"
|
||||||
seamless
|
seamless
|
||||||
scrolling="no"
|
scrolling="no"
|
||||||
allow="payment *; microphone *; geolocation *;">
|
allow="payment *; microphone *; geolocation *;"></iframe>
|
||||||
</iframe>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,9 @@ jest.mock("@/mixins/base-mixin", () => ({
|
||||||
filterOutFees(items) {
|
filterOutFees(items) {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
filterOutServicePackageDiscountPart(items) {
|
||||||
|
return null;
|
||||||
|
},
|
||||||
isFormValid(form) {
|
isFormValid(form) {
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
@ -577,6 +580,52 @@ describe("quote.vue", () => {
|
||||||
//Assert
|
//Assert
|
||||||
expect(wrapper.vm.isInsuranceSelected).toBe(false);
|
expect(wrapper.vm.isInsuranceSelected).toBe(false);
|
||||||
});
|
});
|
||||||
|
test("should default to cash if isCashSelected is true, despite all other checks pointing to insurance selection", async () => {
|
||||||
|
// Also needs no query parameter or previous selection in store to be present
|
||||||
|
//Arrange
|
||||||
|
|
||||||
|
store.getters = {
|
||||||
|
lineItems: {
|
||||||
|
glassParts: ["item", "item2"],
|
||||||
|
},
|
||||||
|
applicationUser: {
|
||||||
|
experiments: [],
|
||||||
|
},
|
||||||
|
order: {
|
||||||
|
lineItems: {
|
||||||
|
glassParts: ["item", "item2"],
|
||||||
|
},
|
||||||
|
payment: {
|
||||||
|
isInsurance: true,
|
||||||
|
},
|
||||||
|
customer: {
|
||||||
|
emailAddress: "test@test.com",
|
||||||
|
},
|
||||||
|
serviceLocation: {
|
||||||
|
state: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
vehicle: {
|
||||||
|
cardId: "123",
|
||||||
|
},
|
||||||
|
experimentSettings: {
|
||||||
|
settingName: "SERVICE_PACKAGE_DISCOUNT",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
mockTierOnePrice = 505;
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
//Act
|
||||||
|
await quote.beforeRouteEnter.call(
|
||||||
|
wrapper.vm,
|
||||||
|
{ query: { fmgPage: "quote", isInsuranceSelected: true, isCashSelected: true } },
|
||||||
|
undefined,
|
||||||
|
(c) => c(wrapper.vm)
|
||||||
|
);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(wrapper.vm.isInsuranceSelected).toBe(false);
|
||||||
|
});
|
||||||
test("should default to insurance if total economy package price is over $500", async () => {
|
test("should default to insurance if total economy package price is over $500", async () => {
|
||||||
// Also needs no query parameter or previous selection in store to be present
|
// Also needs no query parameter or previous selection in store to be present
|
||||||
//Arrange
|
//Arrange
|
||||||
|
|
|
||||||
|
|
@ -328,6 +328,10 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
const getIsInsuranceSelectedValue = (availableLineItems, insuranceThreshold) => {
|
const getIsInsuranceSelectedValue = (availableLineItems, insuranceThreshold) => {
|
||||||
|
// query param checked here is set on insurance-selection "Pay on my own" link click
|
||||||
|
if (to.query?.isCashSelected || to.query?.iscashselected) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const serviceLocationState = store.getters.order.serviceLocation.state;
|
const serviceLocationState = store.getters.order.serviceLocation.state;
|
||||||
|
|
||||||
// usually true when returning from heritage but can be false when returning from heritage on a save quote
|
// usually true when returning from heritage but can be false when returning from heritage on a save quote
|
||||||
|
|
@ -357,16 +361,28 @@ export default {
|
||||||
)
|
)
|
||||||
: baseMixin.methods.filterOutFees(availableLineItems);
|
: baseMixin.methods.filterOutFees(availableLineItems);
|
||||||
|
|
||||||
|
const lineItemsNoDiscount =
|
||||||
|
baseMixin.methods.filterOutServicePackageDiscountPart(
|
||||||
|
lineItemsForCalculatingPrice
|
||||||
|
);
|
||||||
|
|
||||||
if (isInsuranceFromQueryString != null) {
|
if (isInsuranceFromQueryString != null) {
|
||||||
if (isInsuranceFromQueryString.toLowerCase() === "true") {
|
if (isInsuranceFromQueryString.toLowerCase() === "true") {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
const tierOnePrice =
|
||||||
baseMixin.methods.getTierOnePackagePrice(lineItemsForCalculatingPrice) >
|
baseMixin.methods.getTierOnePackagePrice(lineItemsNoDiscount);
|
||||||
insuranceThreshold
|
// prettier-ignore
|
||||||
); // compare base price vs arbitrary threshold (representing insurance price)
|
{
|
||||||
|
if (store.getters.applicationUser.loggingOption) {
|
||||||
|
console.log(new Date() + " quote tierOnePrice comparison: " + tierOnePrice + " > " + insuranceThreshold + "=" + (tierOnePrice > insuranceThreshold));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// compare base price vs arbitrary threshold (representing insurance price)
|
||||||
|
return tierOnePrice > insuranceThreshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -383,8 +399,7 @@ export default {
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
{
|
{
|
||||||
const log = getQuerystringParameter(queryStrings.LOG);
|
if (store.getters.applicationUser.loggingOption) {
|
||||||
if (log === "true") {
|
|
||||||
console.log("------------- quote.vue thresholds start -----------------");
|
console.log("------------- quote.vue thresholds start -----------------");
|
||||||
console.log(new Date() + " isExternalParameter: " + isExternalParameter);
|
console.log(new Date() + " isExternalParameter: " + isExternalParameter);
|
||||||
console.log(new Date() + " internalThreshold: " + internalThreshold);
|
console.log(new Date() + " internalThreshold: " + internalThreshold);
|
||||||
|
|
@ -394,6 +409,12 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isExternalParameter === externalParameterStatus.NOT_SET) {
|
if (isExternalParameter === externalParameterStatus.NOT_SET) {
|
||||||
|
// prettier-ignore
|
||||||
|
{
|
||||||
|
if (store.getters.applicationUser.loggingOption) {
|
||||||
|
console.log(new Date() + "quote.vue tab select EXTERNAL not set");
|
||||||
|
}
|
||||||
|
}
|
||||||
// there are no active or inactive external parameters; use internal threshold
|
// there are no active or inactive external parameters; use internal threshold
|
||||||
if (internalThreshold) thresholdToUse = internalThreshold;
|
if (internalThreshold) thresholdToUse = internalThreshold;
|
||||||
|
|
||||||
|
|
@ -405,6 +426,13 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
// user came from an external source, however, the externalParms may have been reset on service-zip, property-questions, etc...
|
// user came from an external source, however, the externalParms may have been reset on service-zip, property-questions, etc...
|
||||||
if (getBoolFromString(store.getters.externalParameterQuote?.isInsurance)) {
|
if (getBoolFromString(store.getters.externalParameterQuote?.isInsurance)) {
|
||||||
|
// prettier-ignore
|
||||||
|
{
|
||||||
|
if (store.getters.applicationUser.loggingOption) {
|
||||||
|
console.log(new Date() + "quote.vue tab select EXTERNAL ISINSURANCE");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// did user intentionally select insurance?
|
// did user intentionally select insurance?
|
||||||
vm.isInsuranceSelected = true;
|
vm.isInsuranceSelected = true;
|
||||||
vm.servicePackage = store.getters.externalParameterQuote.servicePackage;
|
vm.servicePackage = store.getters.externalParameterQuote.servicePackage;
|
||||||
|
|
@ -424,6 +452,13 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
if (externalThreshold) thresholdToUse = externalThreshold;
|
if (externalThreshold) thresholdToUse = externalThreshold;
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
{
|
||||||
|
if (store.getters.applicationUser.loggingOption) {
|
||||||
|
console.log(new Date() + "quote.vue tab select NOT EXTERNAL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
vm.isInsuranceSelected = getIsInsuranceSelectedValue(
|
vm.isInsuranceSelected = getIsInsuranceSelectedValue(
|
||||||
vm.availableLineItems,
|
vm.availableLineItems,
|
||||||
thresholdToUse
|
thresholdToUse
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,11 @@ import { nextTick } from "vue";
|
||||||
jest.mock("@/store", () => ({
|
jest.mock("@/store", () => ({
|
||||||
commit: jest.fn(),
|
commit: jest.fn(),
|
||||||
dispatch: jest.fn(),
|
dispatch: jest.fn(),
|
||||||
|
getters: {
|
||||||
|
applicationUser: {
|
||||||
|
loggingOption: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const mockExperimentSettings = experimentSettings;
|
const mockExperimentSettings = experimentSettings;
|
||||||
|
|
@ -29,6 +34,9 @@ jest.mock("@/mixins/experiment-mixin.js", () => ({
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
hasSettingEqualTo(settingName, settingValue) {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -843,6 +851,7 @@ function setupMocks({ mountOptionsMockData, props = mockProps }) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
mountOptionsMockData = Object.assign(mountOptionsMockDataDefault, mountOptionsMockData);
|
mountOptionsMockData = Object.assign(mountOptionsMockDataDefault, mountOptionsMockData);
|
||||||
const mountOptions = getMountOptions(mountOptionsMockData);
|
const mountOptions = getMountOptions(mountOptionsMockData);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,12 @@
|
||||||
:answers="servicePackageAnswers"
|
:answers="servicePackageAnswers"
|
||||||
:groupName="groupName"
|
:groupName="groupName"
|
||||||
buttonTypeString="servicePackageRadio"
|
buttonTypeString="servicePackageRadio"
|
||||||
:buttonTypeObject="servicePackageRadio"
|
:buttonTypeObject="hasPricingByDay() ? servicePackageRadioExperiment : servicePackageRadio"
|
||||||
v-model="selectedPackageName"
|
v-model="selectedPackageName"
|
||||||
:validationRules="validationRules"
|
:validationRules="validationRules"
|
||||||
:isRequired="isRequired"
|
:isRequired="isRequired"
|
||||||
:logDisplayedValuesEvent="true" />
|
:logDisplayedValuesEvent="true"
|
||||||
|
:isInsuranceSelected="isInsuranceSelected" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
@ -16,6 +17,7 @@ import buttonQuestion from "@/digital-components/button-question/button-question
|
||||||
import baseMixin from "@/mixins/base-mixin.js";
|
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 { 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 {
|
||||||
|
|
@ -58,6 +60,7 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
servicePackageRadio: servicePackageRadio,
|
servicePackageRadio: servicePackageRadio,
|
||||||
|
servicePackageRadioExperiment: servicePackageRadioExperiment,
|
||||||
selectedPackageName: null,
|
selectedPackageName: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
@ -223,6 +226,14 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
processIfStatements,
|
processIfStatements,
|
||||||
|
hasPricingByDay() {
|
||||||
|
return (
|
||||||
|
experimentMixin.methods.hasSettingEqualTo(
|
||||||
|
experimentSettings.PRICING_BY_DAY,
|
||||||
|
"true"
|
||||||
|
) && !this.isInsuranceSelected
|
||||||
|
);
|
||||||
|
},
|
||||||
getHeaderTextFromCms(cmsWidgetName) {
|
getHeaderTextFromCms(cmsWidgetName) {
|
||||||
return this.getCmsContent(cmsWidgetName, "HeaderText");
|
return this.getCmsContent(cmsWidgetName, "HeaderText");
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,10 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 col-xl-4 start-over">
|
<div class="col-md-6 col-xl-4 start-over">
|
||||||
<a @click="startOver" alt="Start Over Button"
|
<a @click="startOver" alt="Start Over Button">
|
||||||
><img v-bind:src="startOverImage" />{{ startOverText }}</a
|
<img v-bind:src="startOverImage" />
|
||||||
>
|
{{ startOverText }}
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
import router from "@/router";
|
import router from "@/router";
|
||||||
import baseMixin from "../../mixins/base-mixin";
|
import baseMixin from "../../mixins/base-mixin";
|
||||||
|
import { experimentSettings } from "../../constants/experiments";
|
||||||
|
|
||||||
// Mock basemixin
|
// Mock basemixin
|
||||||
jest.mock("@/mixins/base-mixin.js", () => ({
|
jest.mock("@/mixins/base-mixin.js", () => ({
|
||||||
|
|
@ -177,6 +178,7 @@ beforeEach(() => {
|
||||||
glassParts: [],
|
glassParts: [],
|
||||||
supportingItems: [],
|
supportingItems: [],
|
||||||
},
|
},
|
||||||
|
experimentSettings: {},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,18 @@
|
||||||
marginTopSizeOverride="1" />
|
marginTopSizeOverride="1" />
|
||||||
</template>
|
</template>
|
||||||
<locationAlerts cmsWidgetPrefix="LocationAlert-" ref="locationAlerts" />
|
<locationAlerts cmsWidgetPrefix="LocationAlert-" ref="locationAlerts" />
|
||||||
|
<datePickerForPricingByDay
|
||||||
|
v-if="isPricingByDayExperiment"
|
||||||
|
customComponentId="dateQuestion"
|
||||||
|
selectableDatesSetting="custom"
|
||||||
|
ref="datePicker"
|
||||||
|
v-model="selectedDate"
|
||||||
|
class="text-link-small"
|
||||||
|
:customSelectableDatesCallback="getAvailableDatesMethod"
|
||||||
|
validationRules="date-required"
|
||||||
|
@date-clicked="openInshopTimeSlotsModal" />
|
||||||
<datePicker
|
<datePicker
|
||||||
|
v-else
|
||||||
customComponentId="dateQuestion"
|
customComponentId="dateQuestion"
|
||||||
selectableDatesSetting="custom"
|
selectableDatesSetting="custom"
|
||||||
ref="datePicker"
|
ref="datePicker"
|
||||||
|
|
@ -74,6 +85,7 @@ import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-heade
|
||||||
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 datePicker from "@/digital-components/date-picker/date-picker";
|
import datePicker from "@/digital-components/date-picker/date-picker";
|
||||||
|
import datePickerForPricingByDay from "@/experiment-components/date-picker-for-pricing-by-day";
|
||||||
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";
|
||||||
|
|
@ -97,6 +109,8 @@ import { AppointmentTypeStrings, PREMIUM_FEE_PART_TYPE } from "@/constants/sched
|
||||||
import { errorMessages } from "@/constants/error-messages";
|
import { errorMessages } from "@/constants/error-messages";
|
||||||
import { required } from "@/helpers/validation-rules";
|
import { required } from "@/helpers/validation-rules";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
import experimentMixin from "@/mixins/experiment-mixin.js";
|
||||||
|
import { experimentSettings } from "@/constants/experiments";
|
||||||
|
|
||||||
// DEFINE VALIDATION RULES
|
// DEFINE VALIDATION RULES
|
||||||
defineRule("date-required", required(errorMessages.DATE_REQUIRED));
|
defineRule("date-required", required(errorMessages.DATE_REQUIRED));
|
||||||
|
|
@ -228,6 +242,8 @@ export default {
|
||||||
customSelectableDatesCallback: getAvailableDates,
|
customSelectableDatesCallback: getAvailableDates,
|
||||||
preSelectedDate: preSelectedDate,
|
preSelectedDate: preSelectedDate,
|
||||||
});
|
});
|
||||||
|
// TODO - ONCE A DATEPICKER VERSION IS FINALIZED,
|
||||||
|
// MAKE SURE THE ABOVE METHOD loadInitialData POINTS TO THE RIGHT FILE
|
||||||
|
|
||||||
const premiumFeePromise = baseMixin.methods.dispatchStoreActionWithLogging(
|
const premiumFeePromise = baseMixin.methods.dispatchStoreActionWithLogging(
|
||||||
storeActions.GET_MOBILE_PREMIUM_FEE,
|
storeActions.GET_MOBILE_PREMIUM_FEE,
|
||||||
|
|
@ -310,6 +326,13 @@ export default {
|
||||||
(selectableDate) => selectableDate.date === this.selectedDate
|
(selectableDate) => selectableDate.date === this.selectedDate
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
isPricingByDayExperiment() {
|
||||||
|
return (
|
||||||
|
experimentMixin.methods
|
||||||
|
.getSettingValue(experimentSettings.PRICING_BY_DAY)
|
||||||
|
?.toLowerCase() === "true"
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
splitCopyOnCMSPlaceHolder,
|
splitCopyOnCMSPlaceHolder,
|
||||||
|
|
@ -602,6 +625,7 @@ export default {
|
||||||
Form,
|
Form,
|
||||||
loadingModal,
|
loadingModal,
|
||||||
datePicker,
|
datePicker,
|
||||||
|
datePickerForPricingByDay,
|
||||||
locationAlerts,
|
locationAlerts,
|
||||||
timeSlotModalQuestion,
|
timeSlotModalQuestion,
|
||||||
textBlock,
|
textBlock,
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ export async function getPricedMobileFeePart(serviceZipCode, pageNameToLog) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const zipCodeData = await baseMixin.methods.getZipCodeData(serviceZipCode, pageNameToLog);
|
const zipCodeData = await getZipCodeData(serviceZipCode);
|
||||||
|
|
||||||
// Get the Mobile Fee Part
|
// Get the Mobile Fee Part
|
||||||
const mobileFeePart = await baseMixin.methods.dispatchStoreActionWithLogging(
|
const mobileFeePart = await baseMixin.methods.dispatchStoreActionWithLogging(
|
||||||
|
|
@ -103,3 +103,7 @@ export async function getAvailabilityRating(
|
||||||
|
|
||||||
return shopStatus;
|
return shopStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getZipCodeData(serviceZipCode) {
|
||||||
|
return await baseMixin.methods.getZipCodeData(serviceZipCode, "service-location");
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,7 @@ describe("mobile-location-modal-questions.vue", () => {
|
||||||
expect(wrapper.vm.$refs.MobileLocationModalWidget.openModal).toHaveBeenCalled();
|
expect(wrapper.vm.$refs.MobileLocationModalWidget.openModal).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should emit updated-mobile-location-questions on setMobileLocation for a valid address and vehicle protection answer", async () => {
|
it("Should emit setMobileLocationAndProceed on setMobileLocation for a valid address and vehicle protection answer", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const mobileLocationQuestions = {
|
const mobileLocationQuestions = {
|
||||||
addressQuestions: {
|
addressQuestions: {
|
||||||
|
|
@ -240,26 +240,19 @@ describe("mobile-location-modal-questions.vue", () => {
|
||||||
});
|
});
|
||||||
wrapper.vm.$refs.MobileLocationModalWidget.closeModal = jest.fn();
|
wrapper.vm.$refs.MobileLocationModalWidget.closeModal = jest.fn();
|
||||||
|
|
||||||
// Mock the emitEvent function
|
|
||||||
wrapper.vm.emitEvent = jest.fn(async (eventName, payload) => {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
wrapper.vm.$emit(eventName, newMobileLocationQuestions, resolve);
|
|
||||||
resolve(newMobileLocationQuestions);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
// Act
|
// Act
|
||||||
wrapper.vm.internalModel = newMobileLocationQuestions;
|
wrapper.vm.internalModel = newMobileLocationQuestions;
|
||||||
|
|
||||||
await wrapper.vm.setMobileLocation();
|
await wrapper.vm.setMobileLocation();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.emitted()["updated-mobile-location-questions"]).toBeTruthy();
|
expect(wrapper.emitted()["setMobileLocationAndProceed"]).toBeTruthy();
|
||||||
expect(wrapper.emitted()["updated-mobile-location-questions"][0][0]).toBe(
|
expect(wrapper.emitted()["setMobileLocationAndProceed"][0][0]).toStrictEqual(
|
||||||
newMobileLocationQuestions
|
newMobileLocationQuestions
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should not emit updated-mobile-location-questions on setMobileLocation for an invalid address zip code", async () => {
|
it("Should not emit setMobileLocationAndProceed on setMobileLocation for an invalid address zip code", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const mobileLocationQuestions = {
|
const mobileLocationQuestions = {
|
||||||
addressQuestions: {
|
addressQuestions: {
|
||||||
|
|
@ -302,7 +295,7 @@ describe("mobile-location-modal-questions.vue", () => {
|
||||||
await wrapper.vm.setMobileLocation();
|
await wrapper.vm.setMobileLocation();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.emitted("updated-mobile-location-questions")).not.toBeTruthy();
|
expect(wrapper.emitted("setMobileLocationAndProceed")).not.toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should display invalid zip alert for invalid address zip inputs", async () => {
|
it("Should display invalid zip alert for invalid address zip inputs", async () => {
|
||||||
|
|
|
||||||
|
|
@ -97,8 +97,7 @@ export default {
|
||||||
"updated-mobile-fee-part",
|
"updated-mobile-fee-part",
|
||||||
"updated-contains-military-base",
|
"updated-contains-military-base",
|
||||||
"updated-bill-to-account-number",
|
"updated-bill-to-account-number",
|
||||||
"mobileLocationSelected",
|
"setMobileLocationAndProceed",
|
||||||
"updated-mobile-location-questions",
|
|
||||||
],
|
],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -287,15 +286,7 @@ export default {
|
||||||
this.$emit("updated-bill-to-account-number", billToAccountNumber);
|
this.$emit("updated-bill-to-account-number", billToAccountNumber);
|
||||||
}
|
}
|
||||||
// update the page level model
|
// update the page level model
|
||||||
await this.emitEvent("updated-mobile-location-questions", this.internalModel);
|
this.$emit("setMobileLocationAndProceed", this.internalModel);
|
||||||
|
|
||||||
this.$emit("mobileLocationSelected");
|
|
||||||
},
|
|
||||||
//creating emitEvent function to raise events with resolve callback to handle events sequencing
|
|
||||||
async emitEvent(eventName, payload) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
this.$emit(eventName, payload, resolve);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,15 @@ jest.mock(
|
||||||
getShopProviderData: jest.fn((mockServiceZipCode) => {
|
getShopProviderData: jest.fn((mockServiceZipCode) => {
|
||||||
return mockGetShopProviderData(mockServiceZipCode);
|
return mockGetShopProviderData(mockServiceZipCode);
|
||||||
}),
|
}),
|
||||||
|
getZipCodeData: jest.fn().mockImplementation(() => {
|
||||||
|
return Promise.resolve({
|
||||||
|
containsMilitaryBase: false,
|
||||||
|
isServiceable: true,
|
||||||
|
isValid: true,
|
||||||
|
state: "testState",
|
||||||
|
zipCodeCtu: "testCTU",
|
||||||
|
});
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -182,9 +191,6 @@ beforeEach(() => {
|
||||||
zipCode: "43235",
|
zipCode: "43235",
|
||||||
state: "OH",
|
state: "OH",
|
||||||
},
|
},
|
||||||
vehicle: {
|
|
||||||
isMobileStaticRecalibrationApplicable: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
isRepair: false,
|
isRepair: false,
|
||||||
|
|
@ -427,6 +433,7 @@ describe("service-location.vue", () => {
|
||||||
},
|
},
|
||||||
selectedAppointmentType: "Mobile",
|
selectedAppointmentType: "Mobile",
|
||||||
providerData: { mobileProviderNumber: "01820" },
|
providerData: { mobileProviderNumber: "01820" },
|
||||||
|
isGlassServiceableMobile: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const mobileLocationQuestionsComponent = wrapper.findComponent({
|
const mobileLocationQuestionsComponent = wrapper.findComponent({
|
||||||
|
|
@ -438,6 +445,7 @@ describe("service-location.vue", () => {
|
||||||
ref: "serviceZipCodeQuestion",
|
ref: "serviceZipCodeQuestion",
|
||||||
});
|
});
|
||||||
serviceZipCodeComponent.resetMobileFeePart = jest.fn();
|
serviceZipCodeComponent.resetMobileFeePart = jest.fn();
|
||||||
|
wrapper.vm.forwardButtonAction = jest.fn();
|
||||||
|
|
||||||
const mobileLocationQuestions = {
|
const mobileLocationQuestions = {
|
||||||
addressQuestions: {
|
addressQuestions: {
|
||||||
|
|
@ -450,7 +458,6 @@ describe("service-location.vue", () => {
|
||||||
isVehicleProtected: null,
|
isVehicleProtected: null,
|
||||||
};
|
};
|
||||||
wrapper.vm.mobileLocationQuestions = mobileLocationQuestions;
|
wrapper.vm.mobileLocationQuestions = mobileLocationQuestions;
|
||||||
|
|
||||||
const newMobileLocationQuestions = {
|
const newMobileLocationQuestions = {
|
||||||
addressQuestions: {
|
addressQuestions: {
|
||||||
streetAddress: "5555 Sulgrave Dr",
|
streetAddress: "5555 Sulgrave Dr",
|
||||||
|
|
@ -462,23 +469,16 @@ describe("service-location.vue", () => {
|
||||||
isVehicleProtected: true,
|
isVehicleProtected: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
wrapper.vm.closeModalAction = jest.fn();
|
||||||
// Act
|
// Act
|
||||||
|
|
||||||
// Mock the emitEvent function
|
|
||||||
mobileLocationQuestionsComponent.vm.emitEvent = jest.fn(async (eventName, payload) => {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
mobileLocationQuestionsComponent.vm.$emit(eventName, payload, resolve);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Trigger the event
|
// Trigger the event
|
||||||
await mobileLocationQuestionsComponent.vm.emitEvent(
|
await mobileLocationQuestionsComponent.vm.$emit(
|
||||||
"updated-mobile-location-questions",
|
"setMobileLocationAndProceed",
|
||||||
newMobileLocationQuestions
|
newMobileLocationQuestions
|
||||||
);
|
);
|
||||||
|
|
||||||
await wrapper.vm.$nextTick(); // Wait for DOM updates
|
await wrapper.vm.$nextTick(); // Wait for DOM updates
|
||||||
await new Promise((resolve) => setTimeout(resolve, 0)); // Wait for promise to resolve
|
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.vm.mobileLocationQuestions).toStrictEqual(newMobileLocationQuestions);
|
expect(wrapper.vm.mobileLocationQuestions).toStrictEqual(newMobileLocationQuestions);
|
||||||
|
|
@ -494,6 +494,7 @@ describe("service-location.vue", () => {
|
||||||
},
|
},
|
||||||
selectedAppointmentType: "Mobile",
|
selectedAppointmentType: "Mobile",
|
||||||
providerData: { mobileProviderNumber: "01820" },
|
providerData: { mobileProviderNumber: "01820" },
|
||||||
|
isGlassServiceableMobile: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const mobileLocationQuestionsComponent = wrapper.findComponent({
|
const mobileLocationQuestionsComponent = wrapper.findComponent({
|
||||||
|
|
@ -505,7 +506,7 @@ describe("service-location.vue", () => {
|
||||||
ref: "serviceZipCodeQuestion",
|
ref: "serviceZipCodeQuestion",
|
||||||
});
|
});
|
||||||
serviceZipCodeComponent.resetMobileFeePart = jest.fn();
|
serviceZipCodeComponent.resetMobileFeePart = jest.fn();
|
||||||
|
wrapper.vm.forwardButtonAction = jest.fn();
|
||||||
const mobileLocationQuestions = {
|
const mobileLocationQuestions = {
|
||||||
addressQuestions: {
|
addressQuestions: {
|
||||||
streetAddress: "",
|
streetAddress: "",
|
||||||
|
|
@ -543,23 +544,16 @@ describe("service-location.vue", () => {
|
||||||
zipCodeCtu: "01526",
|
zipCodeCtu: "01526",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
wrapper.vm.closeModalAction = jest.fn();
|
||||||
// Act
|
// Act
|
||||||
|
|
||||||
// Mock the emitEvent function
|
|
||||||
mobileLocationQuestionsComponent.vm.emitEvent = jest.fn(async (eventName, payload) => {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
mobileLocationQuestionsComponent.vm.$emit(eventName, payload, resolve);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Trigger the event
|
// Trigger the event
|
||||||
await mobileLocationQuestionsComponent.vm.emitEvent(
|
await mobileLocationQuestionsComponent.vm.$emit(
|
||||||
"updated-mobile-location-questions",
|
"setMobileLocationAndProceed",
|
||||||
newMobileLocationQuestions
|
newMobileLocationQuestions
|
||||||
);
|
);
|
||||||
|
|
||||||
await wrapper.vm.$nextTick(); // Wait for DOM updates
|
await wrapper.vm.$nextTick(); // Wait for DOM updates
|
||||||
await new Promise((resolve) => setTimeout(resolve, 0)); // Wait for promise to resolve
|
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.vm.serviceZipCodeQuestion).toStrictEqual(newServiceZipCodeInfo);
|
expect(wrapper.vm.serviceZipCodeQuestion).toStrictEqual(newServiceZipCodeInfo);
|
||||||
|
|
@ -581,6 +575,8 @@ describe("service-location.vue", () => {
|
||||||
ref: "mobileLocationQuestions",
|
ref: "mobileLocationQuestions",
|
||||||
});
|
});
|
||||||
mobileLocationQuestionsComponent.resetComponent = jest.fn();
|
mobileLocationQuestionsComponent.resetComponent = jest.fn();
|
||||||
|
wrapper.vm.forwardButtonAction = jest.fn();
|
||||||
|
wrapper.vm.isServiceableMobile = true;
|
||||||
|
|
||||||
const serviceZipCodeComponent = wrapper.findComponent({
|
const serviceZipCodeComponent = wrapper.findComponent({
|
||||||
ref: "serviceZipCodeQuestion",
|
ref: "serviceZipCodeQuestion",
|
||||||
|
|
@ -598,8 +594,12 @@ describe("service-location.vue", () => {
|
||||||
isVehicleProtected: "YesAnswer",
|
isVehicleProtected: "YesAnswer",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
wrapper.vm.closeModalAction = jest.fn();
|
||||||
// Act
|
// Act
|
||||||
mobileLocationQuestionsComponent.vm.$emit("update:modelValue", mobileLocationQuestions);
|
mobileLocationQuestionsComponent.vm.$emit(
|
||||||
|
"setMobileLocationAndProceed",
|
||||||
|
mobileLocationQuestions
|
||||||
|
);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.vm.selectedAppointmentType).toStrictEqual("Mobile");
|
expect(wrapper.vm.selectedAppointmentType).toStrictEqual("Mobile");
|
||||||
|
|
@ -1432,6 +1432,5 @@ function setupMocks({ mountOptionsMockData = {} }) {
|
||||||
const mountOptions = getMountOptions(mountOptionsMockData);
|
const mountOptions = getMountOptions(mountOptionsMockData);
|
||||||
const wrapper = shallowMount(serviceLocation, mountOptions);
|
const wrapper = shallowMount(serviceLocation, mountOptions);
|
||||||
wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent;
|
wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent;
|
||||||
|
|
||||||
return { wrapper, apiPromise };
|
return { wrapper, apiPromise };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,12 +93,11 @@
|
||||||
@updated-serviceability="setServiceabilityDetails"
|
@updated-serviceability="setServiceabilityDetails"
|
||||||
@updated-contains-military-base="setContainsMilitaryBase"
|
@updated-contains-military-base="setContainsMilitaryBase"
|
||||||
@updated-mobile-ctu="setCtuForMobile"
|
@updated-mobile-ctu="setCtuForMobile"
|
||||||
@updated-mobile-location-questions="setMobileLocationQuestions"
|
|
||||||
validationRules="mobile-location-required"
|
validationRules="mobile-location-required"
|
||||||
ref="mobileLocationQuestions"
|
ref="mobileLocationQuestions"
|
||||||
linkWidgetName="MobileLocationLinkWidget"
|
linkWidgetName="MobileLocationLinkWidget"
|
||||||
modalWidgetName="MobileLocationModalWidget"
|
modalWidgetName="MobileLocationModalWidget"
|
||||||
@mobileLocationSelected="forwardButtonAction" />
|
@setMobileLocationAndProceed="setMobileLocationQuestions" />
|
||||||
|
|
||||||
<shopQuestion
|
<shopQuestion
|
||||||
ref="shopQuestion"
|
ref="shopQuestion"
|
||||||
|
|
@ -150,6 +149,7 @@ import {
|
||||||
getPricedMobileFeePart,
|
getPricedMobileFeePart,
|
||||||
getServiceabilityDetails,
|
getServiceabilityDetails,
|
||||||
getShopProviderData,
|
getShopProviderData,
|
||||||
|
getZipCodeData,
|
||||||
} from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
|
} from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
|
||||||
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";
|
||||||
|
|
@ -192,8 +192,6 @@ export default {
|
||||||
isRecalibrationServiceableInshop: null,
|
isRecalibrationServiceableInshop: null,
|
||||||
isGlassServiceableMobile: null,
|
isGlassServiceableMobile: null,
|
||||||
isRecalibrationServiceableMobile: null,
|
isRecalibrationServiceableMobile: null,
|
||||||
isVehicleMobileStaticRecalibrationApplicable:
|
|
||||||
this.getIsVehicleMobileStaticRecalibrationApplicableFromStore(),
|
|
||||||
selectedAppointmentType: this.getSelectedAppointmentType(),
|
selectedAppointmentType: this.getSelectedAppointmentType(),
|
||||||
selectedProvider: this.getSelectedProvider(),
|
selectedProvider: this.getSelectedProvider(),
|
||||||
mobileFeePart: null,
|
mobileFeePart: null,
|
||||||
|
|
@ -209,10 +207,7 @@ export default {
|
||||||
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
||||||
|
|
||||||
const serviceZipCode = store.getters.order.serviceLocation.zipCode;
|
const serviceZipCode = store.getters.order.serviceLocation.zipCode;
|
||||||
const zipCodeDataPromise = baseMixin.methods.getZipCodeData(
|
const zipCodeDataPromise = getZipCodeData(serviceZipCode);
|
||||||
serviceZipCode,
|
|
||||||
"service-location"
|
|
||||||
);
|
|
||||||
|
|
||||||
const serviceabilityDetailsPromise = getServiceabilityDetails(
|
const serviceabilityDetailsPromise = getServiceabilityDetails(
|
||||||
serviceZipCode,
|
serviceZipCode,
|
||||||
|
|
@ -297,19 +292,6 @@ export default {
|
||||||
isVehicleProtected: this.isVehicleProtected,
|
isVehicleProtected: this.isVehicleProtected,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async set(newValue) {
|
|
||||||
if (newValue.addressQuestions.zipCode !== this.zipCode) {
|
|
||||||
await getShopProviderData(newValue.addressQuestions.zipCode).then((result) => {
|
|
||||||
this.shopProviderData = result.data;
|
|
||||||
this.selectedProvider = new Provider(
|
|
||||||
this.shopProviderData.mobileProviderNumber
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.setMobileLocation(newValue);
|
|
||||||
//handle promise
|
|
||||||
newValue.resolve?.();
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
isServiceableMobile() {
|
isServiceableMobile() {
|
||||||
if (this.isRecalibrationServiceableMobile !== null) {
|
if (this.isRecalibrationServiceableMobile !== null) {
|
||||||
|
|
@ -324,9 +306,8 @@ export default {
|
||||||
isMobileStaticRecalibrationApplicable() {
|
isMobileStaticRecalibrationApplicable() {
|
||||||
return (
|
return (
|
||||||
this.displayMSR &&
|
this.displayMSR &&
|
||||||
this.isVehicleMobileStaticRecalibrationApplicable &&
|
|
||||||
this.mobileFeePart?.partNumber == partNumberStrings.MOBILE_STATIC_RECAL_FEE &&
|
this.mobileFeePart?.partNumber == partNumberStrings.MOBILE_STATIC_RECAL_FEE &&
|
||||||
(this.isInsurance ? this.mobileFeePart?.isInsurable : true)
|
(this.isInsurance && !this.isITAC ? this.mobileFeePart?.isInsurable : true)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
displayMSR() {
|
displayMSR() {
|
||||||
|
|
@ -403,6 +384,9 @@ export default {
|
||||||
isInsurance() {
|
isInsurance() {
|
||||||
return store.getters.payment.isInsurance;
|
return store.getters.payment.isInsurance;
|
||||||
},
|
},
|
||||||
|
isITAC() {
|
||||||
|
return store.getters.order.policy.isItac;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
arePagePrerequisitesValid() {
|
arePagePrerequisitesValid() {
|
||||||
|
|
@ -488,8 +472,32 @@ export default {
|
||||||
this.mobileFeePart = mobileFeePart;
|
this.mobileFeePart = mobileFeePart;
|
||||||
},
|
},
|
||||||
//creating async function as the computed property can not directly handle asynchronous operations or promises.
|
//creating async function as the computed property can not directly handle asynchronous operations or promises.
|
||||||
async setMobileLocationQuestions(newValue, resolve) {
|
async setMobileLocationQuestions(newValue) {
|
||||||
this.mobileLocationQuestions = { ...newValue, resolve };
|
var newZipCode = newValue.addressQuestions.zipCode;
|
||||||
|
if (newZipCode !== this.zipCode) {
|
||||||
|
await getShopProviderData(newZipCode).then((result) => {
|
||||||
|
if (this.isServiceableMobile) {
|
||||||
|
this.shopProviderData = result.data;
|
||||||
|
this.selectedProvider = new Provider(
|
||||||
|
this.shopProviderData.mobileProviderNumber
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (this.isServiceableMobile) {
|
||||||
|
this.setMobileLocation(newValue);
|
||||||
|
//proceed to next page
|
||||||
|
this.forwardButtonAction();
|
||||||
|
} else {
|
||||||
|
this.closeModalAction("mobileLocationQuestions");
|
||||||
|
await getZipCodeData(newZipCode).then((zipCodeData) => {
|
||||||
|
this.serviceZipCodeQuestion = {
|
||||||
|
state: zipCodeData.state,
|
||||||
|
zipCode: newZipCode,
|
||||||
|
zipCodeCtu: zipCodeData.zipCodeCtu,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
getServiceAddressFromStore() {
|
getServiceAddressFromStore() {
|
||||||
return store.getters.order.serviceLocation.address;
|
return store.getters.order.serviceLocation.address;
|
||||||
|
|
@ -515,9 +523,6 @@ export default {
|
||||||
getSelectedProvider() {
|
getSelectedProvider() {
|
||||||
return store.getters.order.serviceLocation.provider;
|
return store.getters.order.serviceLocation.provider;
|
||||||
},
|
},
|
||||||
getIsVehicleMobileStaticRecalibrationApplicableFromStore() {
|
|
||||||
return store.getters.order.vehicle.isMobileStaticRecalibrationApplicable;
|
|
||||||
},
|
|
||||||
resetMobileLocation() {
|
resetMobileLocation() {
|
||||||
this.streetAddress = "";
|
this.streetAddress = "";
|
||||||
this.apartmentNumberOrBusinessName = "";
|
this.apartmentNumberOrBusinessName = "";
|
||||||
|
|
@ -676,6 +681,9 @@ export default {
|
||||||
openModalAction(modalName) {
|
openModalAction(modalName) {
|
||||||
this.$refs[modalName].openModal();
|
this.$refs[modalName].openModal();
|
||||||
},
|
},
|
||||||
|
closeModalAction(modalName) {
|
||||||
|
this.$refs[modalName].closeModal();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
zipCode: {
|
zipCode: {
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,12 @@
|
||||||
:aria-label="buttonLabel"
|
:aria-label="buttonLabel"
|
||||||
class="button-content list-button-content d-flex flex-column justify-content-center py-3 px-4">
|
class="button-content list-button-content d-flex flex-column justify-content-center py-3 px-4">
|
||||||
<div class="row-one">
|
<div class="row-one">
|
||||||
<span class="m-0 button-label-copy text-truncate" :class="textPosition">{{
|
<span class="m-0 button-label-copy text-truncate" :class="textPosition">
|
||||||
buttonLabel
|
{{ buttonLabel }}
|
||||||
}}</span>
|
</span>
|
||||||
<span class="m-0 caption mx-1 text-nowrap" :class="textPosition">{{
|
<span class="m-0 caption mx-1 text-nowrap" :class="textPosition">
|
||||||
buttonLabelSubCopy
|
{{ buttonLabelSubCopy }}
|
||||||
}}</span>
|
</span>
|
||||||
<div
|
<div
|
||||||
v-if="displayAvailabilityIndicators"
|
v-if="displayAvailabilityIndicators"
|
||||||
class="availability-indicator rounded-pill text-nowrap"
|
class="availability-indicator rounded-pill text-nowrap"
|
||||||
|
|
@ -22,9 +22,9 @@
|
||||||
v-if="!isLoaderDisplayed"
|
v-if="!isLoaderDisplayed"
|
||||||
class="availability-badge"
|
class="availability-badge"
|
||||||
:class="availabilityRating == 'high' ? 'green' : 'orange'"></div>
|
:class="availabilityRating == 'high' ? 'green' : 'orange'"></div>
|
||||||
<span v-if="!isLoaderDisplayed" class="m-0 button-auxillary-copy">{{
|
<span v-if="!isLoaderDisplayed" class="m-0 button-auxillary-copy">
|
||||||
badgeText
|
{{ badgeText }}
|
||||||
}}</span>
|
</span>
|
||||||
<loader
|
<loader
|
||||||
v-if="isLoaderDisplayed"
|
v-if="isLoaderDisplayed"
|
||||||
:loaderPosition="left"
|
:loaderPosition="left"
|
||||||
|
|
|
||||||
|
|
@ -202,12 +202,18 @@ export default {
|
||||||
async forwardButtonAction() {
|
async forwardButtonAction() {
|
||||||
const zipCodeData = await this.getZipCodeData(this.serviceZipCode);
|
const zipCodeData = await this.getZipCodeData(this.serviceZipCode);
|
||||||
|
|
||||||
|
// if no value due to field being optional, blank both phone and email address
|
||||||
|
if (!this.emailOrSms) {
|
||||||
|
await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, "", false);
|
||||||
|
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, "", false);
|
||||||
|
} else {
|
||||||
if (vinPagesMixin.methods.isPhoneNumber(this.emailOrSms)) {
|
if (vinPagesMixin.methods.isPhoneNumber(this.emailOrSms)) {
|
||||||
const phone = this.emailOrSms.replace(/[()]/g, "");
|
const phone = this.emailOrSms.replace(/[()]/g, "");
|
||||||
await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, phone, false);
|
await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, phone, false);
|
||||||
} else {
|
} else {
|
||||||
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, this.emailOrSms, false);
|
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, this.emailOrSms, false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await this.dispatchStoreAction(
|
await this.dispatchStoreAction(
|
||||||
storeActions.SAVE_PAGE_DATA,
|
storeActions.SAVE_PAGE_DATA,
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,20 @@ export default {
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
vm.forwardButtonAction();
|
vm.forwardButtonAction();
|
||||||
} else {
|
} else {
|
||||||
|
if (store.getters.externalParameterCustomer?.phoneNumber) {
|
||||||
|
await baseMixin.methods.dispatchStoreAction(
|
||||||
|
storeActions.SAVE_PHONE_NUMBER,
|
||||||
|
store.getters.externalParameterCustomer.phoneNumber,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
await baseMixin.methods.dispatchStoreAction(
|
||||||
|
storeActions.SAVE_IS_SMS_OPT_IN,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (store.getters.externalParameterServiceZip.zipCode) {
|
if (store.getters.externalParameterServiceZip.zipCode) {
|
||||||
await baseMixin.methods.dispatchStoreAction(
|
await baseMixin.methods.dispatchStoreAction(
|
||||||
storeActions.SAVE_SERVICE_ZIP_CODE_INFO,
|
storeActions.SAVE_SERVICE_ZIP_CODE_INFO,
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,6 @@ export default {
|
||||||
modelOptions: [],
|
modelOptions: [],
|
||||||
styleOptions: [],
|
styleOptions: [],
|
||||||
displayNoServiceAlert: false,
|
displayNoServiceAlert: false,
|
||||||
isMobileStaticRecalibrationApplicable: this.getIsMobileStaticRecalibrationApplicable(),
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -444,8 +443,6 @@ export default {
|
||||||
this.imageVifNumber = result?.data.imageVifNumber;
|
this.imageVifNumber = result?.data.imageVifNumber;
|
||||||
this.imageVifColor = result?.data.imageVifColor;
|
this.imageVifColor = result?.data.imageVifColor;
|
||||||
this.displayNoServiceAlert = !result?.data.canSafeliteService;
|
this.displayNoServiceAlert = !result?.data.canSafeliteService;
|
||||||
this.isMobileStaticRecalibrationApplicable =
|
|
||||||
result?.data.isMobileStaticRecalibrationApplicable;
|
|
||||||
},
|
},
|
||||||
resetAlert() {
|
resetAlert() {
|
||||||
this.displayNoServiceAlert = false;
|
this.displayNoServiceAlert = false;
|
||||||
|
|
@ -464,8 +461,6 @@ export default {
|
||||||
imageUrl: this.imageUrl,
|
imageUrl: this.imageUrl,
|
||||||
imageVifNumber: this.imageVifNumber,
|
imageVifNumber: this.imageVifNumber,
|
||||||
imageVifColor: this.imageVifColor,
|
imageVifColor: this.imageVifColor,
|
||||||
isMobileStaticRecalibrationApplicable:
|
|
||||||
this.isMobileStaticRecalibrationApplicable,
|
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
@ -541,9 +536,6 @@ export default {
|
||||||
getImageVifColorfromStore() {
|
getImageVifColorfromStore() {
|
||||||
return store.getters.vehicle.imageVifColor;
|
return store.getters.vehicle.imageVifColor;
|
||||||
},
|
},
|
||||||
getIsMobileStaticRecalibrationApplicable() {
|
|
||||||
return store.getters.vehicle.isMobileStaticRecalibrationApplicable;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
|
|
|
||||||
|
|
@ -330,11 +330,25 @@ export default {
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// if no value due to field being optional, blank both phone and email address
|
||||||
|
if (!this.emailOrSms) {
|
||||||
|
await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, "", false);
|
||||||
|
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, "", false);
|
||||||
|
} else {
|
||||||
if (vinPagesMixin.methods.isPhoneNumber(this.emailOrSms)) {
|
if (vinPagesMixin.methods.isPhoneNumber(this.emailOrSms)) {
|
||||||
const phone = this.emailOrSms.replace(/[()]/g, "");
|
const phone = this.emailOrSms.replace(/[()]/g, "");
|
||||||
await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, phone, false);
|
await this.dispatchStoreAction(
|
||||||
|
storeActions.SAVE_PHONE_NUMBER,
|
||||||
|
phone,
|
||||||
|
false
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, this.emailOrSms, false);
|
await this.dispatchStoreAction(
|
||||||
|
storeActions.SAVE_EMAIL,
|
||||||
|
this.emailOrSms,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.dispatchStoreAction(
|
await this.dispatchStoreAction(
|
||||||
|
|
@ -380,11 +394,25 @@ export default {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if no value due to field being optional, blank both phone and email address
|
||||||
|
if (!this.emailOrSms) {
|
||||||
|
await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, "", false);
|
||||||
|
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, "", false);
|
||||||
|
} else {
|
||||||
if (vinPagesMixin.methods.isPhoneNumber(this.emailOrSms)) {
|
if (vinPagesMixin.methods.isPhoneNumber(this.emailOrSms)) {
|
||||||
const phone = this.emailOrSms.replace(/[()]/g, "");
|
const phone = this.emailOrSms.replace(/[()]/g, "");
|
||||||
await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, phone, false);
|
await this.dispatchStoreAction(
|
||||||
|
storeActions.SAVE_PHONE_NUMBER,
|
||||||
|
phone,
|
||||||
|
false
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, this.emailOrSms, false);
|
await this.dispatchStoreAction(
|
||||||
|
storeActions.SAVE_EMAIL,
|
||||||
|
this.emailOrSms,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.dispatchStoreAction(
|
await this.dispatchStoreAction(
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,13 @@ export default {
|
||||||
});
|
});
|
||||||
|
|
||||||
let totalPrice = this.getTotalPriceOfAllLineItemsAndChildParts(lineItemsToPrice, false);
|
let totalPrice = this.getTotalPriceOfAllLineItemsAndChildParts(lineItemsToPrice, false);
|
||||||
|
// prettier-ignore
|
||||||
|
{
|
||||||
|
if (store.getters.applicationUser.loggingOption) {
|
||||||
|
console.log(new Date() + " base-mixin getTierOnePackagePrice lineItemsToPrice: " + JSON.stringify(lineItemsToPrice));
|
||||||
|
console.log(new Date() + " base-mixin getTierOnePackagePrice totalPrice: " + totalPrice);
|
||||||
|
}
|
||||||
|
}
|
||||||
return totalPrice;
|
return totalPrice;
|
||||||
},
|
},
|
||||||
filterOutFees(lineItems) {
|
filterOutFees(lineItems) {
|
||||||
|
|
@ -103,6 +110,12 @@ export default {
|
||||||
});
|
});
|
||||||
return filteredLineItems;
|
return filteredLineItems;
|
||||||
},
|
},
|
||||||
|
filterOutServicePackageDiscountPart(lineItems) {
|
||||||
|
const filteredLineItems = lineItems?.filter((item) => {
|
||||||
|
return !item?.partType?.includes(partTypeStrings.SERVICE_PACKAGE_DISCOUNT);
|
||||||
|
});
|
||||||
|
return filteredLineItems;
|
||||||
|
},
|
||||||
filterOutRecalibration(lineItems) {
|
filterOutRecalibration(lineItems) {
|
||||||
return getItemsWithoutRecalParts(lineItems);
|
return getItemsWithoutRecalParts(lineItems);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import { singleWindshieldCarIds } from "@/constants/single-windshield-carids";
|
||||||
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||||
import { deleteFunnelCookie } from "@/helpers/heritage-integration/cookie-helper.js";
|
import { deleteFunnelCookie } from "@/helpers/heritage-integration/cookie-helper.js";
|
||||||
import { deepEqual } from "@/helpers/object-helper";
|
import { deepEqual } from "@/helpers/object-helper";
|
||||||
|
import baseMixin from "@/mixins/base-mixin.js";
|
||||||
import {
|
import {
|
||||||
AppointmentTypeStrings,
|
AppointmentTypeStrings,
|
||||||
PREMIUM_FEE_PART_TYPE,
|
PREMIUM_FEE_PART_TYPE,
|
||||||
|
|
@ -45,6 +46,7 @@ import {
|
||||||
containsRecalParts,
|
containsRecalParts,
|
||||||
getTopLevelPartsWithRecal,
|
getTopLevelPartsWithRecal,
|
||||||
isRecalPartOrHasChildRecalPart,
|
isRecalPartOrHasChildRecalPart,
|
||||||
|
getRecalPartNumbers,
|
||||||
} from "@/helpers/recal-helper";
|
} from "@/helpers/recal-helper";
|
||||||
import { externalParameterStatus } from "@/constants/external-parameters";
|
import { externalParameterStatus } from "@/constants/external-parameters";
|
||||||
import { experimentSettings } from "@/constants/experiments";
|
import { experimentSettings } from "@/constants/experiments";
|
||||||
|
|
@ -68,7 +70,6 @@ const getDefaultState = () => {
|
||||||
registration: {
|
registration: {
|
||||||
licensePlate: null,
|
licensePlate: null,
|
||||||
},
|
},
|
||||||
isMobileStaticRecalibrationApplicable: false,
|
|
||||||
},
|
},
|
||||||
serviceLocation: {
|
serviceLocation: {
|
||||||
address: null,
|
address: null,
|
||||||
|
|
@ -227,10 +228,6 @@ export const mutations = {
|
||||||
updateVehicleVin(state, vin) {
|
updateVehicleVin(state, vin) {
|
||||||
state.order.vehicle.vin = vin;
|
state.order.vehicle.vin = vin;
|
||||||
},
|
},
|
||||||
updateIsMobileStaticRecalibrationApplicable(state, isMobileStaticRecalibrationApplicable) {
|
|
||||||
state.order.vehicle.isMobileStaticRecalibrationApplicable =
|
|
||||||
isMobileStaticRecalibrationApplicable;
|
|
||||||
},
|
|
||||||
updateIsRepair(state, isRepair) {
|
updateIsRepair(state, isRepair) {
|
||||||
state.order.damage.isRepair = isRepair;
|
state.order.damage.isRepair = isRepair;
|
||||||
},
|
},
|
||||||
|
|
@ -365,8 +362,6 @@ export const mutations = {
|
||||||
state.order.vehicle.imageUrl = vehicleInfo.imageUrl;
|
state.order.vehicle.imageUrl = vehicleInfo.imageUrl;
|
||||||
state.order.vehicle.imageVifNumber = vehicleInfo.imageVifNumber;
|
state.order.vehicle.imageVifNumber = vehicleInfo.imageVifNumber;
|
||||||
state.order.vehicle.imageColor = vehicleInfo.imageVifColor;
|
state.order.vehicle.imageColor = vehicleInfo.imageVifColor;
|
||||||
state.order.vehicle.isMobileStaticRecalibrationApplicable =
|
|
||||||
vehicleInfo.isMobileStaticRecalibrationApplicable;
|
|
||||||
},
|
},
|
||||||
updateRegistration(state, registrationInfo) {
|
updateRegistration(state, registrationInfo) {
|
||||||
state.order.vehicle.registration.licensePlate = registrationInfo?.licensePlate;
|
state.order.vehicle.registration.licensePlate = registrationInfo?.licensePlate;
|
||||||
|
|
@ -535,6 +530,7 @@ export const mutations = {
|
||||||
},
|
},
|
||||||
resetExternalParameterQuoteState(state) {
|
resetExternalParameterQuoteState(state) {
|
||||||
externalParameterState.quote.servicePackage = null;
|
externalParameterState.quote.servicePackage = null;
|
||||||
|
externalParameterState.quote.isInsurance = null;
|
||||||
saveExternalParameterState(externalParameterState);
|
saveExternalParameterState(externalParameterState);
|
||||||
},
|
},
|
||||||
resetIsExternalParameter(state) {
|
resetIsExternalParameter(state) {
|
||||||
|
|
@ -559,7 +555,6 @@ export const mutations = {
|
||||||
state.order.vehicle.imageUrl = null;
|
state.order.vehicle.imageUrl = null;
|
||||||
state.order.vehicle.imageVifNumber = null;
|
state.order.vehicle.imageVifNumber = null;
|
||||||
state.order.vehicle.imageColor = null;
|
state.order.vehicle.imageColor = null;
|
||||||
state.order.vehicle.isMobileStaticRecalibrationApplicable = false;
|
|
||||||
},
|
},
|
||||||
resetDamageState(state) {
|
resetDamageState(state) {
|
||||||
state.order.damage.isRepair = null;
|
state.order.damage.isRepair = null;
|
||||||
|
|
@ -1028,8 +1023,12 @@ export const getters = {
|
||||||
return email;
|
return email;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we're here, then we have both email and phone so get the value from pageData to reflect what they entered on the page
|
// if we're here, then we have both email and phone so get the value from pageData to reflect what they entered on the page.
|
||||||
return state.applicationUser.pageData[fmgPageValues.EMAIL_SMS_PAGES]?.emailOrSmsValue;
|
// if they didn't enter anything on the page default to email
|
||||||
|
return (
|
||||||
|
state.applicationUser.pageData[fmgPageValues.EMAIL_SMS_PAGES]?.emailOrSmsValue ??
|
||||||
|
state.order.customer.emailAddress
|
||||||
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1705,8 +1704,6 @@ export const actions = {
|
||||||
const coverageStatus = coverageStatusEnum(order.payment?.insuranceCoverage?.coverageStatus);
|
const coverageStatus = coverageStatusEnum(order.payment?.insuranceCoverage?.coverageStatus);
|
||||||
const coverageType = coverageTypeEnum(order.payment?.insuranceCoverage?.coverageType);
|
const coverageType = coverageTypeEnum(order.payment?.insuranceCoverage?.coverageType);
|
||||||
const isItacOptimized = order.policy?.isItac ?? false;
|
const isItacOptimized = order.policy?.isItac ?? false;
|
||||||
const isMobileStaticRecalibrationApplicable =
|
|
||||||
order.vehicle?.isMobileStaticRecalibrationApplicable;
|
|
||||||
const zipCode =
|
const zipCode =
|
||||||
serviceZipCode ??
|
serviceZipCode ??
|
||||||
order.serviceLocation?.provider?.address?.zipCode ??
|
order.serviceLocation?.provider?.address?.zipCode ??
|
||||||
|
|
@ -1722,12 +1719,15 @@ export const actions = {
|
||||||
|
|
||||||
var endPoint = `${endpoints.GetMobileFeePart.url}/?serviceType=${serviceType}&facilityType=${facilityType}&parentAccountNumber=${parentAccountNumber}&billToAccountNumber=${billToAccountNumber}&providerNumber=${providerNumber}&isItacOptimized=${isItacOptimized}&zipCode=${zipCode}`;
|
var endPoint = `${endpoints.GetMobileFeePart.url}/?serviceType=${serviceType}&facilityType=${facilityType}&parentAccountNumber=${parentAccountNumber}&billToAccountNumber=${billToAccountNumber}&providerNumber=${providerNumber}&isItacOptimized=${isItacOptimized}&zipCode=${zipCode}`;
|
||||||
|
|
||||||
if (isMobileStaticRecalibrationApplicable) {
|
const recalPartNumbers = getRecalPartNumbers(order.lineItems?.glassParts);
|
||||||
const recalPartNumber = getRecalPartNumber(order.lineItems?.glassParts[0]);
|
|
||||||
const carId = order.vehicle?.carId;
|
const carId = order.vehicle?.carId;
|
||||||
if (recalPartNumber && carId) {
|
if (recalPartNumbers && recalPartNumbers.length > 0 && carId) {
|
||||||
endPoint = `${endPoint}&partNumbers=${recalPartNumber}&carId=${carId}`;
|
let additionalQueryParams = ``;
|
||||||
|
for (let i = 0; i < recalPartNumbers.length; i++) {
|
||||||
|
additionalQueryParams += `&partNumbers[${i}]=${recalPartNumbers[i]}`;
|
||||||
}
|
}
|
||||||
|
additionalQueryParams += `&carId=${carId}`;
|
||||||
|
endPoint += additionalQueryParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (coverageStatus) {
|
if (coverageStatus) {
|
||||||
|
|
@ -2330,18 +2330,7 @@ export const actions = {
|
||||||
// Vehicle
|
// Vehicle
|
||||||
saveVehicle(
|
saveVehicle(
|
||||||
context,
|
context,
|
||||||
{
|
{ year, make, model, style, carId, category, imageUrl, imageVifNumber, imageVifColor }
|
||||||
year,
|
|
||||||
make,
|
|
||||||
model,
|
|
||||||
style,
|
|
||||||
carId,
|
|
||||||
category,
|
|
||||||
imageUrl,
|
|
||||||
imageVifNumber,
|
|
||||||
imageVifColor,
|
|
||||||
isMobileStaticRecalibrationApplicable,
|
|
||||||
}
|
|
||||||
) {
|
) {
|
||||||
if (
|
if (
|
||||||
context.state.order.vehicle.year != year ||
|
context.state.order.vehicle.year != year ||
|
||||||
|
|
@ -2362,10 +2351,6 @@ export const actions = {
|
||||||
context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_URL, imageUrl);
|
context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_URL, imageUrl);
|
||||||
context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, imageVifNumber);
|
context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, imageVifNumber);
|
||||||
context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, imageVifColor);
|
context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, imageVifColor);
|
||||||
context.commit(
|
|
||||||
storeMutations.UPDATE_VEHICLE_MOBILE_STATIC_RECALIBRATION_APPLICABLE,
|
|
||||||
isMobileStaticRecalibrationApplicable
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -3190,6 +3175,42 @@ export const actions = {
|
||||||
context.commit(storeMutations.UPDATE_AFFILIATE_COOKIES, affiliateCookies);
|
context.commit(storeMutations.UPDATE_AFFILIATE_COOKIES, affiliateCookies);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
addDonationToSubmittedOrder(context, donationAmount) {
|
||||||
|
console.log("running addDonationToSubmittedOrder()... donationAmount: ", donationAmount);
|
||||||
|
|
||||||
|
const submittedOrder = deepClone(baseMixin.methods.getSubmittedOrder());
|
||||||
|
|
||||||
|
if (donationAmount > 0) {
|
||||||
|
console.log(
|
||||||
|
"running addDonationToSubmittedOrder()... adding donation to supportingItems in session storage "
|
||||||
|
);
|
||||||
|
submittedOrder.lineItems.supportingItems.push({
|
||||||
|
partNumber: "DONATION",
|
||||||
|
description: null,
|
||||||
|
partType: "DONATION",
|
||||||
|
isInsurable: false,
|
||||||
|
isChildPart: false,
|
||||||
|
laborAmount: 0,
|
||||||
|
sellingPrice: donationAmount,
|
||||||
|
kitPrice: 0,
|
||||||
|
salesTax: null,
|
||||||
|
id: "donation-" + donationAmount,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// filter out all donations
|
||||||
|
console.log(
|
||||||
|
"running addDonationToSubmittedOrder()... removing all donation items in supportingItems in session storage "
|
||||||
|
);
|
||||||
|
|
||||||
|
const nonDonationItems = submittedOrder.lineItems.supportingItems.filter((item) => {
|
||||||
|
return item.partType !== "DONATION";
|
||||||
|
});
|
||||||
|
submittedOrder.lineItems.supportingItems = nonDonationItems;
|
||||||
|
}
|
||||||
|
// set to local storage
|
||||||
|
window.sessionStorage.setItem("submittedOrder", JSON.stringify(submittedOrder));
|
||||||
|
},
|
||||||
|
|
||||||
resetSubmittedOrder(context) {
|
resetSubmittedOrder(context) {
|
||||||
// clear from local storage
|
// clear from local storage
|
||||||
window.sessionStorage.removeItem("submittedOrder");
|
window.sessionStorage.removeItem("submittedOrder");
|
||||||
|
|
@ -3791,15 +3812,3 @@ function getExternalParameterDefaultState() {
|
||||||
function saveExternalParameterState(externalParameterState) {
|
function saveExternalParameterState(externalParameterState) {
|
||||||
window.sessionStorage.setItem("externalParameterState", JSON.stringify(externalParameterState));
|
window.sessionStorage.setItem("externalParameterState", JSON.stringify(externalParameterState));
|
||||||
}
|
}
|
||||||
|
|
||||||
//This function finds the recalibration part and returns the part number for it.
|
|
||||||
function getRecalPartNumber(glassPartsArray) {
|
|
||||||
const recalPart = glassPartsArray.childParts.find(
|
|
||||||
(item) => item.partType === partTypeStrings.ADAS_RECALIBRATION
|
|
||||||
);
|
|
||||||
if (recalPart) {
|
|
||||||
return recalPart.partNumber;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -156,5 +156,48 @@ export default {
|
||||||
transition: background 0s 0s ease-in-out;
|
transition: background 0s 0s ease-in-out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&.btn-success {
|
||||||
|
position: relative;
|
||||||
|
background: $green-100;
|
||||||
|
border: 1px solid $green-400;
|
||||||
|
border-radius: $border-radius-lg;
|
||||||
|
color: $black;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 150ms linear;
|
||||||
|
height: 3rem;
|
||||||
|
&:hover {
|
||||||
|
color: $black;
|
||||||
|
background: $green-200;
|
||||||
|
}
|
||||||
|
&:focus, // Mouse, touch, stylus focus
|
||||||
|
&:focus-visible {
|
||||||
|
// Keyboard focus for accessibility
|
||||||
|
outline: none;
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 3px $white,
|
||||||
|
0 0 0 5.5px $green-400;
|
||||||
|
color: $black;
|
||||||
|
background: $green-100;
|
||||||
|
}
|
||||||
|
&:disabled {
|
||||||
|
background: transparent;
|
||||||
|
color: $gray-550 !important;
|
||||||
|
font-weight: 400;
|
||||||
|
height: 48px;
|
||||||
|
border: 1px solid $gray-550;
|
||||||
|
border-radius: $border-radius-lg;
|
||||||
|
cursor: pointer;
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
|
&.has-loader {
|
||||||
|
color: $white;
|
||||||
|
@include blue-gradient;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
&.delay {
|
||||||
|
// fixes flicker while transitioning between states
|
||||||
|
transition: background 0s 0s ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,26 @@
|
||||||
<template>
|
<template>
|
||||||
<loadingModal v-if="useLoadingModal" notFullScreen ref="loadingModal" />
|
<loadingModal v-if="useLoadingModal" notFullScreen ref="loadingModal" />
|
||||||
<a v-if="linkType === 'navigation'" @click="handleClick" class="navigation-link" :href="href"
|
<a v-if="linkType === 'navigation'" @click="handleClick" class="navigation-link" :href="href">
|
||||||
>{{ text }}<slot name="after-text"></slot
|
{{ text }}
|
||||||
></a>
|
<slot name="after-text"></slot>
|
||||||
|
</a>
|
||||||
<a
|
<a
|
||||||
v-else-if="linkType === 'newWindowLink'"
|
v-else-if="linkType === 'newWindowLink'"
|
||||||
@click="handleClick"
|
@click="handleClick"
|
||||||
class="new-window-link"
|
class="new-window-link"
|
||||||
:href="href"
|
:href="href"
|
||||||
target="_blank"
|
target="_blank">
|
||||||
>{{ text }}<slot name="after-text"></slot
|
{{ text }}
|
||||||
></a>
|
<slot name="after-text"></slot>
|
||||||
<a v-else-if="linkType === 'textSmall'" @click="handleClick" class="small" :href="href"
|
</a>
|
||||||
>{{ text }}<slot name="after-text"></slot
|
<a v-else-if="linkType === 'textSmall'" @click="handleClick" class="small" :href="href">
|
||||||
></a>
|
{{ text }}
|
||||||
<a v-else-if="linkType === 'text'" @click="handleClick" :href="href"
|
<slot name="after-text"></slot>
|
||||||
>{{ text }}<slot name="after-text"></slot
|
</a>
|
||||||
></a>
|
<a v-else-if="linkType === 'text'" @click="handleClick" :href="href">
|
||||||
|
{{ text }}
|
||||||
|
<slot name="after-text"></slot>
|
||||||
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue