Merge branch 'release/2025.05.22' into rlsmerge/2025.05.22-to-develop
This commit is contained in:
commit
7d19e1d931
30 changed files with 512 additions and 277 deletions
|
|
@ -23,7 +23,6 @@ const GaActions = {
|
||||||
SUBMITTED: "Submitted",
|
SUBMITTED: "Submitted",
|
||||||
DISPLAYED: "Displayed",
|
DISPLAYED: "Displayed",
|
||||||
CASH_QUOTE_DISPLAYED: "Cash quote displayed",
|
CASH_QUOTE_DISPLAYED: "Cash quote displayed",
|
||||||
SERVICE_PACKAGE_PRICE: "Service package price",
|
|
||||||
MOBILE_AVAILABLE: "mobile_available",
|
MOBILE_AVAILABLE: "mobile_available",
|
||||||
SHOPS_FIRST_DISPLAYED: "shops_first_displayed",
|
SHOPS_FIRST_DISPLAYED: "shops_first_displayed",
|
||||||
MORE_LOCATIONS_CLICKED: "more_locations_clicked",
|
MORE_LOCATIONS_CLICKED: "more_locations_clicked",
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ const storeMutations = {
|
||||||
UPDATE_SETTLED_TENDER_AMOUNT: "updateSettledTenderAmount",
|
UPDATE_SETTLED_TENDER_AMOUNT: "updateSettledTenderAmount",
|
||||||
UPDATE_IS_RECAL_ACK_OPT_IN: "updateIsRecalAckOptIn",
|
UPDATE_IS_RECAL_ACK_OPT_IN: "updateIsRecalAckOptIn",
|
||||||
UPDATE_IS_MSR_FEE_APPLICABLE: "updateIsMSRFeeApplicable",
|
UPDATE_IS_MSR_FEE_APPLICABLE: "updateIsMSRFeeApplicable",
|
||||||
|
UPDATE_CASH_PRICE_SUBTOTAL: "updateCashPriceSubTotal",
|
||||||
|
|
||||||
// EVENT BUS MUTATIONS
|
// EVENT BUS MUTATIONS
|
||||||
ADD_EVENT_TO_BUS: "addEventToBus",
|
ADD_EVENT_TO_BUS: "addEventToBus",
|
||||||
|
|
|
||||||
|
|
@ -35,4 +35,9 @@ export const inputButtonProps = {
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
suppressError: Boolean,
|
suppressError: Boolean,
|
||||||
|
labelBold: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,9 @@ https://safelite.atlassian.net/wiki/spaces/DC/pages/76644418/Button+Question+Com
|
||||||
v-if="questionText && answers && answers.length > 0"
|
v-if="questionText && answers && answers.length > 0"
|
||||||
class="question-text d-flex"
|
class="question-text d-flex"
|
||||||
:class="{ 'small-question-text': isSmallQuestionText }">
|
:class="{ 'small-question-text': isSmallQuestionText }">
|
||||||
<span class="fw-bold w-100">{{ questionText }}</span>
|
<span class="fw-bold w-100" :class="[labelBold ? 'span-bold' : '']">
|
||||||
|
{{ questionText }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-100 d-flex justify-content-center">
|
<div class="w-100 d-flex justify-content-center">
|
||||||
|
|
@ -57,6 +59,7 @@ https://safelite.atlassian.net/wiki/spaces/DC/pages/76644418/Button+Question+Com
|
||||||
:setLastValuePushedToGa="setLastValuePushedToGa"
|
:setLastValuePushedToGa="setLastValuePushedToGa"
|
||||||
:isInsuranceSelected="isInsuranceSelected"
|
:isInsuranceSelected="isInsuranceSelected"
|
||||||
:suppressError="suppressError"
|
:suppressError="suppressError"
|
||||||
|
:labelBold="labelBold"
|
||||||
@buttonEvent="handleButtonEvent"
|
@buttonEvent="handleButtonEvent"
|
||||||
v-model="selectedValues" />
|
v-model="selectedValues" />
|
||||||
<!-- For nested questions -->
|
<!-- For nested questions -->
|
||||||
|
|
@ -141,6 +144,11 @@ export default {
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
isInsuranceSelected: Boolean,
|
isInsuranceSelected: Boolean,
|
||||||
|
labelBold: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const propsClone = Object.assign({}, props);
|
const propsClone = Object.assign({}, props);
|
||||||
|
|
@ -360,6 +368,9 @@ export default {
|
||||||
& > span {
|
& > span {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
.span-bold {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.small-question-text {
|
.small-question-text {
|
||||||
&.question-text {
|
&.question-text {
|
||||||
|
|
|
||||||
|
|
@ -1042,6 +1042,9 @@ export default {
|
||||||
min-width: 3rem;
|
min-width: 3rem;
|
||||||
width: 3rem;
|
width: 3rem;
|
||||||
}
|
}
|
||||||
|
&:hover {
|
||||||
|
border: 2px solid $blue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.unavailable-day {
|
&.unavailable-day {
|
||||||
|
|
@ -1083,7 +1086,6 @@ export default {
|
||||||
}
|
}
|
||||||
input[type="radio"]:checked + label:after {
|
input[type="radio"]:checked + label:after {
|
||||||
background-color: $black;
|
background-color: $black;
|
||||||
top: 2rem;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
:for="dropdownId"
|
:for="dropdownId"
|
||||||
:aria-label="questionText"
|
:aria-label="questionText"
|
||||||
class="form-label"
|
class="form-label"
|
||||||
|
:class="[labelBold ? 'label-bold' : '']"
|
||||||
v-html="questionText"></label>
|
v-html="questionText"></label>
|
||||||
<select
|
<select
|
||||||
v-model="selectedOption"
|
v-model="selectedOption"
|
||||||
|
|
@ -61,6 +62,11 @@ export default {
|
||||||
required: false,
|
required: false,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
labelBold: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const uuid = uuidv4();
|
const uuid = uuidv4();
|
||||||
|
|
@ -133,6 +139,10 @@ export default {
|
||||||
color: $black;
|
color: $black;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
.label-bold {
|
||||||
|
color: $black;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
.form-label {
|
.form-label {
|
||||||
margin-bottom: 0.25rem;
|
margin-bottom: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,12 +119,14 @@ export default {
|
||||||
},
|
},
|
||||||
onModalOpened() {
|
onModalOpened() {
|
||||||
this.onModalOpenedCallback?.();
|
this.onModalOpenedCallback?.();
|
||||||
|
this.$emit("isModalOpened", true);
|
||||||
},
|
},
|
||||||
onModalClosed() {
|
onModalClosed() {
|
||||||
this.resetButtonStyle();
|
this.resetButtonStyle();
|
||||||
this.onModalClosedCallback?.();
|
this.onModalClosedCallback?.();
|
||||||
if (this.suppressPageScroll)
|
if (this.suppressPageScroll)
|
||||||
document.querySelector("body").classList.remove("prevent-modal-scroll");
|
document.querySelector("body").classList.remove("prevent-modal-scroll");
|
||||||
|
this.$emit("isModalOpened", false);
|
||||||
},
|
},
|
||||||
openModal() {
|
openModal() {
|
||||||
if (this.suppressPageScroll)
|
if (this.suppressPageScroll)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<span class="salesforce-webchat"></span>
|
<span class="salesforce-webchat">
|
||||||
|
<button v-show="hideSalesforceWebchatLaunchButton" class="salesforce-chat-button"></button>
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
@ -49,12 +51,121 @@ export default {
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style type="text/css">
|
<style lang="scss">
|
||||||
.embeddedServiceHelpButton .helpButton .uiButton {
|
.salesforce-webchat {
|
||||||
background-color: #167cac;
|
.salesforce-chat-button {
|
||||||
font-family: "Arial", sans-serif;
|
width: 1.5rem;
|
||||||
|
height: 1.5rem;
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xml:space='preserve' id='Layer_2' x='0' y='0' style='enable-background:new 0 0 24 24' version='1.1' viewBox='0 0 24 24'%3E%3Cstyle%3E .st0%7Bfill:%232e6fb6%7D %3C/style%3E%3Ccircle cx='12' cy='12' r='12' class='st0'/%3E%3Cpath d='M18.3 12c0 3.5-2.8 6.3-6.3 6.3-1.2 0-2.4-.4-3.4-1l-2.3.7s-.5.1-.3-.4.5-1.7.7-2.2c-.6-1-1-2.1-1-3.3 0-3.5 2.8-6.3 6.3-6.3 3.5-.1 6.3 2.7 6.3 6.2z' style='fill:%23fff'/%3E%3Ccircle cx='10.2' cy='12' r='.6' class='st0'/%3E%3Ccircle cx='12' cy='12' r='.6' class='st0'/%3E%3Ccircle cx='13.8' cy='12' r='.6' class='st0'/%3E%3C/svg%3E");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 1.5rem 1.5rem;
|
||||||
|
background-position: center;
|
||||||
|
border: none;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dockableContainer {
|
||||||
|
&.showDockableContainer {
|
||||||
|
font-family: AvertaRegular;
|
||||||
|
color: $gray-600;
|
||||||
|
.sidebarBody {
|
||||||
|
button {
|
||||||
|
background: $blue;
|
||||||
|
color: $white;
|
||||||
|
&:hover {
|
||||||
|
background: $blue-700 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.form-element__help {
|
||||||
|
color: $red;
|
||||||
|
}
|
||||||
|
.embeddedServiceSidebarFormField {
|
||||||
|
.uiInput {
|
||||||
|
.uiLabel-left {
|
||||||
|
color: $gray-600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.embeddedServiceSidebarButton {
|
||||||
|
&.uiButton--inverse {
|
||||||
|
.label {
|
||||||
|
color: $white;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
&:not(:disabled):focus {
|
||||||
|
background-color: $blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.embeddedServiceLiveAgentStateChatAvatar {
|
||||||
|
&.isLightningOutContext {
|
||||||
|
.agentIconColor0 {
|
||||||
|
background-color: $blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.embeddedServiceLiveAgentStateChatInputFooter {
|
||||||
|
.footerMenuWrapper {
|
||||||
|
.footer-menu-items {
|
||||||
|
.slds-dropdown__item {
|
||||||
|
> a {
|
||||||
|
padding-left: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.embeddedServiceLiveAgentStateChatInputFooter {
|
||||||
|
.footerMenuWrapper {
|
||||||
|
.footer-menu-items {
|
||||||
|
a {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.chatHeaderBranding {
|
||||||
|
background-color: $blue;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
&.required {
|
||||||
|
color: $red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.embeddedServiceHelpButton {
|
||||||
|
.helpButton {
|
||||||
|
.uiButton {
|
||||||
|
background-color: $blue !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.embeddedServiceIcon {
|
||||||
|
display: inline-block !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.modalContainer {
|
||||||
|
&.sidebarMinimized {
|
||||||
|
&.layout-docted {
|
||||||
|
&.embeddedServiceSidebar {
|
||||||
|
.embeddedServiceSidebarMinimizedDefaultUI {
|
||||||
|
.helpButton,
|
||||||
|
.sidebarHeader {
|
||||||
|
&.minimizedContainer {
|
||||||
|
.embeddedServiceSidebarMinimizedDefaultUI {
|
||||||
|
background-color: $blue;
|
||||||
|
border: 1px solid $blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.embeddedServiceHelpButton .helpButton .uiButton:focus {
|
|
||||||
outline: 1px solid #167cac;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
:class="[
|
:class="[
|
||||||
questionAlignment === 'center' ? 'text-center w-100 mb-5' : '',
|
questionAlignment === 'center' ? 'text-center w-100 mb-5' : '',
|
||||||
hideInput ? 'hide-input' : '',
|
hideInput ? 'hide-input' : '',
|
||||||
|
labelBold ? 'label-bold' : '',
|
||||||
]"
|
]"
|
||||||
v-html="questionText"></label>
|
v-html="questionText"></label>
|
||||||
<div
|
<div
|
||||||
|
|
@ -132,6 +133,11 @@ export default {
|
||||||
isRequired: false,
|
isRequired: false,
|
||||||
},
|
},
|
||||||
autocomplete: String,
|
autocomplete: String,
|
||||||
|
labelBold: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const uuid = uuidv4();
|
const uuid = uuidv4();
|
||||||
|
|
@ -251,6 +257,9 @@ export default {
|
||||||
color: $black;
|
color: $black;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
.label-bold {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
.input-wrapper {
|
.input-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
&.has-search-icon {
|
&.has-search-icon {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@
|
||||||
hasIcon
|
hasIcon
|
||||||
validationRules="street-address-required"
|
validationRules="street-address-required"
|
||||||
autocomplete="street-address"
|
autocomplete="street-address"
|
||||||
@keydown.enter.prevent />
|
@keydown.enter.prevent
|
||||||
|
:labelBold="labelBold" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<transition name="fade" mode="out-in">
|
<transition name="fade" mode="out-in">
|
||||||
|
|
@ -27,7 +28,8 @@
|
||||||
customInputId="apartmentNumberOrBusinessName"
|
customInputId="apartmentNumberOrBusinessName"
|
||||||
cmsWidgetName="ApartmentNumberOrBusinessNameQuestionWidget"
|
cmsWidgetName="ApartmentNumberOrBusinessNameQuestionWidget"
|
||||||
v-model="addressModel.apartmentNumberOrBusinessName"
|
v-model="addressModel.apartmentNumberOrBusinessName"
|
||||||
ref="apartmentNumberOrBusinessName" />
|
ref="apartmentNumberOrBusinessName"
|
||||||
|
:labelBold="labelBold" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
|
|
@ -40,7 +42,8 @@
|
||||||
v-model="addressModel.city"
|
v-model="addressModel.city"
|
||||||
ref="city"
|
ref="city"
|
||||||
validationRules="city-required"
|
validationRules="city-required"
|
||||||
autocomplete="address-level2" />
|
autocomplete="address-level2"
|
||||||
|
:labelBold="labelBold" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
|
|
@ -54,7 +57,8 @@
|
||||||
ref="state"
|
ref="state"
|
||||||
:options="stateOptions"
|
:options="stateOptions"
|
||||||
validationRules="state-required"
|
validationRules="state-required"
|
||||||
autocomplete="address-level1" />
|
autocomplete="address-level1"
|
||||||
|
:labelBold="labelBold" />
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<textboxQuestion
|
<textboxQuestion
|
||||||
|
|
@ -64,7 +68,8 @@
|
||||||
ref="zipCode"
|
ref="zipCode"
|
||||||
mask="#####"
|
mask="#####"
|
||||||
validationRules="zip-code-required|zip-code-format"
|
validationRules="zip-code-required|zip-code-format"
|
||||||
autocomplete="postal-code" />
|
autocomplete="postal-code"
|
||||||
|
:labelBold="labelBold" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
|
|
@ -124,6 +129,11 @@ export default {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
labelBold: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,22 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div class="funnel-header" v-if="imageSrc">
|
||||||
class="funnel-header d-flex justify-content-center align-items-center flex-column"
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
v-if="imageSrc">
|
<div class="button-container">
|
||||||
<!-- HIDDEN FOR 05.08 <salesforceWebchat hideSalesforceWebchatLaunchButton /> -->
|
<!-- HIDDEN FOR 05.08 <salesforceWebchat hideSalesforceWebchatLaunchButton /> -->
|
||||||
|
</div>
|
||||||
|
<div class="site-logo">
|
||||||
<img class="logo-image img-fluid" :src="imageSrc" alt="Safelite logo" />
|
<img class="logo-image img-fluid" :src="imageSrc" alt="Safelite logo" />
|
||||||
|
</div>
|
||||||
|
<div class="button-container">
|
||||||
<!-- // menu-modal temporarily not in use until the hamburger menu is re-implemented for use with the progress bar. See display: none below. -->
|
<!-- // menu-modal temporarily not in use until the hamburger menu is re-implemented for use with the progress bar. See display: none below. -->
|
||||||
<menuModal />
|
<menuModal />
|
||||||
<!-- // menu-modal temporarily not in use until the hamburger menu is re-implemented for use with the progress bar. See display: none below. -->
|
<!-- // menu-modal temporarily not in use until the hamburger menu is re-implemented for use with the progress bar. See display: none below. -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex w-100">
|
||||||
<progressBar />
|
<progressBar />
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<template v-for="globalAlert in globalAlertMessages" :key="globalAlert.id">
|
<template v-for="globalAlert in globalAlertMessages" :key="globalAlert.id">
|
||||||
<transition name="fade" mode="out-in">
|
<transition name="fade" mode="out-in">
|
||||||
<alert
|
<alert
|
||||||
|
|
@ -45,7 +53,7 @@ export default {
|
||||||
cmsWidgetName: String,
|
cmsWidgetName: String,
|
||||||
hideSalesforceWebchatLaunchButton: {
|
hideSalesforceWebchatLaunchButton: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -100,16 +108,23 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
:deep(.menu-modal-container) {
|
|
||||||
display: none; // This is temporary until the hamburger menu is re-implemented for use with the progress bar
|
|
||||||
}
|
|
||||||
.funnel-header {
|
.funnel-header {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-top: 0.97rem;
|
padding: 1rem 0;
|
||||||
|
|
||||||
|
.button-container {
|
||||||
|
width: 1.5rem;
|
||||||
|
height: 1.625rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo-image {
|
.logo-image {
|
||||||
width: auto;
|
width: auto;
|
||||||
height: 1.1875rem;
|
height: 1.1875rem;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.menu-modal-container) {
|
||||||
|
display: none; // This is temporary until the hamburger menu is re-implemented for use with the progress bar
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -11,21 +11,6 @@
|
||||||
<div class="bar3"></div>
|
<div class="bar3"></div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="menu-modal-container">
|
|
||||||
<button
|
|
||||||
aria-hidden="true"
|
|
||||||
tabindex="-1"
|
|
||||||
class="menu-button"
|
|
||||||
type="button"
|
|
||||||
:class="[isActive ? 'active' : '']"
|
|
||||||
@click="toggleModal"
|
|
||||||
aria-label="Hamburger Menu (modal window)">
|
|
||||||
<div class="bar1"></div>
|
|
||||||
<div class="bar2"></div>
|
|
||||||
<div class="bar3"></div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<!-- Modal -->
|
|
||||||
<div
|
<div
|
||||||
class="modal menu-modal fade"
|
class="modal menu-modal fade"
|
||||||
data-bs-backdrop="false"
|
data-bs-backdrop="false"
|
||||||
|
|
@ -123,9 +108,6 @@ export default {
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.menu-modal-container {
|
.menu-modal-container {
|
||||||
position: absolute;
|
|
||||||
padding: 1.47rem 0 1.47rem 1.47rem;
|
|
||||||
right: 0;
|
|
||||||
button {
|
button {
|
||||||
border: none;
|
border: none;
|
||||||
&.menu-button {
|
&.menu-button {
|
||||||
|
|
@ -164,7 +146,7 @@ export default {
|
||||||
}
|
}
|
||||||
.modal {
|
.modal {
|
||||||
&.menu-modal {
|
&.menu-modal {
|
||||||
left: auto;
|
//left: auto;
|
||||||
height: calc(100% - 56px);
|
height: calc(100% - 56px);
|
||||||
top: 56px;
|
top: 56px;
|
||||||
border-top: 1px solid $gray-300;
|
border-top: 1px solid $gray-300;
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ export default {
|
||||||
cmsWidgetName: String,
|
cmsWidgetName: String,
|
||||||
lineItems: Array,
|
lineItems: Array,
|
||||||
isAfterpayBreakoutDisplay: Boolean,
|
isAfterpayBreakoutDisplay: Boolean,
|
||||||
|
isInsuranceSelected: Boolean,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {};
|
return {};
|
||||||
|
|
@ -72,9 +73,14 @@ export default {
|
||||||
afterpayCopyTokens() {
|
afterpayCopyTokens() {
|
||||||
if (this.isAfterpayBreakoutDisplay) {
|
if (this.isAfterpayBreakoutDisplay) {
|
||||||
// Text for Afterpay Breakout experiment
|
// Text for Afterpay Breakout experiment
|
||||||
return splitCopyOnCMSPlaceHolder(
|
var afterpayBannerCopy = this.getCmsContent(this.cmsWidgetName, "BodyText2");
|
||||||
this.getCmsContent(this.cmsWidgetName, "BodyText2")
|
afterpayBannerCopy =
|
||||||
|
afterpayBannerCopy &&
|
||||||
|
afterpayBannerCopy.replaceAll(
|
||||||
|
"{custom:paymentType}",
|
||||||
|
this.isInsuranceSelected ? "deductible" : "spending"
|
||||||
);
|
);
|
||||||
|
return splitCopyOnCMSPlaceHolder(afterpayBannerCopy);
|
||||||
} else {
|
} else {
|
||||||
return splitCopyOnCMSPlaceHolder(
|
return splitCopyOnCMSPlaceHolder(
|
||||||
this.getCmsContent(this.cmsWidgetName, "HeaderText")
|
this.getCmsContent(this.cmsWidgetName, "HeaderText")
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,30 @@ import baseMixin from "@/mixins/base-mixin.js";
|
||||||
import { containsLineItemWithPartType } from "../../helpers/service-package-helper";
|
import { containsLineItemWithPartType } from "../../helpers/service-package-helper";
|
||||||
|
|
||||||
jest.mock("@/store", () => ({
|
jest.mock("@/store", () => ({
|
||||||
commit: jest.fn(),
|
commit: jest.fn(), // Mock store.commit
|
||||||
dispatch: jest.fn(),
|
dispatch: jest.fn(),
|
||||||
|
getters: {
|
||||||
|
order: {
|
||||||
|
lineItems: {
|
||||||
|
glassParts: [],
|
||||||
|
supportingItems: [],
|
||||||
|
vaps: [],
|
||||||
|
promos: [],
|
||||||
|
},
|
||||||
|
payment: {
|
||||||
|
insuranceCoverage: {},
|
||||||
|
isInsurance: false,
|
||||||
|
parentAccountNumber: 167132,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
payment: {
|
||||||
|
insuranceCoverage: {},
|
||||||
|
isInsurance: false,
|
||||||
|
},
|
||||||
|
customer: {
|
||||||
|
emailAddress: "test@test.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Mock fetchCmsContentForPage
|
// Mock fetchCmsContentForPage
|
||||||
|
|
@ -157,6 +179,7 @@ afterEach(() => {
|
||||||
describe("quote.vue", () => {
|
describe("quote.vue", () => {
|
||||||
test("IsInsurance false should navigateWithSaving", async () => {
|
test("IsInsurance false should navigateWithSaving", async () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
|
store.commit = jest.fn(); // Ensure store.commit is mocked
|
||||||
store.getters.payment = {
|
store.getters.payment = {
|
||||||
insuranceCoverage: {},
|
insuranceCoverage: {},
|
||||||
isInsurance: false,
|
isInsurance: false,
|
||||||
|
|
@ -785,6 +808,7 @@ describe("quote.vue", () => {
|
||||||
});
|
});
|
||||||
test("On forward button action save promos", async () => {
|
test("On forward button action save promos", async () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
|
store.commit = jest.fn(); // Ensure store.commit is mocked
|
||||||
store.getters.payment = {
|
store.getters.payment = {
|
||||||
insuranceCoverage: {},
|
insuranceCoverage: {},
|
||||||
isInsurance: false,
|
isInsurance: false,
|
||||||
|
|
@ -810,6 +834,7 @@ describe("quote.vue", () => {
|
||||||
});
|
});
|
||||||
test("On forward button action save supporting lineItems", async () => {
|
test("On forward button action save supporting lineItems", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
|
store.commit = jest.fn(); // Ensure store.commit is mocked
|
||||||
store.getters.payment = {
|
store.getters.payment = {
|
||||||
insuranceCoverage: {},
|
insuranceCoverage: {},
|
||||||
isInsurance: false,
|
isInsurance: false,
|
||||||
|
|
@ -820,6 +845,7 @@ describe("quote.vue", () => {
|
||||||
parentAccountNumber: 167132,
|
parentAccountNumber: 167132,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const { wrapper } = setupMocks({
|
const { wrapper } = setupMocks({
|
||||||
customMountOptions: {
|
customMountOptions: {
|
||||||
router: {
|
router: {
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@
|
||||||
v-if="showAfterpayBanner"
|
v-if="showAfterpayBanner"
|
||||||
cmsWidgetName="AfterpayModalWidget"
|
cmsWidgetName="AfterpayModalWidget"
|
||||||
:isAfterpayBreakoutDisplay="isAfterpayBreakoutDisplay"
|
:isAfterpayBreakoutDisplay="isAfterpayBreakoutDisplay"
|
||||||
|
:isInsuranceSelected="isInsuranceSelected"
|
||||||
:lineItems="lineItems" />
|
:lineItems="lineItems" />
|
||||||
|
|
||||||
<!-- If vehicle requires recal AND we are hiding recal pricing info, then show recal disclaimer banner. -->
|
<!-- If vehicle requires recal AND we are hiding recal pricing info, then show recal disclaimer banner. -->
|
||||||
|
|
@ -168,6 +169,7 @@ import {
|
||||||
} from "@/helpers/heritage-integration/cookie-helper";
|
} from "@/helpers/heritage-integration/cookie-helper";
|
||||||
import { showFmgLoadingModal } from "@/helpers/loading-modal-helper";
|
import { showFmgLoadingModal } from "@/helpers/loading-modal-helper";
|
||||||
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||||
|
import { storeMutations } from "@/constants/store-mutations";
|
||||||
|
|
||||||
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
|
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
|
||||||
|
|
||||||
|
|
@ -588,7 +590,7 @@ export default {
|
||||||
},
|
},
|
||||||
showAfterpayBanner() {
|
showAfterpayBanner() {
|
||||||
return (
|
return (
|
||||||
!this.isInsuranceSelected &&
|
(this.isAfterpayBreakoutDisplay || !this.isInsuranceSelected) &&
|
||||||
(!this.isRecalibrationOnOrder || !this.shouldHideRecalibration)
|
(!this.isRecalibrationOnOrder || !this.shouldHideRecalibration)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -766,6 +768,22 @@ export default {
|
||||||
this.$route
|
this.$route
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const lineItems = store.getters.order?.lineItems ?? {};
|
||||||
|
const combinedLineItems = [
|
||||||
|
...(lineItems.glassParts || []),
|
||||||
|
...(lineItems.supportingItems || []),
|
||||||
|
...(lineItems.vaps || []),
|
||||||
|
...(lineItems.promos || []),
|
||||||
|
];
|
||||||
|
|
||||||
|
const cashPriceSubtotal = combinedLineItems.length
|
||||||
|
? baseMixin.methods
|
||||||
|
.getTotalPriceOfAllLineItemsAndChildParts(combinedLineItems, false)
|
||||||
|
.toFixed(2)
|
||||||
|
: "";
|
||||||
|
|
||||||
|
store.commit(storeMutations.UPDATE_CASH_PRICE_SUBTOTAL, cashPriceSubtotal);
|
||||||
},
|
},
|
||||||
attachCustomEventsForAnalytics() {
|
attachCustomEventsForAnalytics() {
|
||||||
this.prependActionToMethod(this, this.forwardButtonAction, () => {
|
this.prependActionToMethod(this, this.forwardButtonAction, () => {
|
||||||
|
|
@ -789,8 +807,10 @@ export default {
|
||||||
});
|
});
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
const availablePackageNames = this.$refs.servicePackage?.servicePackageAnswers;
|
const availablePackageNames = this.$refs.servicePackage?.servicePackageAnswers;
|
||||||
var eventLabel = "";
|
var priceLabel = "";
|
||||||
availablePackageNames?.forEach((tier) => {
|
|
||||||
|
if (availablePackageNames?.length > 1 || availablePackageNames?.length == 1) {
|
||||||
|
var tier = availablePackageNames[0];
|
||||||
if (tier) {
|
if (tier) {
|
||||||
let lineItemsToPrice = this.availableLineItems;
|
let lineItemsToPrice = this.availableLineItems;
|
||||||
if (this.isRecalibrationOnOrder && this.shouldHideRecalibration) {
|
if (this.isRecalibrationOnOrder && this.shouldHideRecalibration) {
|
||||||
|
|
@ -815,14 +835,11 @@ export default {
|
||||||
baseMixin.methods.filterOutFees(lineItemsToPrice)
|
baseMixin.methods.filterOutFees(lineItemsToPrice)
|
||||||
);
|
);
|
||||||
price += this.$refs.servicePackage.getVapsPrice(tier.value);
|
price += this.$refs.servicePackage.getVapsPrice(tier.value);
|
||||||
const formattedPrice = parseFloat(price).toFixed(2);
|
priceLabel = price.toFixed(2);
|
||||||
eventLabel += tier.value + "_" + formattedPrice;
|
|
||||||
}
|
}
|
||||||
eventLabel += ",";
|
}
|
||||||
});
|
const subTotalLabel = this.Variables?.CASH_SUBTOTAL;
|
||||||
eventLabel = eventLabel.slice(0, -1);
|
this.pushVariableToDataLayer?.({ [subTotalLabel]: priceLabel });
|
||||||
|
|
||||||
this.pushEventToGA("quote", this.GaActions.SERVICE_PACKAGE_PRICE, eventLabel, true);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async skip(insuranceSelection, packageSelection) {
|
async skip(insuranceSelection, packageSelection) {
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ import servicePackageRadioExperiment from "@/experiment-components/service-packa
|
||||||
import servicePackageRadioAfterpayExperiment from "@/experiment-components/service-package-radio-for-afterpay";
|
import servicePackageRadioAfterpayExperiment from "@/experiment-components/service-package-radio-for-afterpay";
|
||||||
import { partTypeStrings } from "@/constants/part-type-strings";
|
import { partTypeStrings } from "@/constants/part-type-strings";
|
||||||
import { packageNames } from "@/constants/package-names";
|
import { packageNames } from "@/constants/package-names";
|
||||||
|
import store from "@/store";
|
||||||
|
import { storeMutations } from "@/constants/store-mutations";
|
||||||
import {
|
import {
|
||||||
shouldFrontWipersBeAvailable,
|
shouldFrontWipersBeAvailable,
|
||||||
shouldRearWipersBeAvailable,
|
shouldRearWipersBeAvailable,
|
||||||
|
|
@ -161,6 +163,7 @@ export default {
|
||||||
isInsuranceSelected: this.isInsuranceSelected,
|
isInsuranceSelected: this.isInsuranceSelected,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.$emit("currentNumberOfPackages", modifiedAnswers.length);
|
this.$emit("currentNumberOfPackages", modifiedAnswers.length);
|
||||||
return modifiedAnswers;
|
return modifiedAnswers;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -375,6 +375,10 @@ export default {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.$refs.datePicker.showAnotherMonth().then((moreSelectableDatesData) => {
|
this.$refs.datePicker.showAnotherMonth().then((moreSelectableDatesData) => {
|
||||||
this.selectableDatesData = moreSelectableDatesData;
|
this.selectableDatesData = moreSelectableDatesData;
|
||||||
|
if (selectableDatesData?.days?.length > 0) {
|
||||||
|
this.selectedDate = selectableDatesData.days[0].date;
|
||||||
|
}
|
||||||
|
|
||||||
this.setDisplayWaitList();
|
this.setDisplayWaitList();
|
||||||
});
|
});
|
||||||
}, 50);
|
}, 50);
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@
|
||||||
v-model="selectedValue"
|
v-model="selectedValue"
|
||||||
:suppressError="suppressError"
|
:suppressError="suppressError"
|
||||||
:validationRules="validationRules"
|
:validationRules="validationRules"
|
||||||
isRequired />
|
isRequired
|
||||||
|
:labelBold="labelBold" />
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -34,6 +35,12 @@ export default {
|
||||||
isServiceableInshop: Boolean,
|
isServiceableInshop: Boolean,
|
||||||
isServiceableDropoff: Boolean,
|
isServiceableDropoff: Boolean,
|
||||||
mobileFeeApplies: Boolean,
|
mobileFeeApplies: Boolean,
|
||||||
|
labelBold: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
zipCode: String,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
questionText() {
|
questionText() {
|
||||||
|
|
@ -47,6 +54,7 @@ export default {
|
||||||
const shouldShowInshop = this.isServiceableInshop;
|
const shouldShowInshop = this.isServiceableInshop;
|
||||||
const shouldShowDropoff =
|
const shouldShowDropoff =
|
||||||
this.isServiceableDropoff && !this.$store.getters.damage.isRepair;
|
this.isServiceableDropoff && !this.$store.getters.damage.isRepair;
|
||||||
|
const zipCode = this.zipCode;
|
||||||
|
|
||||||
var answers = this.answersFromCms
|
var answers = this.answersFromCms
|
||||||
? this.answersFromCms.filter((answer) => {
|
? this.answersFromCms.filter((answer) => {
|
||||||
|
|
@ -87,12 +95,15 @@ export default {
|
||||||
watch: {
|
watch: {
|
||||||
answersToDisplay: {
|
answersToDisplay: {
|
||||||
handler(newValue) {
|
handler(newValue) {
|
||||||
// If there is only one option to display and that option is 'Mobile' then select it
|
// If there is only one option to display and that option is 'Mobile' or 'Inshop' then select it
|
||||||
|
if (newValue.length == 1) {
|
||||||
|
const name = newValue[0].Name;
|
||||||
if (
|
if (
|
||||||
newValue.length == 1 &&
|
name === AppointmentTypeStrings.MOBILE ||
|
||||||
newValue.findIndex((answer) => answer.Name == "Mobile") != -1
|
name === AppointmentTypeStrings.IN_SHOP
|
||||||
) {
|
) {
|
||||||
this.selectedValue = "Mobile";
|
this.selectedValue = name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
immediate: true,
|
immediate: true,
|
||||||
|
|
@ -100,14 +111,14 @@ export default {
|
||||||
isMobileOnly: {
|
isMobileOnly: {
|
||||||
handler(newValue) {
|
handler(newValue) {
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
this.selectedValue = "Mobile";
|
this.selectedValue = AppointmentTypeStrings.MOBILE;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
isInshopOnly: {
|
isInshopOnly: {
|
||||||
handler(newValue) {
|
handler(newValue) {
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
this.selectedValue = "Inshop";
|
this.selectedValue = AppointmentTypeStrings.IN_SHOP;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ export async function getPricedMobileFeePart(serviceZipCode, pageNameToLog) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const zipCodeData = await getZipCodeData(serviceZipCode);
|
const zipCodeData = await getZipCodeData(serviceZipCode);
|
||||||
|
const providerData = await getShopProviderData(serviceZipCode);
|
||||||
|
|
||||||
// Get the Mobile Fee Part
|
// Get the Mobile Fee Part
|
||||||
const mobileFeePart = await baseMixin.methods.dispatchStoreActionWithLogging(
|
const mobileFeePart = await baseMixin.methods.dispatchStoreActionWithLogging(
|
||||||
|
|
@ -15,6 +16,7 @@ export async function getPricedMobileFeePart(serviceZipCode, pageNameToLog) {
|
||||||
{
|
{
|
||||||
serviceZipCode: serviceZipCode,
|
serviceZipCode: serviceZipCode,
|
||||||
serviceZipCodeCtu: zipCodeData.zipCodeCtu,
|
serviceZipCodeCtu: zipCodeData.zipCodeCtu,
|
||||||
|
mobileProviderNumber: providerData.data.mobileProviderNumber,
|
||||||
},
|
},
|
||||||
pageNameToLog,
|
pageNameToLog,
|
||||||
false
|
false
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ const mockStoreActionPriceOrderItemsAndSaveServerData =
|
||||||
storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA;
|
storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA;
|
||||||
const mockStoreActionGetServiceabilityDetails = storeActions.GET_SERVICEABILITY_DETAILS;
|
const mockStoreActionGetServiceabilityDetails = storeActions.GET_SERVICEABILITY_DETAILS;
|
||||||
const mockStoreActionGetShopTimeSlots = storeActions.GET_SHOP_TIME_SLOTS;
|
const mockStoreActionGetShopTimeSlots = storeActions.GET_SHOP_TIME_SLOTS;
|
||||||
|
const mockStoreActionGetProviders = storeActions.GET_PROVIDERS;
|
||||||
|
|
||||||
const mockGetShopTimeSlotsGoodAvailability = {
|
const mockGetShopTimeSlotsGoodAvailability = {
|
||||||
data: {
|
data: {
|
||||||
|
|
@ -236,6 +237,15 @@ jest.mock("@/mixins/base-mixin.js", () => ({
|
||||||
|
|
||||||
return Promise.resolve(mockGetShopTimeSlotsLowAvailability);
|
return Promise.resolve(mockGetShopTimeSlotsLowAvailability);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (actionName === mockStoreActionGetProviders) {
|
||||||
|
return Promise.resolve({
|
||||||
|
data: {
|
||||||
|
mobileProviderNumber: "001820",
|
||||||
|
shopProviders: [{}],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -189,41 +189,7 @@ describe("mobile-location-modal-questions.vue", () => {
|
||||||
expect(wrapper.vm.internalModel.serviceZipCode).toEqual("55555");
|
expect(wrapper.vm.internalModel.serviceZipCode).toEqual("55555");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should open the modal when the 'Enter your service address' link is clicked", async () => {
|
it("Should emit setMobileLocation on setMobileLocation for a valid address and vehicle protection answer", async () => {
|
||||||
// Arrange
|
|
||||||
const mobileLocationQuestions = {
|
|
||||||
addressQuestions: {
|
|
||||||
streetAddress: "555 Some St",
|
|
||||||
apartmentNumberOrBusinessName: "Apt 1",
|
|
||||||
city: "Funkytown",
|
|
||||||
state: "OH",
|
|
||||||
zipCode: "55555",
|
|
||||||
},
|
|
||||||
isVehicleProtected: true,
|
|
||||||
serviceZipCode: "55555",
|
|
||||||
};
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({
|
|
||||||
mixins: [mockMixin],
|
|
||||||
props: {
|
|
||||||
modelValue: mobileLocationQuestions,
|
|
||||||
},
|
|
||||||
mountOptions: {
|
|
||||||
attachTo: document.body,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
wrapper.vm.$refs.MobileLocationModalWidget.openModal = jest.fn();
|
|
||||||
const mobileLocationLink = wrapper.findComponent({ ref: "mobileLocationLink" });
|
|
||||||
|
|
||||||
// // Act
|
|
||||||
await mobileLocationLink.trigger("click-event");
|
|
||||||
|
|
||||||
// // Assert
|
|
||||||
expect(wrapper.vm.$refs.MobileLocationModalWidget.openModal).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Should emit setMobileLocationAndProceed on setMobileLocation for a valid address and vehicle protection answer", async () => {
|
|
||||||
// Arrange
|
// Arrange
|
||||||
const mobileLocationQuestions = {
|
const mobileLocationQuestions = {
|
||||||
addressQuestions: {
|
addressQuestions: {
|
||||||
|
|
@ -258,7 +224,6 @@ describe("mobile-location-modal-questions.vue", () => {
|
||||||
},
|
},
|
||||||
attachTo: document.body,
|
attachTo: document.body,
|
||||||
});
|
});
|
||||||
wrapper.vm.$refs.MobileLocationModalWidget.closeModal = jest.fn();
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
wrapper.vm.internalModel = newMobileLocationQuestions;
|
wrapper.vm.internalModel = newMobileLocationQuestions;
|
||||||
|
|
@ -266,13 +231,13 @@ describe("mobile-location-modal-questions.vue", () => {
|
||||||
await wrapper.vm.setMobileLocation();
|
await wrapper.vm.setMobileLocation();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.emitted()["setMobileLocationAndProceed"]).toBeTruthy();
|
expect(wrapper.emitted()["setMobileLocation"]).toBeTruthy();
|
||||||
expect(wrapper.emitted()["setMobileLocationAndProceed"][0][0]).toStrictEqual(
|
expect(wrapper.emitted()["setMobileLocation"][0][0]).toStrictEqual(
|
||||||
newMobileLocationQuestions
|
newMobileLocationQuestions
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should not emit setMobileLocationAndProceed on setMobileLocation for an invalid address zip code", async () => {
|
it("Should not emit setMobileLocation on setMobileLocation for an invalid address zip code", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const mobileLocationQuestions = {
|
const mobileLocationQuestions = {
|
||||||
addressQuestions: {
|
addressQuestions: {
|
||||||
|
|
@ -307,15 +272,13 @@ describe("mobile-location-modal-questions.vue", () => {
|
||||||
},
|
},
|
||||||
attachTo: document.body,
|
attachTo: document.body,
|
||||||
});
|
});
|
||||||
wrapper.vm.$refs.MobileLocationModalWidget.closeModal = jest.fn();
|
|
||||||
wrapper.vm.resetModalButtonStyle = jest.fn();
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
wrapper.vm.internalModel = newMobileLocationQuestions;
|
wrapper.vm.internalModel = newMobileLocationQuestions;
|
||||||
await wrapper.vm.setMobileLocation();
|
await wrapper.vm.setMobileLocation();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.emitted("setMobileLocationAndProceed")).not.toBeTruthy();
|
expect(wrapper.emitted("setMobileLocation")).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 () => {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
|
<transition name="fade" mode="out-in">
|
||||||
<div class="mobile-location-questions">
|
<div class="mobile-location-questions">
|
||||||
<div class="text-center" :id="componentId">
|
<div class="text-center" :id="componentId">
|
||||||
<label
|
<label
|
||||||
|
|
@ -13,7 +14,7 @@
|
||||||
linkType="text"
|
linkType="text"
|
||||||
:text="mobileLocationLinkText"
|
:text="mobileLocationLinkText"
|
||||||
href="#!"
|
href="#!"
|
||||||
@click-event="openModal" />
|
@click-event="toggleMobileLocation" />
|
||||||
</div>
|
</div>
|
||||||
<div v-show="errorMessage" class="row my-1 form-test-error">
|
<div v-show="errorMessage" class="row my-1 form-test-error">
|
||||||
<span
|
<span
|
||||||
|
|
@ -29,24 +30,19 @@
|
||||||
cmsWidgetName="MobileFeeDisclaimerWidget"
|
cmsWidgetName="MobileFeeDisclaimerWidget"
|
||||||
typeStyle="caption" />
|
typeStyle="caption" />
|
||||||
</div>
|
</div>
|
||||||
<modal
|
<div v-if="isMobileLocationOpened" class="address-questions-container">
|
||||||
:ref="modalName"
|
<div v-html="headerText" class="HeaderText" />
|
||||||
:headerText="modalHeaderText"
|
|
||||||
:footerButtonText="modalFooterText"
|
|
||||||
:onModalOpenedCallback="onModalOpened"
|
|
||||||
:onModalClosedCallback="onModalClosed"
|
|
||||||
@isModalOpened="setModalStatus"
|
|
||||||
@footer-button-event="setMobileLocation">
|
|
||||||
<template v-if="isModalOpened">
|
|
||||||
<addressQuestions
|
<addressQuestions
|
||||||
ref="addressQuestions"
|
ref="addressQuestions"
|
||||||
v-model="internalModel.addressQuestions"
|
v-model="internalModel.addressQuestions"
|
||||||
captureApartmentNumberOrBusinessName="true"
|
captureApartmentNumberOrBusinessName="true"
|
||||||
preserveCityAndStateOnReset="true" />
|
preserveCityAndStateOnReset="true"
|
||||||
|
labelBold="true" />
|
||||||
<vehicleProtectedQuestion
|
<vehicleProtectedQuestion
|
||||||
ref="vehicleProtectedQuestion"
|
ref="vehicleProtectedQuestion"
|
||||||
v-model="internalModel.isVehicleProtected"
|
v-model="internalModel.isVehicleProtected"
|
||||||
cmsWidgetName="VehicleProtectedQuestionWidget" />
|
cmsWidgetName="VehicleProtectedQuestionWidget"
|
||||||
|
labelBold="true" />
|
||||||
<textBlock cmsWidgetName="WorkspaceRequirementsWidget" typeStyle="caption" />
|
<textBlock cmsWidgetName="WorkspaceRequirementsWidget" typeStyle="caption" />
|
||||||
<alert
|
<alert
|
||||||
ref="alertInvalidZip"
|
ref="alertInvalidZip"
|
||||||
|
|
@ -62,16 +58,14 @@
|
||||||
cmsWidgetName="AlertMismatchStateAndZipWidget"
|
cmsWidgetName="AlertMismatchStateAndZipWidget"
|
||||||
alertClass="alert-danger"
|
alertClass="alert-danger"
|
||||||
v-bind:isDismissible="false" />
|
v-bind:isDismissible="false" />
|
||||||
</template>
|
|
||||||
</modal>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Components
|
// Components
|
||||||
import textLink from "@/ux-components/text-link/text-link";
|
import textLink from "@/ux-components/text-link/text-link";
|
||||||
import textBlock from "@/digital-components/text-block/text-block";
|
import textBlock from "@/digital-components/text-block/text-block";
|
||||||
import modal from "@/digital-components/modal/modal";
|
|
||||||
import alert from "@/ux-components/alert/alert";
|
import alert from "@/ux-components/alert/alert";
|
||||||
import addressQuestions from "@/fmg-components/address-questions/address-questions";
|
import addressQuestions from "@/fmg-components/address-questions/address-questions";
|
||||||
import vehicleProtectedQuestion from "@/layouts/service-location/mobile-location-modal-questions/vehicle-protected-question/vehicle-protected-question";
|
import vehicleProtectedQuestion from "@/layouts/service-location/mobile-location-modal-questions/vehicle-protected-question/vehicle-protected-question";
|
||||||
|
|
@ -96,13 +90,14 @@ 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",
|
||||||
"setMobileLocationAndProceed",
|
"setMobileLocation",
|
||||||
|
"set-mobile-location-invalid",
|
||||||
],
|
],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
internalModel: deepClone(this.modelValue),
|
internalModel: deepClone(this.modelValue),
|
||||||
displayInvalidZipAlert: false,
|
displayInvalidZipAlert: false,
|
||||||
isModalOpened: false,
|
isMobileLocationOpened: false,
|
||||||
displayMismatchStateAndZipAlert: false,
|
displayMismatchStateAndZipAlert: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
@ -188,23 +183,29 @@ export default {
|
||||||
this.mobileFeePart.kitPrice
|
this.mobileFeePart.kitPrice
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
modalName() {
|
headerText() {
|
||||||
return "MobileLocationModalWidget";
|
|
||||||
},
|
|
||||||
modalHeaderText() {
|
|
||||||
return this.getCmsContent(this.modalWidgetName, "HeaderText");
|
return this.getCmsContent(this.modalWidgetName, "HeaderText");
|
||||||
},
|
},
|
||||||
modalFooterText() {
|
|
||||||
return this.getCmsContent(this.modalWidgetName, "FooterText");
|
|
||||||
},
|
|
||||||
modal() {
|
|
||||||
return this.$refs[this.modalName];
|
|
||||||
},
|
|
||||||
addressModel: {
|
addressModel: {
|
||||||
get: function () {
|
get: function () {
|
||||||
return this.modelValue.addressQuestions;
|
return this.modelValue.addressQuestions;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
isMobileAddressAvailable: {
|
||||||
|
get: function () {
|
||||||
|
return (
|
||||||
|
this.addressModel.streetAddress &&
|
||||||
|
this.addressModel.streetAddress !== "" &&
|
||||||
|
this.addressModel.city &&
|
||||||
|
this.addressModel.city !== "" &&
|
||||||
|
this.addressModel.state &&
|
||||||
|
this.addressModel.state !== "" &&
|
||||||
|
this.addressModel.zipCode &&
|
||||||
|
this.addressModel.zipCode !== "" &&
|
||||||
|
this.internalModel.isVehicleProtected !== null
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
isMobileAddressComplete() {
|
isMobileAddressComplete() {
|
||||||
|
|
@ -220,24 +221,14 @@ export default {
|
||||||
this.internalModel.isVehicleProtected !== null
|
this.internalModel.isVehicleProtected !== null
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
openModal() {
|
getIsMobleLocationOpened() {
|
||||||
this.modal.openModal();
|
if (this.isServiceAddressFromStoreAvailable) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
onModalOpened() {
|
setMobileLocationInvalid(isFormInvalid) {
|
||||||
this.internalModel = deepClone(this.modelValue);
|
this.$emit("set-mobile-location-invalid", isFormInvalid);
|
||||||
},
|
|
||||||
setModalStatus(isOpened) {
|
|
||||||
this.isModalOpened = isOpened;
|
|
||||||
},
|
|
||||||
closeModal() {
|
|
||||||
this.modal.closeModal();
|
|
||||||
},
|
|
||||||
onModalClosed() {
|
|
||||||
this.resetAlerts();
|
|
||||||
this.internalModel = deepClone(this.modelValue);
|
|
||||||
},
|
|
||||||
resetModalButtonStyle() {
|
|
||||||
this.modal.resetButtonStyle();
|
|
||||||
},
|
},
|
||||||
resetAlerts() {
|
resetAlerts() {
|
||||||
this.displayInvalidZipAlert = false;
|
this.displayInvalidZipAlert = false;
|
||||||
|
|
@ -246,6 +237,10 @@ export default {
|
||||||
async setMobileLocation() {
|
async setMobileLocation() {
|
||||||
this.resetAlerts();
|
this.resetAlerts();
|
||||||
// Validate the Zip Code
|
// Validate the Zip Code
|
||||||
|
if (this.internalModel.addressQuestions.zipCode === "") {
|
||||||
|
this.setMobileLocationInvalid(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const zipCodeData = await this.getZipCodeData(
|
const zipCodeData = await this.getZipCodeData(
|
||||||
this.internalModel.addressQuestions.zipCode,
|
this.internalModel.addressQuestions.zipCode,
|
||||||
"service-location"
|
"service-location"
|
||||||
|
|
@ -253,11 +248,11 @@ export default {
|
||||||
|
|
||||||
if (!zipCodeData.isValid) {
|
if (!zipCodeData.isValid) {
|
||||||
this.displayInvalidZipAlert = true;
|
this.displayInvalidZipAlert = true;
|
||||||
this.resetModalButtonStyle();
|
this.setMobileLocationInvalid(true);
|
||||||
return;
|
return;
|
||||||
} else if (zipCodeData.state != this.internalModel.addressQuestions.state) {
|
} else if (zipCodeData.state != this.internalModel.addressQuestions.state) {
|
||||||
this.displayMismatchStateAndZipAlert = true;
|
this.displayMismatchStateAndZipAlert = true;
|
||||||
this.resetModalButtonStyle();
|
this.setMobileLocationInvalid(true);
|
||||||
return;
|
return;
|
||||||
} else if (
|
} else if (
|
||||||
this.internalModel.addressQuestions.zipCode !==
|
this.internalModel.addressQuestions.zipCode !==
|
||||||
|
|
@ -296,10 +291,21 @@ 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
|
||||||
this.$emit("setMobileLocationAndProceed", this.internalModel);
|
this.$emit("setMobileLocation", this.internalModel);
|
||||||
|
this.setMobileLocationInvalid(false);
|
||||||
|
},
|
||||||
|
toggleMobileLocation() {
|
||||||
|
this.isMobileLocationOpened = !this.isMobileLocationOpened;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
internalModel: {
|
||||||
|
handler() {
|
||||||
|
this.setMobileLocation();
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
|
||||||
modelValue: {
|
modelValue: {
|
||||||
handler(newValue) {
|
handler(newValue) {
|
||||||
this.internalModel = deepClone(newValue);
|
this.internalModel = deepClone(newValue);
|
||||||
|
|
@ -308,15 +314,11 @@ export default {
|
||||||
deep: true,
|
deep: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
unmounted() {
|
|
||||||
if (this.isModalOpened) this.closeModal();
|
|
||||||
},
|
|
||||||
components: {
|
components: {
|
||||||
textLink,
|
|
||||||
modal,
|
|
||||||
addressQuestions,
|
addressQuestions,
|
||||||
vehicleProtectedQuestion,
|
vehicleProtectedQuestion,
|
||||||
textBlock,
|
textBlock,
|
||||||
|
textLink,
|
||||||
alert,
|
alert,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -327,7 +329,6 @@ export default {
|
||||||
.center-error-message {
|
.center-error-message {
|
||||||
justify-content: center !important;
|
justify-content: center !important;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.update-zip-text-link::before {
|
.update-zip-text-link::before {
|
||||||
content: "";
|
content: "";
|
||||||
|
|
@ -340,15 +341,22 @@ export default {
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.address-questions {
|
|
||||||
margin-top: 0em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.update-mobile-location-text-link {
|
.update-mobile-location-text-link {
|
||||||
white-space: pre-line;
|
white-space: pre-line;
|
||||||
}
|
}
|
||||||
|
.address-questions-container {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.text-black {
|
.text-black {
|
||||||
color: $black;
|
color: $black;
|
||||||
}
|
}
|
||||||
|
.HeaderText {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.625rem;
|
||||||
|
color: $black;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@
|
||||||
textPosition="text-center"
|
textPosition="text-center"
|
||||||
v-model="selectedValue"
|
v-model="selectedValue"
|
||||||
validationRules="option-required"
|
validationRules="option-required"
|
||||||
isRequired />
|
isRequired
|
||||||
|
:labelBold="labelBold" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
@ -27,6 +28,11 @@ export default {
|
||||||
isVehicleProtected: Boolean,
|
isVehicleProtected: Boolean,
|
||||||
},
|
},
|
||||||
cmsWidgetName: String,
|
cmsWidgetName: String,
|
||||||
|
labelBold: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
buttonQuestion,
|
buttonQuestion,
|
||||||
|
|
|
||||||
|
|
@ -343,7 +343,6 @@ describe("service-location.vue", () => {
|
||||||
zipCode: "43054",
|
zipCode: "43054",
|
||||||
},
|
},
|
||||||
isVehicleProtected: true,
|
isVehicleProtected: true,
|
||||||
isMobileSelected: true,
|
|
||||||
};
|
};
|
||||||
wrapper.vm.mobileLocationQuestions = mobileLocationQuestions;
|
wrapper.vm.mobileLocationQuestions = mobileLocationQuestions;
|
||||||
|
|
||||||
|
|
@ -356,7 +355,6 @@ describe("service-location.vue", () => {
|
||||||
zipCode: "61606",
|
zipCode: "61606",
|
||||||
},
|
},
|
||||||
isVehicleProtected: null,
|
isVehicleProtected: null,
|
||||||
isMobileSelected: false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -459,7 +457,6 @@ describe("service-location.vue", () => {
|
||||||
zipCode: "",
|
zipCode: "",
|
||||||
},
|
},
|
||||||
isVehicleProtected: null,
|
isVehicleProtected: null,
|
||||||
isMobileSelected: false,
|
|
||||||
};
|
};
|
||||||
wrapper.vm.mobileLocationQuestions = mobileLocationQuestions;
|
wrapper.vm.mobileLocationQuestions = mobileLocationQuestions;
|
||||||
const newMobileLocationQuestions = {
|
const newMobileLocationQuestions = {
|
||||||
|
|
@ -471,15 +468,14 @@ describe("service-location.vue", () => {
|
||||||
zipCode: "43054",
|
zipCode: "43054",
|
||||||
},
|
},
|
||||||
isVehicleProtected: true,
|
isVehicleProtected: true,
|
||||||
isMobileSelected: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
wrapper.vm.closeModalAction = jest.fn();
|
//wrapper.vm.closeModalAction = jest.fn();
|
||||||
// Act
|
// Act
|
||||||
|
|
||||||
// Trigger the event
|
// Trigger the event
|
||||||
await mobileLocationQuestionsComponent.vm.$emit(
|
await mobileLocationQuestionsComponent.vm.$emit(
|
||||||
"setMobileLocationAndProceed",
|
"setMobileLocation",
|
||||||
newMobileLocationQuestions
|
newMobileLocationQuestions
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -521,7 +517,7 @@ describe("service-location.vue", () => {
|
||||||
zipCode: "",
|
zipCode: "",
|
||||||
},
|
},
|
||||||
isVehicleProtected: null,
|
isVehicleProtected: null,
|
||||||
isMobileSelected: false,
|
isMobileSelectedAndModalClosed: false,
|
||||||
mobileFeePart: null,
|
mobileFeePart: null,
|
||||||
};
|
};
|
||||||
wrapper.vm.mobileLocationQuestions = mobileLocationQuestions;
|
wrapper.vm.mobileLocationQuestions = mobileLocationQuestions;
|
||||||
|
|
@ -535,7 +531,7 @@ describe("service-location.vue", () => {
|
||||||
zipCode: "43081",
|
zipCode: "43081",
|
||||||
},
|
},
|
||||||
isVehicleProtected: true,
|
isVehicleProtected: true,
|
||||||
isMobileSelected: true,
|
isMobileSelectedAndModalClosed: true,
|
||||||
mobileFeePart: null,
|
mobileFeePart: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -556,7 +552,7 @@ describe("service-location.vue", () => {
|
||||||
|
|
||||||
// Trigger the event
|
// Trigger the event
|
||||||
await mobileLocationQuestionsComponent.vm.$emit(
|
await mobileLocationQuestionsComponent.vm.$emit(
|
||||||
"setMobileLocationAndProceed",
|
"setMobileLocation",
|
||||||
newMobileLocationQuestions
|
newMobileLocationQuestions
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -599,15 +595,12 @@ describe("service-location.vue", () => {
|
||||||
zipCode: "43081",
|
zipCode: "43081",
|
||||||
},
|
},
|
||||||
isVehicleProtected: "YesAnswer",
|
isVehicleProtected: "YesAnswer",
|
||||||
isMobileSelected: true,
|
isMobileSelectedAndModalClosed: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
wrapper.vm.closeModalAction = jest.fn();
|
wrapper.vm.closeModalAction = jest.fn();
|
||||||
// Act
|
// Act
|
||||||
mobileLocationQuestionsComponent.vm.$emit(
|
mobileLocationQuestionsComponent.vm.$emit("setMobileLocation", mobileLocationQuestions);
|
||||||
"setMobileLocationAndProceed",
|
|
||||||
mobileLocationQuestions
|
|
||||||
);
|
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.vm.selectedAppointmentType).toStrictEqual("Mobile");
|
expect(wrapper.vm.selectedAppointmentType).toStrictEqual("Mobile");
|
||||||
|
|
|
||||||
|
|
@ -79,10 +79,12 @@
|
||||||
:isServiceableDropoff="isServiceableDropoff"
|
:isServiceableDropoff="isServiceableDropoff"
|
||||||
:isDisplayed="isAppointmentTypeDisplayed"
|
:isDisplayed="isAppointmentTypeDisplayed"
|
||||||
:mobileFeeApplies="mobileFeeApplies"
|
:mobileFeeApplies="mobileFeeApplies"
|
||||||
|
:zipCode="zipCode"
|
||||||
ref="appointmentTypeQuestion"
|
ref="appointmentTypeQuestion"
|
||||||
groupName="appointmentTypeQuestion"
|
groupName="appointmentTypeQuestion"
|
||||||
cmsWidgetName="AppointmentTypeQuestionWidget"
|
cmsWidgetName="AppointmentTypeQuestionWidget"
|
||||||
validationRules="option-required" />
|
validationRules="option-required"
|
||||||
|
labelBold="true" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
|
|
@ -102,7 +104,8 @@
|
||||||
ref="mobileLocationQuestions"
|
ref="mobileLocationQuestions"
|
||||||
linkWidgetName="MobileLocationLinkWidget"
|
linkWidgetName="MobileLocationLinkWidget"
|
||||||
modalWidgetName="MobileLocationModalWidget"
|
modalWidgetName="MobileLocationModalWidget"
|
||||||
@setMobileLocationAndProceed="setMobileLocationQuestions" />
|
@setMobileLocation="setMobileLocationQuestions"
|
||||||
|
@set-mobile-location-invalid="setMobileLocationInValid" />
|
||||||
|
|
||||||
<shopQuestion
|
<shopQuestion
|
||||||
ref="shopQuestion"
|
ref="shopQuestion"
|
||||||
|
|
@ -111,15 +114,14 @@
|
||||||
:selectedAppointmentType="selectedAppointmentType"
|
:selectedAppointmentType="selectedAppointmentType"
|
||||||
:shopProviderData="shopProviderData"
|
:shopProviderData="shopProviderData"
|
||||||
:isDisplayed="isShopQuestionDisplayed"
|
:isDisplayed="isShopQuestionDisplayed"
|
||||||
cmsWidgetName="ShopQuestionWidget"
|
cmsWidgetName="ShopQuestionWidget" />
|
||||||
:isInshopOnly="isInshopOnly" />
|
|
||||||
|
|
||||||
<contentGroupModal ref="RecalModal" cmsWidgetName="RecalModal" />
|
<contentGroupModal ref="RecalModal" cmsWidgetName="RecalModal" />
|
||||||
|
|
||||||
<navbar
|
<navbar
|
||||||
cmsWidgetName="FunnelFooterWidget"
|
cmsWidgetName="FunnelFooterWidget"
|
||||||
ref="navbar"
|
ref="navbar"
|
||||||
:isForwardActionDisabled="!meta.valid || displayNoShopsAlert"
|
:isForwardActionDisabled="!meta.valid || isForwardActionDisabled"
|
||||||
@back-clicked="backButtonAction"
|
@back-clicked="backButtonAction"
|
||||||
@ForwardClicked="forwardButtonAction" />
|
@ForwardClicked="forwardButtonAction" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -173,12 +175,12 @@ const MOBILE_FEE_PART_TYPE = "MOBILE FEE";
|
||||||
// DEFINE VALIDATION RULES
|
// DEFINE VALIDATION RULES
|
||||||
defineRule("mobile-location-required", (value) => {
|
defineRule("mobile-location-required", (value) => {
|
||||||
if (
|
if (
|
||||||
value.isMobileSelected &&
|
|
||||||
(!value.addressQuestions.streetAddress ||
|
(!value.addressQuestions.streetAddress ||
|
||||||
!value.addressQuestions.city ||
|
!value.addressQuestions.city ||
|
||||||
!value.addressQuestions.state ||
|
!value.addressQuestions.state ||
|
||||||
!value.addressQuestions.zipCode ||
|
!value.addressQuestions.zipCode ||
|
||||||
!value.isVehicleProtected)
|
!value.isVehicleProtected) &&
|
||||||
|
value.isMobileSelected
|
||||||
) {
|
) {
|
||||||
return errorMessages.MOBILE_LOCATION_REQUIRED;
|
return errorMessages.MOBILE_LOCATION_REQUIRED;
|
||||||
}
|
}
|
||||||
|
|
@ -212,6 +214,7 @@ export default {
|
||||||
billToAccountNumber: null,
|
billToAccountNumber: null,
|
||||||
shopProviderData: null,
|
shopProviderData: null,
|
||||||
navigatingForward: false,
|
navigatingForward: false,
|
||||||
|
isMobileAddressValid: true,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async beforeRouteEnter(to, from, next) {
|
async beforeRouteEnter(to, from, next) {
|
||||||
|
|
@ -302,7 +305,6 @@ export default {
|
||||||
zipCode: this.zipCode,
|
zipCode: this.zipCode,
|
||||||
},
|
},
|
||||||
isVehicleProtected: this.isVehicleProtected,
|
isVehicleProtected: this.isVehicleProtected,
|
||||||
isMobileSelected: this.selectedAppointmentType === "Mobile",
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -416,10 +418,8 @@ export default {
|
||||||
isNoComp() {
|
isNoComp() {
|
||||||
return store.getters.order.policy.isNoComp;
|
return store.getters.order.policy.isNoComp;
|
||||||
},
|
},
|
||||||
isInshopOnly() {
|
isForwardActionDisabled() {
|
||||||
return (
|
return this.displayNoShopsAlert || !this.isMobileAddressValid;
|
||||||
this.isServiceableInshop && !this.isServiceableMobile && !this.isServiceableDropoff
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -523,10 +523,7 @@ export default {
|
||||||
}
|
}
|
||||||
if (this.isServiceableMobile) {
|
if (this.isServiceableMobile) {
|
||||||
this.setMobileLocation(newValue);
|
this.setMobileLocation(newValue);
|
||||||
//proceed to next page
|
|
||||||
this.forwardButtonAction();
|
|
||||||
} else {
|
} else {
|
||||||
this.closeModalAction("mobileLocationQuestions");
|
|
||||||
await getZipCodeData(newZipCode).then((zipCodeData) => {
|
await getZipCodeData(newZipCode).then((zipCodeData) => {
|
||||||
this.serviceZipCodeQuestion = {
|
this.serviceZipCodeQuestion = {
|
||||||
state: zipCodeData.state,
|
state: zipCodeData.state,
|
||||||
|
|
@ -694,7 +691,7 @@ export default {
|
||||||
},
|
},
|
||||||
async forwardButtonAction() {
|
async forwardButtonAction() {
|
||||||
this.navigatingForward = true;
|
this.navigatingForward = true;
|
||||||
|
var ctu = this.zipCodeCtu;
|
||||||
if (
|
if (
|
||||||
this.selectedAppointmentType == AppointmentTypeStrings.MOBILE &&
|
this.selectedAppointmentType == AppointmentTypeStrings.MOBILE &&
|
||||||
this.selectedProvider &&
|
this.selectedProvider &&
|
||||||
|
|
@ -705,18 +702,13 @@ export default {
|
||||||
this.selectedProvider.address.state = null;
|
this.selectedProvider.address.state = null;
|
||||||
this.selectedProvider.address.zipCode = null;
|
this.selectedProvider.address.zipCode = null;
|
||||||
this.selectedProvider.address.zipCodeCtu = null;
|
this.selectedProvider.address.zipCodeCtu = null;
|
||||||
}
|
} else {
|
||||||
|
|
||||||
var ctu = this.zipCodeCtu;
|
|
||||||
if (this.selectedAppointmentType == AppointmentTypeStrings.IN_SHOP) {
|
|
||||||
// if we have a provider.zipCodeCtu that's different than the servicelocation.zipCodeCtu then they
|
// if we have a provider.zipCodeCtu that's different than the servicelocation.zipCodeCtu then they
|
||||||
// may have selected a shop in a different ctu. change the servicelocation zip/ctu if different
|
// may have selected a shop in a different ctu. change the servicelocation zip/ctu if different
|
||||||
if (this.zipCodeCtu != this.selectedProvider.address.zipCodeCtu) {
|
if (this.zipCodeCtu != this.selectedProvider.address.zipCodeCtu) {
|
||||||
ctu = this.selectedProvider.address.zipCodeCtu;
|
ctu = this.selectedProvider.address.zipCodeCtu;
|
||||||
}
|
}
|
||||||
|
this.resetMobileLocation();
|
||||||
this.city = null;
|
|
||||||
this.streetAddress = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.dispatchStoreAction(
|
await this.dispatchStoreAction(
|
||||||
|
|
@ -756,11 +748,15 @@ export default {
|
||||||
this.updateAndSaveIsMSRFeeApplicable();
|
this.updateAndSaveIsMSRFeeApplicable();
|
||||||
this.$router.navigateWithSaving(this.navigationScenarios.CLICKED_FORWARD, this.$route);
|
this.$router.navigateWithSaving(this.navigationScenarios.CLICKED_FORWARD, this.$route);
|
||||||
},
|
},
|
||||||
openModalAction(modalName) {
|
setMobileLocationInValid(isMobileLocationInValid) {
|
||||||
this.$refs[modalName].openModal();
|
this.isMobileAddressValid = !isMobileLocationInValid;
|
||||||
},
|
},
|
||||||
closeModalAction(modalName) {
|
displayMobileAddressQuestion(openMobileLocation) {
|
||||||
this.$refs[modalName].closeModal();
|
var mobileLocationQuestionsRef = this.$refs.mobileLocationQuestions;
|
||||||
|
var isMobileAddressComplete = mobileLocationQuestionsRef.isMobileAddressComplete;
|
||||||
|
if (isMobileAddressComplete) {
|
||||||
|
mobileLocationQuestionsRef.isMobileLocationOpened = openMobileLocation;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|
@ -773,8 +769,10 @@ export default {
|
||||||
this.selectedProvider = new Provider(
|
this.selectedProvider = new Provider(
|
||||||
this.shopProviderData.mobileProviderNumber
|
this.shopProviderData.mobileProviderNumber
|
||||||
);
|
);
|
||||||
|
this.displayMobileAddressQuestion(true);
|
||||||
} else {
|
} else {
|
||||||
this.selectedProvider = new Provider();
|
this.selectedProvider = new Provider();
|
||||||
|
this.displayMobileAddressQuestion(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -786,12 +784,10 @@ export default {
|
||||||
this.selectedProvider = new Provider(
|
this.selectedProvider = new Provider(
|
||||||
this.shopProviderData.mobileProviderNumber
|
this.shopProviderData.mobileProviderNumber
|
||||||
);
|
);
|
||||||
var mobileLocationQs = this.$refs.mobileLocationQuestions;
|
this.displayMobileAddressQuestion(true);
|
||||||
if (!mobileLocationQs.isMobileAddressComplete()) {
|
|
||||||
mobileLocationQs.openModal();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
this.selectedProvider = new Provider();
|
this.selectedProvider = new Provider();
|
||||||
|
this.displayMobileAddressQuestion(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,6 @@ export default {
|
||||||
cmsWidgetName: String,
|
cmsWidgetName: String,
|
||||||
validationRules: String,
|
validationRules: String,
|
||||||
isDisplayed: Boolean,
|
isDisplayed: Boolean,
|
||||||
isInshopOnly: Boolean,
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
questionText() {
|
questionText() {
|
||||||
|
|
@ -234,13 +233,9 @@ export default {
|
||||||
// nothing to do with this component. We could mitigate this by showing / hiding this component with v-if but that messes
|
// nothing to do with this component. We could mitigate this by showing / hiding this component with v-if but that messes
|
||||||
// up the component initialization on page load.
|
// up the component initialization on page load.
|
||||||
async handler() {
|
async handler() {
|
||||||
//We do not need to run this handler in case of Inshop only else it will ended running twice function getNextShopsFromList and will cause pagination
|
|
||||||
if (!this.isInshopOnly) {
|
|
||||||
this.resetAnswers();
|
|
||||||
|
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
this.resetAnswers();
|
||||||
this.getNextShopsFromList();
|
this.getNextShopsFromList();
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
shopProviders: {
|
shopProviders: {
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import { getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper";
|
||||||
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||||
import router from "@/router/index.js";
|
import router from "@/router/index.js";
|
||||||
import { getQuerystringParameter } from "@/helpers/querystring-helper";
|
import { getQuerystringParameter } from "@/helpers/querystring-helper";
|
||||||
|
import { Variables } from "../constants/analytics";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -120,6 +121,10 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async pushVariableToDataLayer(data) {
|
||||||
|
pushToDataLayerIfDefined(data);
|
||||||
|
},
|
||||||
|
|
||||||
async pushPageViewToGA() {
|
async pushPageViewToGA() {
|
||||||
const currentPageName = getPageNameByQueryString();
|
const currentPageName = getPageNameByQueryString();
|
||||||
const pageViewEvent = {
|
const pageViewEvent = {
|
||||||
|
|
@ -306,6 +311,18 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
payload.priceSubTotal = "";
|
payload.priceSubTotal = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cash Quote or Cash Price Sub Total
|
||||||
|
if (isPricingAvailable) {
|
||||||
|
const subtotal = baseMixin.methods
|
||||||
|
.getTotalPriceOfAllLineItemsAndChildParts(combinedLineItems, false)
|
||||||
|
.toFixed(2);
|
||||||
|
|
||||||
|
payload.cashPriceSubTotal = parseFloat(subtotal);
|
||||||
|
} else {
|
||||||
|
payload.cashPriceSubTotal = "";
|
||||||
|
}
|
||||||
|
|
||||||
//unverified (in scenarios we don’t display the price)
|
//unverified (in scenarios we don’t display the price)
|
||||||
if (
|
if (
|
||||||
order.payment.isInsurance &&
|
order.payment.isInsurance &&
|
||||||
|
|
@ -770,6 +787,9 @@ export default {
|
||||||
ValueToLogTypes() {
|
ValueToLogTypes() {
|
||||||
return ValueToLogTypes;
|
return ValueToLogTypes;
|
||||||
},
|
},
|
||||||
|
Variables() {
|
||||||
|
return Variables;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -340,6 +340,10 @@ export const mutations = {
|
||||||
updateIsMSRFeeApplicable(state, isMSRFeeApplicable) {
|
updateIsMSRFeeApplicable(state, isMSRFeeApplicable) {
|
||||||
state.order.isMSRFeeApplicable = isMSRFeeApplicable;
|
state.order.isMSRFeeApplicable = isMSRFeeApplicable;
|
||||||
},
|
},
|
||||||
|
updateCashPriceSubTotal(state, cashPriceSubTotal) {
|
||||||
|
state.order.cashPriceSubTotal =
|
||||||
|
cashPriceSubTotal === "" ? null : cashPriceSubTotal.toString();
|
||||||
|
},
|
||||||
updateCCToken(state, ccToken) {
|
updateCCToken(state, ccToken) {
|
||||||
state.order.payment.ccToken.subscriptionId = ccToken.subscriptionId;
|
state.order.payment.ccToken.subscriptionId = ccToken.subscriptionId;
|
||||||
state.order.payment.ccToken.expMonth = ccToken.expMonth;
|
state.order.payment.ccToken.expMonth = ccToken.expMonth;
|
||||||
|
|
@ -1732,7 +1736,10 @@ export const actions = {
|
||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
|
|
||||||
getMobileFeePart(context, { payload: { serviceZipCode, serviceZipCodeCtu }, pageNameToLog }) {
|
getMobileFeePart(
|
||||||
|
context,
|
||||||
|
{ payload: { serviceZipCode, serviceZipCodeCtu, mobileProviderNumber }, pageNameToLog }
|
||||||
|
) {
|
||||||
const serviceType = context.getters.damage.isRepair ? "Repair" : "Install";
|
const serviceType = context.getters.damage.isRepair ? "Repair" : "Install";
|
||||||
const facilityType = "Mobile";
|
const facilityType = "Mobile";
|
||||||
const parentAccountNumber = context.getters.payment.parentAccountNumber;
|
const parentAccountNumber = context.getters.payment.parentAccountNumber;
|
||||||
|
|
@ -1750,6 +1757,7 @@ export const actions = {
|
||||||
order.serviceLocation?.zipCode;
|
order.serviceLocation?.zipCode;
|
||||||
|
|
||||||
var providerNumber =
|
var providerNumber =
|
||||||
|
mobileProviderNumber ??
|
||||||
serviceZipCodeCtu ??
|
serviceZipCodeCtu ??
|
||||||
order.serviceLocation?.provider?.providerNumber ??
|
order.serviceLocation?.provider?.providerNumber ??
|
||||||
order.serviceLocation?.zipCodeCtu;
|
order.serviceLocation?.zipCodeCtu;
|
||||||
|
|
@ -2339,6 +2347,7 @@ export const actions = {
|
||||||
lockToken: order.lockToken,
|
lockToken: order.lockToken,
|
||||||
isRecalAckOptIn: order.isRecalAckOptIn,
|
isRecalAckOptIn: order.isRecalAckOptIn,
|
||||||
isMSRFeeApplicable: order.isMSRFeeApplicable,
|
isMSRFeeApplicable: order.isMSRFeeApplicable,
|
||||||
|
cashPriceSubTotal: order.cashPriceSubTotal,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
logApiCall: true,
|
logApiCall: true,
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<div
|
<div
|
||||||
:aria-label="buttonLabel"
|
:aria-label="buttonLabel"
|
||||||
class="button-content list-button-content d-flex flex-column justify-content-center py-3 px-4 p-md-4">
|
class="button-content list-button-content d-flex flex-column justify-content-center py-3 px-4 p-md-4">
|
||||||
<span class="m-0" :class="textPosition">
|
<span class="m-0" :class="[textPosition, labelBold ? 'span-bold' : '']">
|
||||||
{{ buttonLabel }}
|
{{ buttonLabel }}
|
||||||
</span>
|
</span>
|
||||||
<span v-if="buttonLabelSubCopy" class="m-0 small" :class="textPosition">
|
<span v-if="buttonLabelSubCopy" class="m-0 small" :class="textPosition">
|
||||||
|
|
@ -88,6 +88,9 @@ export default {
|
||||||
&:checked + .list-button-content span {
|
&:checked + .list-button-content span {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
&:checked + .list-button-content .span-bold {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
&:checked + .list-button-content span:nth-child(2) {
|
&:checked + .list-button-content span:nth-child(2) {
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
color: $gray-600;
|
color: $gray-600;
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,10 @@
|
||||||
:class="!isWide ? 'order-1' : 'ms-auto order-3'"
|
:class="!isWide ? 'order-1' : 'ms-auto order-3'"
|
||||||
:src="buttonImage"
|
:src="buttonImage"
|
||||||
:alt="altText" />
|
:alt="altText" />
|
||||||
<p v-if="!isWide" class="small order-3" :class="isMultiSelect ? 'm-0' : 'mt-2 mb-0'">
|
<p
|
||||||
|
v-if="!isWide"
|
||||||
|
class="small order-3"
|
||||||
|
:class="[isMultiSelect ? 'm-0' : 'mt-2 mb-0', labelBold ? 'p-bold' : '']">
|
||||||
{{ buttonLabel }}
|
{{ buttonLabel }}
|
||||||
</p>
|
</p>
|
||||||
<p v-if="buttonLabelSubCopy && !isWide" class="fs-7 m-0 order-4 sub-copy">
|
<p v-if="buttonLabelSubCopy && !isWide" class="fs-7 m-0 order-4 sub-copy">
|
||||||
|
|
@ -122,6 +125,9 @@ export default {
|
||||||
color: $black;
|
color: $black;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
.p-bold {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
.sub-copy {
|
.sub-copy {
|
||||||
color: $gray-600;
|
color: $gray-600;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue