First pass at desktop max width and breakpoints per page.

Rename funnel-footer to nav-bar.
This commit is contained in:
Bryan Mauger 2023-09-18 16:20:45 -04:00
parent ed0d3aacc4
commit ce6916658a
27 changed files with 1100 additions and 1024 deletions

View file

@ -23,8 +23,8 @@
:answerKey="questionsDatum.answerKey" :answerKey="questionsDatum.answerKey"
:validationRules="validationRules" /> :validationRules="validationRules" />
</div> </div>
<funnel-footer <navbar
ref="funnelFooter" ref="navbar"
cmsWidgetName="FunnelFooterWidget" cmsWidgetName="FunnelFooterWidget"
:isForwardActionDisabled="!isMetaValid" :isForwardActionDisabled="!isMetaValid"
@back-clicked="handleBackButtonAction" @back-clicked="handleBackButtonAction"
@ -40,7 +40,7 @@ import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner";
import alert from "@/ux-components/alert/alert"; import alert from "@/ux-components/alert/alert";
import questionChain from "@/digital-components/question-chain/question-chain"; import questionChain from "@/digital-components/question-chain/question-chain";
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header"; import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
import funnelFooter from "@/fmg-components/funnel-footer/funnel-footer"; import navbar from "@/fmg-components/nav-bar/nav-bar";
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue"; import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
export default { export default {
@ -88,7 +88,7 @@ export default {
alert, alert,
questionChain, questionChain,
funnelSubHeader, funnelSubHeader,
funnelFooter, navbar,
loadingModal, loadingModal,
}, },
}; };

View file

@ -1,10 +1,10 @@
import { mount } from "@vue/test-utils"; import { mount } from "@vue/test-utils";
import funnelFooter from "./funnel-footer"; import navbar from "./nav-bar";
describe("funnel-footer.vue", () => { describe("nav-bar.vue", () => {
test("Should emit ForwardClicked on button click", async () => { test("Should emit ForwardClicked on button click", async () => {
// Act // Act
const wrapper = mount(funnelFooter, { const wrapper = mount(navbar, {
mixins: [mockMixin], mixins: [mockMixin],
}); });
wrapper.vm.buttonClick(); wrapper.vm.buttonClick();
@ -14,7 +14,7 @@ describe("funnel-footer.vue", () => {
test("Should emit BackClicked on link click", async () => { test("Should emit BackClicked on link click", async () => {
// Act // Act
const wrapper = mount(funnelFooter, { const wrapper = mount(navbar, {
mixins: [mockMixin], mixins: [mockMixin],
}); });
wrapper.vm.linkClick(); wrapper.vm.linkClick();
@ -24,7 +24,7 @@ describe("funnel-footer.vue", () => {
test("Should change button text when update button text is called", async () => { test("Should change button text when update button text is called", async () => {
// Act // Act
const wrapper = mount(funnelFooter, { const wrapper = mount(navbar, {
mixins: [mockMixin], mixins: [mockMixin],
}); });
wrapper.vm.updateButtonText("newText"); wrapper.vm.updateButtonText("newText");
@ -35,7 +35,7 @@ describe("funnel-footer.vue", () => {
test("should run removeLoader fn on buttonMain and return false for onkeydown fn", async () => { test("should run removeLoader fn on buttonMain and return false for onkeydown fn", async () => {
// Arrange // Arrange
const wrapper = mount(funnelFooter, { const wrapper = mount(navbar, {
mixins: [mockMixin], mixins: [mockMixin],
}); });

View file

@ -1,6 +1,6 @@
<template> <template>
<div class="row"></div> <div class="row"></div>
<footer class="footer container-fluid g-5 my-5 px-0" id="infoBox"> <div class="nav-bar container-fluid g-5 my-5 px-0" id="infoBox">
<div class="row d-flex flex-row-reverse align-items-center vw-100"> <div class="row d-flex flex-row-reverse align-items-center vw-100">
<div class="col button-col d-flex" id="stacked"> <div class="col button-col d-flex" id="stacked">
<buttonMain <buttonMain
@ -13,7 +13,7 @@
:isDisabled="isForwardActionDisabled" :isDisabled="isForwardActionDisabled"
@click-event="buttonClick" @click-event="buttonClick"
data-bs-target="#footerModal" data-bs-target="#footerModal"
data-test-id="funnel-footer-main-button" data-test-id="nav-bar-main-button"
data-bs-dismiss="modal" /> data-bs-dismiss="modal" />
</div> </div>
<div v-if="!isBackButtonHidden" class="col-auto link-col py-1 text-break"> <div v-if="!isBackButtonHidden" class="col-auto link-col py-1 text-break">
@ -27,7 +27,7 @@
data-bs-dismiss="modal" /> data-bs-dismiss="modal" />
</div> </div>
</div> </div>
</footer> </div>
</template> </template>
<script> <script>
@ -35,7 +35,7 @@ import textLink from "@/ux-components/text-link/text-link";
import buttonMain from "@/ux-components/button-main/button-main"; import buttonMain from "@/ux-components/button-main/button-main";
export default { export default {
name: "funnelFooter", name: "navbar",
emits: ["BackClicked", "ForwardClicked"], // <--- should remove oodles of warnings in dev tools emits: ["BackClicked", "ForwardClicked"], // <--- should remove oodles of warnings in dev tools
props: { props: {
isForwardActionDisabled: Boolean, isForwardActionDisabled: Boolean,
@ -90,7 +90,7 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.footer { .nav-bar {
overflow: visible; overflow: visible;
display: flex; display: flex;
a { a {

View file

@ -722,8 +722,8 @@ function setupMocks({
wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => ""); wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => "");
wrapper.vm.setCmsContent = jest.fn(); wrapper.vm.setCmsContent = jest.fn();
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); wrapper.vm.$refs.navbar.updateButtonText = jest.fn();
wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn(); wrapper.vm.$refs.navbar.removeLoader = jest.fn();
return { wrapper }; return { wrapper };
} }

View file

@ -67,9 +67,9 @@
</div> </div>
</div> </div>
</transition> </transition>
<funnel-footer <navbar
cmsWidgetName="FunnelFooterWidget" cmsWidgetName="FunnelFooterWidget"
ref="funnelFooter" ref="navbar"
:isDisabled="!meta.valid" :isDisabled="!meta.valid"
@ForwardClicked="forwardButtonAction" @ForwardClicked="forwardButtonAction"
@back-clicked="backButtonAction" @back-clicked="backButtonAction"
@ -82,7 +82,7 @@
<script> <script>
// Components // Components
import funnelHeader from "@/fmg-components/funnel-header/funnel-header"; import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
import funnelFooter from "@/fmg-components/funnel-footer/funnel-footer"; import navbar from "@/fmg-components/nav-bar/nav-bar";
import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner"; import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner";
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header"; import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
import customerQuestions from "@/layouts/address-lookup/customer-questions/customer-questions"; import customerQuestions from "@/layouts/address-lookup/customer-questions/customer-questions";
@ -241,14 +241,14 @@ export default {
if (!resultMap.vinLookupResponse.isStatePermissible) { if (!resultMap.vinLookupResponse.isStatePermissible) {
// State Restrictions forbid lookup by address // State Restrictions forbid lookup by address
this.displayVinLookupByHomeAddressNotAllowedAlert = true; this.displayVinLookupByHomeAddressNotAllowedAlert = true;
return this.$refs.funnelFooter.removeLoader(); return this.$refs.navbar.removeLoader();
} }
// If a Service Zip is entered and it is an invalid zip code (ex. 11111) then show an alert // If a Service Zip is entered and it is an invalid zip code (ex. 11111) then show an alert
const isZipValid = resultMap.serviceZipValidationResponse.isValid; const isZipValid = resultMap.serviceZipValidationResponse.isValid;
if (this.serviceZipCode && !isZipValid) { if (this.serviceZipCode && !isZipValid) {
this.displayInvalidZipAlert = true; this.displayInvalidZipAlert = true;
return this.$refs.funnelFooter.removeLoader(); return this.$refs.navbar.removeLoader();
} }
this.displayInvalidZipAlert = false; this.displayInvalidZipAlert = false;
@ -271,10 +271,10 @@ export default {
); );
// Update button "Continue with..." // Update button "Continue with..."
this.$refs.funnelFooter.updateButtonText( this.$refs.navbar.updateButtonText(
`Continue with ${carFound.year} ${carFound.make} ${carFound.model}` `Continue with ${carFound.year} ${carFound.make} ${carFound.model}`
); );
return this.$refs.funnelFooter.removeLoader(); return this.$refs.navbar.removeLoader();
} }
// update data if the zip or service zip is serviceable // update data if the zip or service zip is serviceable
@ -294,7 +294,7 @@ export default {
} else { } else {
// No VINS found. // No VINS found.
this.displayVinNotFoundAlert = true; this.displayVinNotFoundAlert = true;
return this.$refs.funnelFooter.removeLoader(); return this.$refs.navbar.removeLoader();
} }
// If the either the registration zip code or service zip code are not serviceable // If the either the registration zip code or service zip code are not serviceable
@ -302,7 +302,7 @@ export default {
if (!this.isZipServiceable) { if (!this.isZipServiceable) {
this.displayNonServiceableZipAlert = true; this.displayNonServiceableZipAlert = true;
this.showServiceZipField = true; this.showServiceZipField = true;
return this.$refs.funnelFooter.removeLoader(); return this.$refs.navbar.removeLoader();
} }
// If the registration zip code is serviceable and nothing was entered for the service zip code // If the registration zip code is serviceable and nothing was entered for the service zip code
@ -420,7 +420,7 @@ export default {
customerQuestions: { customerQuestions: {
handler(newValue) { handler(newValue) {
// if they modify one of the lookup fields (address, city, state, zipCode, or lastName), then modify the button text back to "Get my personalized quote" // if they modify one of the lookup fields (address, city, state, zipCode, or lastName), then modify the button text back to "Get my personalized quote"
this.$refs.funnelFooter.updateButtonText( this.$refs.navbar.updateButtonText(
this.getCmsContent("FunnelFooterWidget", "ForwardButtonText") this.getCmsContent("FunnelFooterWidget", "ForwardButtonText")
); );
this.showServiceZipField = false; this.showServiceZipField = false;
@ -445,7 +445,7 @@ export default {
}, },
components: { components: {
funnelHeader, funnelHeader,
funnelFooter, navbar,
vehicleBanner, vehicleBanner,
funnelSubHeader, funnelSubHeader,
customerQuestions, customerQuestions,

View file

@ -76,8 +76,8 @@ describe("addressVehicles.vue", () => {
}; };
// the following has to be set BEFORE changing the data which is being watched, and requires updateButtonText to be mocked // the following has to be set BEFORE changing the data which is being watched, and requires updateButtonText to be mocked
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); wrapper.vm.$refs.navbar.updateButtonText = jest.fn();
wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn(); wrapper.vm.$refs.navbar.removeLoader = jest.fn();
wrapper.vm.lookupVin = jest.fn(() => Promise.resolve(lookupVinResponse)); wrapper.vm.lookupVin = jest.fn(() => Promise.resolve(lookupVinResponse));
wrapper.vm.$router.navigate = jest.fn(); wrapper.vm.$router.navigate = jest.fn();
wrapper.vm.updateCustomerInfo = jest.fn().mockImplementation(() => {}); wrapper.vm.updateCustomerInfo = jest.fn().mockImplementation(() => {});
@ -106,8 +106,8 @@ describe("addressVehicles.vue", () => {
}; };
// the following has to be set BEFORE changing the data which is being watched, and requires updateButtonText to be mocked // the following has to be set BEFORE changing the data which is being watched, and requires updateButtonText to be mocked
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); wrapper.vm.$refs.navbar.updateButtonText = jest.fn();
wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn(); wrapper.vm.$refs.navbar.removeLoader = jest.fn();
wrapper.vm.lookupVin = jest.fn(() => Promise.reject(lookupVinResponse)); wrapper.vm.lookupVin = jest.fn(() => Promise.reject(lookupVinResponse));
wrapper.vm.$router.navigateWithSaving = jest.fn(); wrapper.vm.$router.navigateWithSaving = jest.fn();
wrapper.vm.updateCustomerInfo = jest.fn().mockImplementation(() => {}); wrapper.vm.updateCustomerInfo = jest.fn().mockImplementation(() => {});
@ -128,7 +128,7 @@ describe("addressVehicles.vue", () => {
test("Should navigate to CLICKED_FORWARD scenario if carId is different and selected glass not available for vehicle on navigateForward", async () => { test("Should navigate to CLICKED_FORWARD scenario if carId is different and selected glass not available for vehicle on navigateForward", async () => {
// Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); wrapper.vm.$refs.navbar.updateButtonText = jest.fn();
wrapper.vm.$router.navigateWithSaving = jest.fn(); wrapper.vm.$router.navigateWithSaving = jest.fn();
// Act // Act
@ -148,7 +148,7 @@ describe("addressVehicles.vue", () => {
test("carId is not different on navigateForward (car was found) => Should handle navigating forward with car match", async () => { test("carId is not different on navigateForward (car was found) => Should handle navigating forward with car match", async () => {
// Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); wrapper.vm.$refs.navbar.updateButtonText = jest.fn();
navigateToHeritage.navigateToHeritageFunnel = jest.fn(); navigateToHeritage.navigateToHeritageFunnel = jest.fn();
// Act // Act
@ -224,7 +224,7 @@ function setupMocks({}) {
const wrapper = shallowMount(addressVehicles, mountOptions); const wrapper = shallowMount(addressVehicles, mountOptions);
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); wrapper.vm.$refs.navbar.updateButtonText = jest.fn();
wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn(); wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn();
return { wrapper }; return { wrapper };

View file

@ -27,9 +27,9 @@
class="mb-3" class="mb-3"
marginTopSizeOverride="3" /> marginTopSizeOverride="3" />
</div> </div>
<funnelFooter <navbar
cmsWidgetName="FunnelFooterWidget" cmsWidgetName="FunnelFooterWidget"
ref="funnelFooter" ref="navbar"
:isForwardActionDisabled="!meta.valid" :isForwardActionDisabled="!meta.valid"
@back-clicked="backButtonAction" @back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" /> @ForwardClicked="forwardButtonAction" />
@ -40,7 +40,7 @@
<script> <script>
// Components // Components
import funnelHeader from "@/fmg-components/funnel-header/funnel-header"; import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
import funnelFooter from "@/fmg-components/funnel-footer/funnel-footer"; import navbar from "@/fmg-components/nav-bar/nav-bar";
import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner"; import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner";
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header"; import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
import alert from "@/ux-components/alert/alert"; import alert from "@/ux-components/alert/alert";
@ -165,7 +165,7 @@ export default {
const vinLookup = await this.dispatchStoreAction(storeActions.LOOKUP_VEHICLE_BY_VIN, { const vinLookup = await this.dispatchStoreAction(storeActions.LOOKUP_VEHICLE_BY_VIN, {
vin: this.selectedVehicle.vin, vin: this.selectedVehicle.vin,
}).catch(() => { }).catch(() => {
this.$refs.funnelFooter.removeLoader(); this.$refs.navbar.removeLoader();
}); });
if (!vinLookup) { if (!vinLookup) {
@ -210,11 +210,11 @@ export default {
this.isCarIdDifferent = this.isCarIdDifferent =
this.selectedVehicle?.vehicle.carId !== store.getters.vehicle.carId; this.selectedVehicle?.vehicle.carId !== store.getters.vehicle.carId;
if (this.isCarIdDifferent) { if (this.isCarIdDifferent) {
this.$refs.funnelFooter.updateButtonText( this.$refs.navbar.updateButtonText(
`Continue with ${this.selectedVehicle.vehicle.year} ${this.selectedVehicle.vehicle.make} ${this.selectedVehicle.vehicle.model}` `Continue with ${this.selectedVehicle.vehicle.year} ${this.selectedVehicle.vehicle.make} ${this.selectedVehicle.vehicle.model}`
); );
} else { } else {
this.$refs.funnelFooter.updateButtonText( this.$refs.navbar.updateButtonText(
this.getCmsContent("FunnelFooterWidget", "ForwardButtonText") this.getCmsContent("FunnelFooterWidget", "ForwardButtonText")
); );
} }
@ -229,7 +229,7 @@ export default {
vehicleBanner, vehicleBanner,
funnelSubHeader, funnelSubHeader,
alert, alert,
funnelFooter, navbar,
addressVehiclesQuestion, addressVehiclesQuestion,
textBlock, textBlock,
}, },

View file

@ -1,57 +1,76 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }"> <Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
<div class="page-container-grouped-styles"> <div class="container-fluid">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <div class="row">
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" class="my-5" /> <div class="col">
<textboxQuestion <funnelHeader cmsWidgetName="FunnelHeaderWidget" />
class="mb-4" </div>
cmsWidgetName="FirstNameWidget" </div>
v-model="firstName" <div class="row">
ref="firstName" <div class="col-md-2 col-lg-3">&nbsp;</div>
customInputId="firstName" <div class="col-md-8 col-lg-6">
validationRules="first-name-required" /> <funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" class="my-5" />
<textboxQuestion <textboxQuestion
class="mb-4" class="mb-4"
cmsWidgetName="LastNameWidget" cmsWidgetName="FirstNameWidget"
v-model="lastName" v-model="firstName"
ref="lastName" ref="firstName"
customInputId="lastName" customInputId="firstName"
validationRules="last-name-required" /> validationRules="first-name-required" />
<textboxQuestion
class="mb-4" <textboxQuestion
cmsWidgetName="emailQuestionWidget" class="mb-4"
v-model="emailAddress" cmsWidgetName="LastNameWidget"
inputId="email" v-model="lastName"
validationRules="email-address-required|email-address-format" /> ref="lastName"
<phoneNumberQuestion customInputId="lastName"
class="mb-4" validationRules="last-name-required" />
cmsWidgetName="PhoneNumberQuestionWidget"
v-model="phoneNumber" <textboxQuestion
isRequired class="mb-4"
validationRules="phone-number-required" /> cmsWidgetName="emailQuestionWidget"
<checkboxQuestion v-model="emailAddress"
class="mb-5" inputId="email"
cmsWidgetName="TextMeQuestionWidget" validationRules="email-address-required|email-address-format" />
v-model="isSmsOptIn" />
<textareaQuestion <phoneNumberQuestion
class="mb-4" class="mb-4"
v-model="techNotes" cmsWidgetName="PhoneNumberQuestionWidget"
cmsWidgetName="TextAreaContentWidget" v-model="phoneNumber"
maxLength="250" /> isRequired
<textBlock cmsWidgetName="DisclaimerCopyWidget" typeStyle="caption" /> validationRules="phone-number-required" />
<funnel-footer
cmsWidgetName="FunnelFooterWidget" <checkboxQuestion
ref="funnelFooter" class="mb-5"
:isForwardActionDisabled="!meta.valid" cmsWidgetName="TextMeQuestionWidget"
@back-clicked="backButtonAction" v-model="isSmsOptIn" />
@ForwardClicked="forwardButtonAction" />
<textareaQuestion
class="mb-4"
v-model="techNotes"
cmsWidgetName="TextAreaContentWidget"
maxLength="250" />
<textBlock cmsWidgetName="DisclaimerCopyWidget" typeStyle="caption" />
<navbar
cmsWidgetName="FunnelFooterWidget"
ref="navbar"
:isForwardActionDisabled="!meta.valid"
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" />
</div>
<div class="col-md-2 col-lg-3">&nbsp;</div>
</div>
</div> </div>
</Form> </Form>
</template> </template>
<script> <script>
import funnelHeader from "@/fmg-components/funnel-header/funnel-header"; import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
import funnelFooter from "@/fmg-components/funnel-footer/funnel-footer"; 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 textareaQuestion from "@/digital-components/textarea-question/textarea-question"; import textareaQuestion from "@/digital-components/textarea-question/textarea-question";
import textboxQuestion from "@/digital-components/textbox-question/textbox-question"; import textboxQuestion from "@/digital-components/textbox-question/textbox-question";
@ -172,7 +191,7 @@ export default {
funnelSubHeader, funnelSubHeader,
textareaQuestion, textareaQuestion,
textboxQuestion, textboxQuestion,
funnelFooter, navbar,
Form, Form,
textBlock, textBlock,
phoneNumberQuestion, phoneNumberQuestion,

View file

@ -254,7 +254,7 @@ function setupMocks({
{ Name: "Provide my license plate # Most accurate VIN match" }, { Name: "Provide my license plate # Most accurate VIN match" },
{ Name: "Provide my home address Most convenient VIN match" }, { Name: "Provide my home address Most convenient VIN match" },
], ],
funnelFooterWidget = { ForwardButtonText: "test txt" }, FunnelFooterWidget = { ForwardButtonText: "test txt" },
mountOptionsMockData = { mountOptionsMockData = {
router: { router: {
navigate: jest.fn(), navigate: jest.fn(),
@ -271,7 +271,7 @@ function setupMocks({
groupName: groupName, groupName: groupName,
QuestionText: cmsQuestionText, QuestionText: cmsQuestionText,
Answers: cmsAnswers, Answers: cmsAnswers,
FunnelFooterWidget: funnelFooterWidget, FunnelFooterWidget: FunnelFooterWidget,
}; };
const apiPromise = Promise.resolve({ cmsContent }); const apiPromise = Promise.resolve({ cmsContent });

View file

@ -1,14 +1,20 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }"> <Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
<div class="page-container-grouped-styles"> <loadingModal ref="loadingModal" />
<loadingModal ref="loadingModal" /> <div class="container-fluid">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <div class="row">
<vehicleBanner cmsWidgetName="VehicleBannerWidget" /> <div class="col">
<funnelSubHeader class="pb-4" cmsWidgetName="FunnelSubHeaderWidget" /> <funnelHeader cmsWidgetName="FunnelHeaderWidget" />
<div class="fade-on-route-transition sub-container make-tall"> </div>
<div v-if="!skipVin"> </div>
<div class="vinlookupquestion" v-html="this.VinLookupQuestionText"></div> <div class="row">
<buttonQuestion <div class="col-md-2 col-lg-3">&nbsp;</div>
<div class="col-md-8 col-lg-6">
<vehicleBanner cmsWidgetName="VehicleBannerWidget" />
<funnelSubHeader class="pb-4" cmsWidgetName="FunnelSubHeaderWidget" />
<div v-if="!skipVin">
<div class="vinlookupquestion" v-html="this.VinLookupQuestionText"></div>
<buttonQuestion
cmsWidgetName="VinLookupMethod" cmsWidgetName="VinLookupMethod"
:answers="answersFromCms" :answers="answersFromCms"
groupName="vinLookupMethodOption" groupName="vinLookupMethodOption"
@ -17,63 +23,59 @@
isRequired isRequired
validationRules="option-required" validationRules="option-required"
:logDisplayedValuesEvent="true" /> :logDisplayedValuesEvent="true" />
</div> </div>
<div v-else>
<alert <div v-else>
<alert
class="my-4" class="my-4"
:cmsWidgetName="alertInfo" :cmsWidgetName="alertInfo"
alertClass="alert-info" alertClass="alert-info"
v-bind:isDismissible="false" /> v-bind:isDismissible="false" />
<div class="row my-2">
<div class="col"> <textboxQuestion
<textboxQuestion cmsWidgetName="ServiceZipQuestionWidget"
cmsWidgetName="ServiceZipQuestionWidget" v-model="serviceZipCode"
v-model="serviceZipCode" inputId="serviceZipCode"
inputId="serviceZipCode" mask="#####"
mask="#####" isRequired
isRequired validationRules="zip-required|zip-format" />
validationRules="zip-required|zip-format" />
</div> <textboxQuestion
</div> cmsWidgetName="EmailAddressQuestionWidget"
<div class="row mt-2"> v-model="emailAddress"
<div class="col"> inputId="emailAddress"
<textboxQuestion isRequired
cmsWidgetName="EmailAddressQuestionWidget" disableAutoFill
v-model="emailAddress" validationRules="email-address-required|email-address-format" />
inputId="emailAddress" <textBlock
isRequired cmsWidgetName="QuoteEmailTextBlockWidget"
disableAutoFill typeStyle="caption" />
validationRules="email-address-required|email-address-format" />
</div> <alert
</div>
<div class="row mb-2">
<div class="col">
<textBlock
cmsWidgetName="QuoteEmailTextBlockWidget"
typeStyle="caption" />
</div>
</div>
<alert
ref="alertInvalidZip" ref="alertInvalidZip"
v-if="displayInvalidZipAlert" v-if="displayInvalidZipAlert"
class="my-4" class="my-4"
cmsWidgetName="AlertInvalidZipWidget" cmsWidgetName="AlertInvalidZipWidget"
alertClass="alert-danger" alertClass="alert-danger"
v-bind:isDismissible="false" /> v-bind:isDismissible="false" />
<alert
<alert
class="my-4" class="my-4"
:manualHeadline="AlertNonServiceableZipHeader" :manualHeadline="AlertNonServiceableZipHeader"
:manualCopy="AlertNonServiceableZipBody" :manualCopy="AlertNonServiceableZipBody"
v-model="customAlertData" v-model="customAlertData"
v-if="displayNonServiceableZipAlert" v-if="displayNonServiceableZipAlert"
alertClass="alert-danger" /> alertClass="alert-danger" />
<navbar
cmsWidgetName="FunnelFooterWidget"
ref="navbar"
:isForwardActionDisabled="!meta.valid"
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" />
</div>
</div> </div>
<funnel-footer <div class="col-md-2 col-lg-3">&nbsp;</div>
cmsWidgetName="FunnelFooterWidget"
ref="funnelFooter"
:isForwardActionDisabled="!meta.valid"
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" />
</div> </div>
</div> </div>
</Form> </Form>
@ -82,7 +84,7 @@
<script> <script>
// Components // Components
import funnelHeader from "@/fmg-components/funnel-header/funnel-header"; import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
import funnelFooter from "@/fmg-components/funnel-footer/funnel-footer"; import navbar from "@/fmg-components/nav-bar/nav-bar";
import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner"; import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner";
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header"; import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
import buttonQuestion from "@/digital-components/button-question/button-question"; import buttonQuestion from "@/digital-components/button-question/button-question";
@ -152,8 +154,8 @@ export default {
} }
const zip = lowerCaseParams.get(queryStrings.ZIP_CODE) const zip = lowerCaseParams.get(queryStrings.ZIP_CODE)
? lowerCaseParams.get(queryStrings.ZIP_CODE) ? lowerCaseParams.get(queryStrings.ZIP_CODE)
: store.getters.order.serviceLocation.zipCode; : store.getters.order.serviceLocation.zipCode;
var vinByAddressPromise; var vinByAddressPromise;
if (zip) { if (zip) {
@ -171,10 +173,10 @@ export default {
promise: cmsContentPromise, promise: cmsContentPromise,
}, },
zip != null && zip != null &&
zip != undefined && { zip != undefined && {
resultKey: "vinByAddress", resultKey: "vinByAddress",
promise: vinByAddressPromise, promise: vinByAddressPromise,
}, },
]; ];
const resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
@ -186,13 +188,13 @@ export default {
vm.skipVinNotRepair = skipVinNotRepair; vm.skipVinNotRepair = skipVinNotRepair;
if (resultMap.cmsContent.FunnelFooterWidget.ForwardButtonText.includes("|")) { if (resultMap.cmsContent.FunnelFooterWidget.ForwardButtonText.includes("|")) {
const forwardTextOption = const forwardTextOption =
resultMap.cmsContent.FunnelFooterWidget.ForwardButtonText.split("|"); resultMap.cmsContent.FunnelFooterWidget.ForwardButtonText.split("|");
if (skipVin) { if (skipVin) {
resultMap.cmsContent.FunnelFooterWidget.ForwardButtonText = resultMap.cmsContent.FunnelFooterWidget.ForwardButtonText =
forwardTextOption[1]; forwardTextOption[1];
} else { } else {
resultMap.cmsContent.FunnelFooterWidget.ForwardButtonText = resultMap.cmsContent.FunnelFooterWidget.ForwardButtonText =
forwardTextOption[0]; forwardTextOption[0];
} }
} }
@ -238,19 +240,19 @@ export default {
if (!zipCodeData.isValid) { if (!zipCodeData.isValid) {
this.displayInvalidZipAlert = true; this.displayInvalidZipAlert = true;
return this.$refs.funnelFooter.removeLoader(); return this.$refs.navbar.removeLoader();
} }
this.displayInvalidZipAlert = false; this.displayInvalidZipAlert = false;
if (!zipCodeData.isServiceable) { if (!zipCodeData.isServiceable) {
this.displayNonServiceableZipAlert = true; this.displayNonServiceableZipAlert = true;
return this.$refs.funnelFooter.removeLoader(); return this.$refs.navbar.removeLoader();
} }
this.displayNonServiceableZipAlert = false; this.displayNonServiceableZipAlert = false;
const payment = this.$store.getters.payment; const payment = this.$store.getters.payment;
const vehicleChangedDuringPolicyLookupInHeritage = const vehicleChangedDuringPolicyLookupInHeritage =
payment.isInsurance && payment.insuranceCoverage.coverageStatus; payment.isInsurance && payment.insuranceCoverage.coverageStatus;
if (vehicleChangedDuringPolicyLookupInHeritage) { if (vehicleChangedDuringPolicyLookupInHeritage) {
navigateToHeritageFunnel({ navigateToHeritageFunnel({
shouldSaveSession: true, shouldSaveSession: true,
@ -346,7 +348,7 @@ export default {
funnelHeader, funnelHeader,
vehicleBanner, vehicleBanner,
funnelSubHeader, funnelSubHeader,
funnelFooter, navbar,
buttonQuestion, buttonQuestion,
Form, Form,
alert, alert,

View file

@ -315,7 +315,7 @@ describe("license-plate-lookup.vue", () => {
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
//Assert //Assert
expect(wrapper.vm.$refs.funnelFooter.updateButtonText).toHaveBeenCalled(); expect(wrapper.vm.$refs.navbar.updateButtonText).toHaveBeenCalled();
}); });
test("Button Text should revert to initial value when registrationZip textfield has new text", async () => { test("Button Text should revert to initial value when registrationZip textfield has new text", async () => {
@ -330,7 +330,7 @@ describe("license-plate-lookup.vue", () => {
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
//Assert //Assert
expect(wrapper.vm.$refs.funnelFooter.updateButtonText).toHaveBeenCalled(); expect(wrapper.vm.$refs.navbar.updateButtonText).toHaveBeenCalled();
}); });
test("Button Text should revert to initial value when serviceZip textfield has new text", async () => { test("Button Text should revert to initial value when serviceZip textfield has new text", async () => {
@ -345,7 +345,7 @@ describe("license-plate-lookup.vue", () => {
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
//Assert //Assert
expect(wrapper.vm.$refs.funnelFooter.updateButtonText).toHaveBeenCalled(); expect(wrapper.vm.$refs.navbar.updateButtonText).toHaveBeenCalled();
}); });
}); });
@ -703,8 +703,8 @@ function setupMocks({
wrapper.vm.setCmsContent = jest.fn(); wrapper.vm.setCmsContent = jest.fn();
wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => ""); wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => "");
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); wrapper.vm.$refs.navbar.updateButtonText = jest.fn();
wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn(); wrapper.vm.$refs.navbar.removeLoader = jest.fn();
return { wrapper, apiPromise }; return { wrapper, apiPromise };
} }

View file

@ -77,8 +77,8 @@
:manualCopy="AlertMatchedDifferentVehicleBody" :manualCopy="AlertMatchedDifferentVehicleBody"
alertClass="alert-warning" alertClass="alert-warning"
v-bind:isDismissible="false" /> v-bind:isDismissible="false" />
<funnelFooter <navbar
ref="funnelFooter" ref="navbar"
cmsWidgetName="FunnelFooterWidget" cmsWidgetName="FunnelFooterWidget"
:isForwardActionDisabled="!meta.valid" :isForwardActionDisabled="!meta.valid"
@back-clicked="backButtonAction" @back-clicked="backButtonAction"
@ -91,7 +91,7 @@
<script> <script>
// Components // Components
import funnelHeader from "@/fmg-components/funnel-header/funnel-header"; import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
import funnelFooter from "@/fmg-components/funnel-footer/funnel-footer"; import navbar from "@/fmg-components/nav-bar/nav-bar";
import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner"; import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner";
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header"; import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
import alert from "@/ux-components/alert/alert"; import alert from "@/ux-components/alert/alert";
@ -230,7 +230,7 @@ export default {
).catch(() => { ).catch(() => {
// No VIN found. // No VIN found.
this.displayVinNotFoundAlert = true; this.displayVinNotFoundAlert = true;
return this.$refs.funnelFooter.removeLoader(); return this.$refs.navbar.removeLoader();
}); });
// If no VIN was found, stop processing after displaying alert // If no VIN was found, stop processing after displaying alert
if (!vinLookup) { if (!vinLookup) {
@ -241,7 +241,7 @@ export default {
const isZipValid = resultMap.serviceZipValidationResponse.isValid; const isZipValid = resultMap.serviceZipValidationResponse.isValid;
if (this.serviceZipCode && !isZipValid) { if (this.serviceZipCode && !isZipValid) {
this.displayInvalidZipAlert = true; this.displayInvalidZipAlert = true;
return this.$refs.funnelFooter.removeLoader(); return this.$refs.navbar.removeLoader();
} }
this.displayInvalidZipAlert = false; this.displayInvalidZipAlert = false;
@ -264,10 +264,10 @@ export default {
); );
// Update button "Continue with..." // Update button "Continue with..."
this.$refs.funnelFooter.updateButtonText( this.$refs.navbar.updateButtonText(
`Continue with ${vinLookup.data.vehicle.year} ${vinLookup.data.vehicle.make} ${vinLookup.data.vehicle.model}` `Continue with ${vinLookup.data.vehicle.year} ${vinLookup.data.vehicle.make} ${vinLookup.data.vehicle.model}`
); );
return this.$refs.funnelFooter.removeLoader(); return this.$refs.navbar.removeLoader();
} }
// If the either the registration zip code or service zip code are not serviceable // If the either the registration zip code or service zip code are not serviceable
@ -275,7 +275,7 @@ export default {
if (!this.isZipServiceable) { if (!this.isZipServiceable) {
this.displayNonServiceableZipAlert = true; this.displayNonServiceableZipAlert = true;
this.showServiceZipField = true; this.showServiceZipField = true;
return this.$refs.funnelFooter.removeLoader(); return this.$refs.navbar.removeLoader();
} }
// If the registration zip code is serviceable and nothing was entered for the service zip code // If the registration zip code is serviceable and nothing was entered for the service zip code
@ -367,26 +367,26 @@ export default {
}, },
watch: { watch: {
licensePlate() { licensePlate() {
this.$refs.funnelFooter.updateButtonText( this.$refs.navbar.updateButtonText(
this.getCmsContent("FunnelFooterWidget", "ForwardButtonText") this.getCmsContent("FunnelFooterWidget", "ForwardButtonText")
); );
}, },
registrationZipCode() { registrationZipCode() {
this.$refs.funnelFooter.updateButtonText( this.$refs.navbar.updateButtonText(
this.getCmsContent("FunnelFooterWidget", "ForwardButtonText") this.getCmsContent("FunnelFooterWidget", "ForwardButtonText")
); );
}, },
serviceZipCode() { serviceZipCode() {
// If they modify the service zip code, then hide the error message. // If they modify the service zip code, then hide the error message.
this.displayNonServiceableZipAlert = false; this.displayNonServiceableZipAlert = false;
this.$refs.funnelFooter.updateButtonText( this.$refs.navbar.updateButtonText(
this.getCmsContent("FunnelFooterWidget", "ForwardButtonText") this.getCmsContent("FunnelFooterWidget", "ForwardButtonText")
); );
}, },
}, },
components: { components: {
funnelHeader, funnelHeader,
funnelFooter, navbar,
vehicleBanner, vehicleBanner,
funnelSubHeader, funnelSubHeader,
textboxQuestion, textboxQuestion,

View file

@ -6,7 +6,7 @@
<funnelSubHeader class="mb-5" cmsWidgetName="FunnelSubHeaderWidget" leftAlignHeader /> <funnelSubHeader class="mb-5" cmsWidgetName="FunnelSubHeaderWidget" leftAlignHeader />
<hr class="mt-0" /> <hr class="mt-0" />
<div class="fade-on-route-transition sub-container make-tall"> <div class="fade-on-route-transition sub-container make-tall">
<funnel-footer <navbar
cmsWidgetName="FunnelFooterWidget" cmsWidgetName="FunnelFooterWidget"
:isForwardActionDisabled="!meta.valid" :isForwardActionDisabled="!meta.valid"
:isBackButtonHidden="shouldHideBackButton" :isBackButtonHidden="shouldHideBackButton"
@ -20,7 +20,7 @@
<script> <script>
// Components // Components
import funnelHeader from "@/fmg-components/funnel-header/funnel-header"; import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
import funnelFooter from "@/fmg-components/funnel-footer/funnel-footer"; 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 { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
@ -63,7 +63,7 @@ export default {
}, },
components: { components: {
funnelHeader, funnelHeader,
funnelFooter, navbar,
funnelSubHeader, funnelSubHeader,
Form, Form,
}, },

View file

@ -1,19 +1,28 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }"> <Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
<div class="page-container-grouped-styles"> <loadingModal ref="loadingModal" />
<loadingModal ref="loadingModal" /> <div class="container-fluid">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <div class="row">
<vehicleBanner <div class="col">
cmsWidgetName="VehicleBannerWidget" <funnelHeader cmsWidgetName="FunnelHeaderWidget" />
:displayGenericVehicleImage="false" /> </div>
<funnelSubHeader class="mb-5" cmsWidgetName="FunnelSubHeaderWidget" /> </div>
<div class="fade-on-route-transition sub-container make-tall"> <div class="row">
<cashOrInsuranceQuestion <div class="col-md-2 col-lg-3">&nbsp;</div>
<div class="col-md-8 col-lg-6">
<vehicleBanner
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="false" />
<funnelSubHeader class="mb-5" cmsWidgetName="FunnelSubHeaderWidget" />
<cashOrInsuranceQuestion
ref="cashOrInsurance" ref="cashOrInsurance"
cmsWidgetName="CashOrInsuranceQuestionWidget" cmsWidgetName="CashOrInsuranceQuestionWidget"
v-model="isInsuranceSelected" v-model="isInsuranceSelected"
groupName="CashOrInsuranceQuestion" /> groupName="CashOrInsuranceQuestion" />
<servicePackageQuestion
<servicePackageQuestion
ref="servicePackage" ref="servicePackage"
cashCmsWidgetName="CashServicePackageQuestionWidget" cashCmsWidgetName="CashServicePackageQuestionWidget"
insuranceCmsWidgetName="InsuranceServicePackageQuestionWidget" insuranceCmsWidgetName="InsuranceServicePackageQuestionWidget"
@ -25,21 +34,25 @@
validationRules="option-required" validationRules="option-required"
isRequired /> isRequired />
<textBlock <textBlock
cmsWidgetName="quoteDisclaimer" cmsWidgetName="quoteDisclaimer"
justifyText="left" justifyText="left"
typeStyle="caption" typeStyle="caption"
class="mt-4" /> class="mt-4" />
<contentGroupModal ref="RainDefenseModal" cmsWidgetName="RainDefenseModal" />
<contentGroupModal ref="FrontWiperModal" cmsWidgetName="FrontWiperModal" /> <contentGroupModal ref="RainDefenseModal" cmsWidgetName="RainDefenseModal" />
<contentGroupModal ref="RearWiperModal" cmsWidgetName="RearWiperModal" /> <contentGroupModal ref="FrontWiperModal" cmsWidgetName="FrontWiperModal" />
<contentGroupModal ref="RecalModal" cmsWidgetName="RecalModal" /> <contentGroupModal ref="RearWiperModal" cmsWidgetName="RearWiperModal" />
<funnel-footer <contentGroupModal ref="RecalModal" cmsWidgetName="RecalModal" />
<navbar
cmsWidgetName="FunnelFooterWidget" cmsWidgetName="FunnelFooterWidget"
:isForwardActionDisabled="!meta.valid" :isForwardActionDisabled="!meta.valid"
:isBackButtonHidden="shouldHideBackButton" :isBackButtonHidden="shouldHideBackButton"
@back-clicked="backButtonAction" @back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" /> @ForwardClicked="forwardButtonAction" />
</div>
<div class="col-md-2 col-lg-3">&nbsp;</div>
</div> </div>
</div> </div>
</Form> </Form>
@ -48,7 +61,7 @@
<script> <script>
// Components // Components
import funnelHeader from "@/fmg-components/funnel-header/funnel-header"; import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
import funnelFooter from "@/fmg-components/funnel-footer/funnel-footer"; import navbar from "@/fmg-components/nav-bar/nav-bar";
import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner"; import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner";
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header"; import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
import cashOrInsuranceQuestion from "./cash-or-insurance-question/cash-or-insurance-question"; import cashOrInsuranceQuestion from "./cash-or-insurance-question/cash-or-insurance-question";
@ -104,8 +117,8 @@ export default {
const resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
const clonedGlassParts = store.getters.order.lineItems.glassParts const clonedGlassParts = store.getters.order.lineItems.glassParts
? JSON.parse(JSON.stringify(store.getters.order.lineItems.glassParts)) ? JSON.parse(JSON.stringify(store.getters.order.lineItems.glassParts))
: []; : [];
const availableLineItems = [ const availableLineItems = [
resultMap.rainDefense, resultMap.rainDefense,
...resultMap.supportingItems, ...resultMap.supportingItems,
@ -156,101 +169,101 @@ export default {
(store.getters.order.damage.isRepair || (store.getters.order.damage.isRepair ||
(store.getters.order.lineItems?.glassParts != null && (store.getters.order.lineItems?.glassParts != null &&
store.getters.order.lineItems.glassParts.length > 0)) && store.getters.order.lineItems.glassParts.length > 0)) &&
store.getters.order.referralNumber?.length !== 6 store.getters.order.referralNumber?.length !== 6
); );
}, },
getDefaultIsInsuranceSelectedValue(availableLineItems) { getDefaultIsInsuranceSelectedValue(availableLineItems) {
const serviceLocationState = store.getters.order.serviceLocation.state; const serviceLocationState = store.getters.order.serviceLocation.state;
// Override if coming back from QuoteDetails. Remove override after Quote release // Override if coming back from QuoteDetails. Remove override after Quote release
const isInsuranceOverrideValue = this.$route.query?.isInsurance; const isInsuranceOverrideValue = this.$route.query?.isInsurance;
const defaultIsInsuranceSelectedValue = this.$store.getters.order.payment.isInsurance; const defaultIsInsuranceSelectedValue = this.$store.getters.order.payment.isInsurance;
//Default to Insurance Tab if user Service zip is from certain States //Default to Insurance Tab if user Service zip is from certain States
if ( if (
serviceLocationState != null && serviceLocationState != null &&
payWithInsuranceStates.find((item) => item === serviceLocationState) payWithInsuranceStates.find((item) => item === serviceLocationState)
) )
return true; return true;
else if (isInsuranceOverrideValue != null) { else if (isInsuranceOverrideValue != null) {
return isInsuranceOverrideValue == "true"; return isInsuranceOverrideValue == "true";
} else if (defaultIsInsuranceSelectedValue != null) { } else if (defaultIsInsuranceSelectedValue != null) {
return defaultIsInsuranceSelectedValue; return defaultIsInsuranceSelectedValue;
} else { } else {
return availableLineItems return availableLineItems
? baseMixin.methods.getTierOnePackagePrice( ? baseMixin.methods.getTierOnePackagePrice(
baseMixin.methods.filterOutFees(availableLineItems) baseMixin.methods.filterOutFees(availableLineItems)
) > 300 ) > 300
: null; : null;
} }
}, },
vapsItemsSelectedAction(vapsItemsSelected) { vapsItemsSelectedAction(vapsItemsSelected) {
this.selectedVaps = vapsItemsSelected; this.selectedVaps = vapsItemsSelected;
}, },
backButtonAction() { backButtonAction() {
vehicleQuestionsMixin.methods.navigateBack(this); vehicleQuestionsMixin.methods.navigateBack(this);
}, },
forwardButtonAction() { forwardButtonAction() {
this.dispatchStoreAction( this.dispatchStoreAction(
this.storeActions.SAVE_PAYMENT_TYPE, this.storeActions.SAVE_PAYMENT_TYPE,
this.isInsuranceSelected, this.isInsuranceSelected,
false false
); );
if (!this.isInsuranceSelected) { if (!this.isInsuranceSelected) {
this.dispatchStoreAction( this.dispatchStoreAction(
this.storeActions.SAVE_PARENT_ACCOUNT_NUMBER, this.storeActions.SAVE_PARENT_ACCOUNT_NUMBER,
applicationConfig.CASH_PARENT_ACCOUNT_NUMBER, applicationConfig.CASH_PARENT_ACCOUNT_NUMBER,
false false
); );
} }
if ( if (
this.$store.getters.order.payment.parentAccountNumber != this.$store.getters.order.payment.parentAccountNumber !=
applicationConfig.CASH_PARENT_ACCOUNT_NUMBER applicationConfig.CASH_PARENT_ACCOUNT_NUMBER
) { ) {
this.supportingItems = this.filterOutFees(this.supportingItems); this.supportingItems = this.filterOutFees(this.supportingItems);
} }
if (this.pricedGlassParts.length > 0) { if (this.pricedGlassParts.length > 0) {
this.dispatchStoreAction( this.dispatchStoreAction(
this.storeActions.SAVE_GLASS_PART_PRICES, this.storeActions.SAVE_GLASS_PART_PRICES,
this.pricedGlassParts, this.pricedGlassParts,
false false
); );
} }
this.dispatchStoreAction(this.storeActions.SAVE_VAPS, this.selectedVaps, false); this.dispatchStoreAction(this.storeActions.SAVE_VAPS, this.selectedVaps, false);
const payment = this.$store.getters.payment; const payment = this.$store.getters.payment;
if (payment.isInsurance) { if (payment.isInsurance) {
navigateToHeritageFunnel({ navigateToHeritageFunnel({
shouldSaveSession: true, shouldSaveSession: true,
loadingModal: this.$refs.loadingModal, loadingModal: this.$refs.loadingModal,
}); });
} else { } else {
this.$router.navigateWithSaving( this.$router.navigateWithSaving(
this.navigationScenarios.CLICKED_FORWARD_WITH_CASH, this.navigationScenarios.CLICKED_FORWARD_WITH_CASH,
this.$route this.$route
); );
} }
}, },
}, },
components: { components: {
funnelHeader, funnelHeader,
funnelFooter, navbar,
vehicleBanner, vehicleBanner,
funnelSubHeader, funnelSubHeader,
Form, Form,
textBlock, textBlock,
cashOrInsuranceQuestion, cashOrInsuranceQuestion,
servicePackageQuestion, servicePackageQuestion,
contentGroupModal, contentGroupModal,
loadingModal, loadingModal,
}, },
}; };
</script> </script>
<style scoped> <style scoped>
.text-block { .text-block {
display: block; display: block;
} }
</style> </style>

View file

@ -1,105 +1,116 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm"> <Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm">
<!-- When customer-details is added: v-slot="{ meta }" --> <!-- When customer-details is added: v-slot="{ meta }" -->
<div class="page-container-grouped-styles"> <div class="container-fluid">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <div class="row">
<vehicleBanner <div class="col">
cmsWidgetName="VehicleBannerWidget" <funnelHeader cmsWidgetName="FunnelHeaderWidget" />
:displayGenericVehicleImage="false" /> </div>
<textBlock
:customText="subHeaderTitle"
typeStyle="h5"
justifyText="center"
margin="mt-1"
class="dark-header" />
<textBlock
:customText="subHeaderBody"
typeStyle="body"
justifyText="left"
margin="mt-0 mb-2" />
<buttonMain
ref="buttonMain"
isPrimary
:buttonText="forwardButtonText"
loaderColor="white"
class="mb-2"
@click-event="forwardButtonAction" />
<div>
<hr />
</div> </div>
<div class="row">
<div class="col-md-2 col-lg-3">&nbsp;</div>
<div class="col-md-8 col-lg-6">
<vehicleBanner
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="false" />
<textBlock <textBlock
customText="Appointment Details" :customText="subHeaderTitle"
typeStyle="label bold" typeStyle="h5"
justifyText="justify-text-left" justifyText="center"
margin="mt-0" margin="mt-1"
class="dark-header" /> class="dark-header" />
<textBlock
:customText="subHeaderBody"
typeStyle="body"
justifyText="left"
margin="mt-0 mb-2" />
<div class="px-4"> <buttonMain
<vehicleReview ref="buttonMain"
cmsWidgetName="VehicleReviewWidget" isPrimary
:vehicle="vehicleInfo" :buttonText="forwardButtonText"
@edit-clicked="editVehicle" /> loaderColor="white"
class="mb-2"
@click-event="forwardButtonAction" />
<hr class="my-0" /> <div>
<hr />
</div>
<damageReview <textBlock
cmsWidgetName="DamageReviewWidget" customText="Appointment Details"
damageLocationsWidgetName="DamageLocationsWidget" typeStyle="label bold"
:damage="damageInfo" justifyText="justify-text-left"
@edit-clicked="editDamage" /> margin="mt-0"
class="dark-header" />
<hr class="my-0" /> <div class="px-4">
<vehicleReview
cmsWidgetName="VehicleReviewWidget"
:vehicle="vehicleInfo"
@edit-clicked="editVehicle" />
<servicePackageReview <hr class="my-0" />
ref="servicePackageReview"
servicePackageOptionsCmsName="ServicePackageTitle"
defaultPackageItemsCmsName="DefaultPackageItemDescriptions"
vapsItemsCmsName="VapsItemDescriptions"
:damage="damageInfo"
:lineItems="lineItems"
@edit-clicked="editServicePackage" />
<hr class="my-0" /> <damageReview
cmsWidgetName="DamageReviewWidget"
damageLocationsWidgetName="DamageLocationsWidget"
:damage="damageInfo"
@edit-clicked="editDamage" />
<serviceLocationReview <hr class="my-0" />
cmsWidgetName="ServiceLocationTitleWidget"
:serviceLocation="serviceLocationInfo"
@edit-clicked="editServiceLocation" />
<hr class="my-0" /> <servicePackageReview
ref="servicePackageReview"
servicePackageOptionsCmsName="ServicePackageTitle"
defaultPackageItemsCmsName="DefaultPackageItemDescriptions"
vapsItemsCmsName="VapsItemDescriptions"
:damage="damageInfo"
:lineItems="lineItems"
@edit-clicked="editServicePackage" />
<scheduleReview <hr class="my-0" />
cmsWidgetName="ScheduleWidget"
:appointmentType="appointmentType"
@edit-clicked="editSchedule" />
<hr class="my-0" /> <serviceLocationReview
cmsWidgetName="ServiceLocationTitleWidget"
:serviceLocation="serviceLocationInfo"
@edit-clicked="editServiceLocation" />
<customerReview <hr class="my-0" />
cmsWidgetName="CustomerReviewWidget"
:customer="customerInfo" <scheduleReview
@edit-clicked="editCustomerDetails" /> cmsWidgetName="ScheduleWidget"
:appointmentType="appointmentType"
@edit-clicked="editSchedule" />
<hr class="my-0" />
<customerReview
cmsWidgetName="CustomerReviewWidget"
:customer="customerInfo"
@edit-clicked="editCustomerDetails" />
</div>
<div>
<hr class="my-0" />
</div>
<navbar
cmsWidgetName="FunnelFooterWidget"
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" />
</div>
<div class="col-md-2 col-lg-3">&nbsp;</div>
</div>
</div> </div>
<div>
<hr class="my-0" />
</div>
<funnelFooter
cmsWidgetName="FunnelFooterWidget"
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" />
</div>
</Form> </Form>
</template> </template>
<script> <script>
import funnelHeader from "@/fmg-components/funnel-header/funnel-header"; import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
import funnelFooter from "@/fmg-components/funnel-footer/funnel-footer"; import navbar from "@/fmg-components/nav-bar/nav-bar";
import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner"; import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner";
import buttonMain from "@/ux-components/button-main/button-main"; import buttonMain from "@/ux-components/button-main/button-main";
import textBlock from "@/digital-components/text-block/text-block"; import textBlock from "@/digital-components/text-block/text-block";
@ -292,7 +303,7 @@ export default {
}, },
components: { components: {
funnelHeader, funnelHeader,
funnelFooter, navbar,
vehicleBanner, vehicleBanner,
buttonMain, buttonMain,
textBlock, textBlock,

View file

@ -623,7 +623,7 @@ function setupMocks({ customMountOptions }) {
wrapper.vm.setCmsContent = jest.fn(); wrapper.vm.setCmsContent = jest.fn();
wrapper.vm.$refs.datePicker.initializeComponent = jest.fn(); wrapper.vm.$refs.datePicker.initializeComponent = jest.fn();
wrapper.vm.$refs.locationAlerts.initializeComponent = jest.fn(); wrapper.vm.$refs.locationAlerts.initializeComponent = jest.fn();
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); wrapper.vm.$refs.navbar.updateButtonText = jest.fn();
wrapper.vm.$refs.timeSlotModalQuestion.openModal = jest.fn(); wrapper.vm.$refs.timeSlotModalQuestion.openModal = jest.fn();
return { wrapper }; return { wrapper };

View file

@ -1,8 +1,15 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }"> <Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
<div class="page-container-grouped-styles"> <loadingModal ref="loadingModal" />
<loadingModal ref="loadingModal" /> <div class="container-fluid">
<div class="row">
<div class="col">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <funnelHeader cmsWidgetName="FunnelHeaderWidget" />
</div>
</div>
<div class="row">
<div class="col-md-2 col-lg-3">&nbsp;</div>
<div class="col-md-8 col-lg-6">
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" class="mt-5" /> <funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" class="mt-5" />
<template v-if="ChangeShopLink.length"> <template v-if="ChangeShopLink.length">
<textBlock <textBlock
@ -39,20 +46,24 @@
:estimatedServiceMinutesMaximum="selectableDatesData.estimatedServiceMinutesMaximum" :estimatedServiceMinutesMaximum="selectableDatesData.estimatedServiceMinutesMaximum"
@time-slot-modal-closed="timeSlotModalClosed" @time-slot-modal-closed="timeSlotModalClosed"
validationRules="time-slot-selection-required" /> validationRules="time-slot-selection-required" />
<funnel-footer <navbar
cmsWidgetName="FunnelFooterWidget" cmsWidgetName="FunnelFooterWidget"
ref="funnelFooter" ref="navbar"
:isForwardActionDisabled="!meta.valid" :isForwardActionDisabled="!meta.valid"
@back-clicked="backButtonAction" @back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" /> @ForwardClicked="forwardButtonAction" />
</div>
<div class="col-md-2 col-lg-3">&nbsp;</div>
</div> </div>
</Form> </div>
</Form>
</template> </template>
<script> <script>
// Components // Components
import funnelHeader from "@/fmg-components/funnel-header/funnel-header"; import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
import funnelFooter from "@/fmg-components/funnel-footer/funnel-footer"; import navbar from "@/fmg-components/nav-bar/nav-bar";
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header"; import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue"; import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
import { Form, defineRule } from "vee-validate"; import { Form, defineRule } from "vee-validate";
@ -69,9 +80,9 @@ import { settleAllPromises } from "@/helpers/layout-helper";
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper"; import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
import { splitCopyOnCMSPlaceHolder } from "@/helpers/cms-content-helper"; import { splitCopyOnCMSPlaceHolder } from "@/helpers/cms-content-helper";
import { import {
calcDaysBetweenDates, calcDaysBetweenDates,
convertDateStringToDate, convertDateStringToDate,
sumDateString, sumDateString,
} from "@/layouts/schedule/helpers/schedule-helper"; } from "@/layouts/schedule/helpers/schedule-helper";
import { AppointmentTypeStrings, PREMIUM_FEE_PART_TYPE } from "@/constants/schedule-constants"; import { AppointmentTypeStrings, PREMIUM_FEE_PART_TYPE } from "@/constants/schedule-constants";
import { errorMessages } from "@/constants/error-messages"; import { errorMessages } from "@/constants/error-messages";
@ -81,431 +92,431 @@ import store from "@/store";
// DEFINE VALIDATION RULES // DEFINE VALIDATION RULES
defineRule("date-required", required(errorMessages.DATE_REQUIRED)); defineRule("date-required", required(errorMessages.DATE_REQUIRED));
defineRule("time-slot-selection-required", (value) => { defineRule("time-slot-selection-required", (value) => {
if (value?.timeSlot?.routeCode == null) { if (value?.timeSlot?.routeCode == null) {
return errorMessages.DATE_REQUIRED; return errorMessages.DATE_REQUIRED;
} }
return true; return true;
}); });
// Define constants // Define constants
const TIME_SLOTS_CALL_DAYS_LIMIT = 34; // needs to be 34 for API limits (35 does not consistently work) const TIME_SLOTS_CALL_DAYS_LIMIT = 34; // needs to be 34 for API limits (35 does not consistently work)
const getAvailableDates = async ( const getAvailableDates = async (
startDateString, startDateString,
endDateString, endDateString,
appointmentType, appointmentType,
providerNumber providerNumber
) => { ) => {
const apiEndDateLimit = sumDateString(startDateString, TIME_SLOTS_CALL_DAYS_LIMIT); const apiEndDateLimit = sumDateString(startDateString, TIME_SLOTS_CALL_DAYS_LIMIT);
const difference = calcDaysBetweenDates(startDateString, endDateString); const difference = calcDaysBetweenDates(startDateString, endDateString);
const apiCallsCount = Math.ceil(difference / TIME_SLOTS_CALL_DAYS_LIMIT); const apiCallsCount = Math.ceil(difference / TIME_SLOTS_CALL_DAYS_LIMIT);
const storeActionConfigs = []; const storeActionConfigs = [];
const timeSlotsData = {}; const timeSlotsData = {};
timeSlotsData.days = []; timeSlotsData.days = [];
let apiStartDate = startDateString; let apiStartDate = startDateString;
let apiEndDate = endDateString; let apiEndDate = endDateString;
for (let i = 1; i <= apiCallsCount; i++) { for (let i = 1; i <= apiCallsCount; i++) {
let storeActionConfig; let storeActionConfig;
if (i > 1) { if (i > 1) {
apiStartDate = sumDateString(apiEndDate, 1); apiStartDate = sumDateString(apiEndDate, 1);
apiEndDate = sumDateString(apiStartDate, TIME_SLOTS_CALL_DAYS_LIMIT); apiEndDate = sumDateString(apiStartDate, TIME_SLOTS_CALL_DAYS_LIMIT);
if (i === apiCallsCount) { if (i === apiCallsCount) {
apiEndDate = endDateString; apiEndDate = endDateString;
}
} else {
if (apiEndDate > apiEndDateLimit) {
apiEndDate = apiEndDateLimit;
}
} }
} else {
if (appointmentType === AppointmentTypeStrings.MOBILE) { if (apiEndDate > apiEndDateLimit) {
storeActionConfig = { apiEndDate = apiEndDateLimit;
storeAction: storeActions.GET_MOBILE_TIME_SLOTS,
payload: {
startDate: apiStartDate,
endDate: apiEndDate,
},
};
} else {
storeActionConfig = {
storeAction: storeActions.GET_SHOP_TIME_SLOTS,
payload: {
startDate: apiStartDate,
endDate: apiEndDate,
shopAppointmentType: appointmentType,
providerNumber: providerNumber,
},
};
} }
if (apiStartDate < apiEndDate) storeActionConfigs.push(storeActionConfig);
} }
const timeSlotsResponsesData = { if (appointmentType === AppointmentTypeStrings.MOBILE) {
days: [], storeActionConfig = {
}; storeAction: storeActions.GET_MOBILE_TIME_SLOTS,
payload: {
function compareDayStrings(a, b) { startDate: apiStartDate,
if (a.date < b.date) return -1; endDate: apiEndDate,
if (a.date > b.date) return 1; },
return 0; };
} else {
storeActionConfig = {
storeAction: storeActions.GET_SHOP_TIME_SLOTS,
payload: {
startDate: apiStartDate,
endDate: apiEndDate,
shopAppointmentType: appointmentType,
providerNumber: providerNumber,
},
};
} }
if (apiStartDate < apiEndDate) storeActionConfigs.push(storeActionConfig);
}
const makeParallelCalls = async () => { const timeSlotsResponsesData = {
await Promise.all( days: [],
storeActionConfigs.map(async (storeAction) => { };
const timeSlotsResponse = await baseMixin.methods.dispatchStoreAction(
storeAction.storeAction,
storeAction.payload,
false
);
timeSlotsResponsesData.estimatedServiceMinutesMinimum =
timeSlotsResponse.data.estimatedServiceMinutesMinimum;
timeSlotsResponsesData.estimatedServiceMinutesMaximum =
timeSlotsResponse.data.estimatedServiceMinutesMaximum;
timeSlotsResponsesData.days = [
...timeSlotsResponsesData.days,
...timeSlotsResponse.data.days,
];
})
);
};
return makeParallelCalls().then(() => { function compareDayStrings(a, b) {
// sort days chronologically if (a.date < b.date) return -1;
timeSlotsResponsesData.days.sort(compareDayStrings); if (a.date > b.date) return 1;
return timeSlotsResponsesData; return 0;
}); }
const makeParallelCalls = async () => {
await Promise.all(
storeActionConfigs.map(async (storeAction) => {
const timeSlotsResponse = await baseMixin.methods.dispatchStoreAction(
storeAction.storeAction,
storeAction.payload,
false
);
timeSlotsResponsesData.estimatedServiceMinutesMinimum =
timeSlotsResponse.data.estimatedServiceMinutesMinimum;
timeSlotsResponsesData.estimatedServiceMinutesMaximum =
timeSlotsResponse.data.estimatedServiceMinutesMaximum;
timeSlotsResponsesData.days = [
...timeSlotsResponsesData.days,
...timeSlotsResponse.data.days,
];
})
);
};
return makeParallelCalls().then(() => {
// sort days chronologically
timeSlotsResponsesData.days.sort(compareDayStrings);
return timeSlotsResponsesData;
});
}; };
export default { export default {
name: "schedule", name: "schedule",
data() { data() {
return { return {
selectedDate: this.getSelectedDate(), selectedDate: this.getSelectedDate(),
selectedTimeSlotInfo: this.getSelectedTimeSlotInfo(), selectedTimeSlotInfo: this.getSelectedTimeSlotInfo(),
selectableDatesData: [], selectableDatesData: [],
mobilePremiumAppointmentFee: null, mobilePremiumAppointmentFee: null,
}; };
}, },
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
// Call APIs // Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage); const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
let preSelectedDate = await store.getters.order.schedule.date; let preSelectedDate = await store.getters.order.schedule.date;
if (!preSelectedDate || preSelectedDate.startTime === null) { if (!preSelectedDate || preSelectedDate.startTime === null) {
preSelectedDate = null; preSelectedDate = null;
} }
const datePickerInitialDataPromise = await datePicker.methods.loadInitialData({ const datePickerInitialDataPromise = await datePicker.methods.loadInitialData({
// setup config options for date-picker // setup config options for date-picker
selectableDatesSetting: "custom", selectableDatesSetting: "custom",
initialViewRowsToShow: 5, initialViewRowsToShow: 5,
customSelectableDatesCallback: getAvailableDates, customSelectableDatesCallback: getAvailableDates,
preSelectedDate: preSelectedDate, preSelectedDate: preSelectedDate,
}); });
const premiumFeePromise = baseMixin.methods.dispatchStoreAction( const premiumFeePromise = baseMixin.methods.dispatchStoreAction(
storeActions.GET_MOBILE_PREMIUM_FEE storeActions.GET_MOBILE_PREMIUM_FEE
); );
const premiumFeeWithPricePromise = premiumFeePromise.then((result) => { const premiumFeeWithPricePromise = premiumFeePromise.then((result) => {
if (result.data) { if (result.data) {
return baseMixin.methods.dispatchStoreAction( return baseMixin.methods.dispatchStoreAction(
storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA, storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA,
{ {
availableLineItems: [result.data], availableLineItems: [result.data],
}, },
false
);
} else {
return result.data;
}
});
const alertReasonsPromise = locationAlerts.methods.loadInitialData(
store.getters.order.serviceLocation.zipCodeCtu,
store.getters.order.serviceLocation.provider?.address?.zipCodeCtu
);
// Settle promises and get results
const promiseResultMap = [
{
resultKey: "cmsContent",
promise: cmsContentPromise,
},
{
resultKey: "alertReasons",
promise: alertReasonsPromise,
},
{
resultKey: "datePickerInitialData",
promise: datePickerInitialDataPromise,
},
{
resultKey: "premiumFeeWithPrice",
promise: premiumFeeWithPricePromise,
},
];
const resultMap = await settleAllPromises(promiseResultMap);
// Call the "next" function to complete the transition to this page.
next((vm) => {
vm.setCmsContent(resultMap.cmsContent);
vm.$refs.datePicker.initializeComponent(resultMap.datePickerInitialData);
vm.$refs.locationAlerts.initializeComponent(resultMap.alertReasons);
vm.selectableDatesData = resultMap.datePickerInitialData.initialShopTimeSlotsResponse;
vm.mobilePremiumAppointmentFee = resultMap.premiumFeeWithPrice
? resultMap.premiumFeeWithPrice[0]
: null;
vm.updateFooterButtonText(vm.selectedTimeSlotInfo);
});
},
computed: {
ChangeShopLinkText() {
return this.getCmsContent("ChangeShopLink", "Text");
},
ChangeShopLink() {
// Splits content when brackets are found in text so that text can be looped through and router-link can be injected when needed
return this.splitCopyOnCMSPlaceHolder(this.ChangeShopLinkText);
},
appointmentType() {
return this.$store.getters.order.serviceLocation.appointmentType;
},
timeSlotsForSelectedDate() {
if (!this.selectedDate) {
return null;
}
return this.selectableDatesData.days?.find(
(selectableDate) => selectableDate.date === this.selectedDate
);
},
},
methods: {
splitCopyOnCMSPlaceHolder,
arePagePrerequisitesValid() {
const serviceLocation = store.getters.order.serviceLocation;
const serviceLocationPreReqs =
serviceLocation.zipCode &&
serviceLocation.zipCodeCtu &&
serviceLocation.appointmentType &&
(serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE ||
serviceLocation.provider.providerNumber);
const paymentInfo = store.getters.payment.isInsurance !== null;
const supportingItems = store.getters.lineItems.supportingItems !== null;
const damageInfo =
store.getters.order.damage.isRepair ||
(store.getters.order.lineItems?.glassParts != null &&
store.getters.order.lineItems.glassParts.length > 0);
return serviceLocationPreReqs && paymentInfo && supportingItems && damageInfo;
},
async getAvailableDatesMethod(startDate, endDate) {
const newShopTimeSlots = await getAvailableDates(
startDate,
endDate,
this.appointmentType,
this.$store.getters.order.serviceLocation.provider.providerNumber
);
// ADD API CALL RESULTS TO EXISTING DATE DATA
this.selectableDatesData.days = this.selectableDatesData.days.concat(
newShopTimeSlots.days
);
return newShopTimeSlots;
},
getAvailableDates,
getServiceZipCtuCodeFromStore() {
return store.getters.order.serviceLocation.zipCodeCtu;
},
openInshopTimeSlotsModal() {
this.$refs["timeSlotModalQuestion"].openModal();
},
getSelectedDate() {
return store.getters.order.schedule.date;
},
getSelectedTimeSlotInfo() {
const supportingItems = this.getSupportingItems();
const isPremiumAppointment =
!!supportingItems.filter((lineItem) => lineItem.partType === PREMIUM_FEE_PART_TYPE)
.length > 0;
const selectedTimeSlotInfo = {
timeSlot: store.getters.order.schedule,
isPremiumAppointment: isPremiumAppointment,
};
return selectedTimeSlotInfo;
},
getSupportingItems() {
return store.getters.lineItems.supportingItems;
},
timeSlotModalClosed() {
// Clear the selectedDate if no timeSlot has been selected
if (this.selectedTimeSlotInfo.timeSlot.routeCode == null) {
this.selectedDate = null;
}
},
updateFooterButtonText(timeSlotInfo) {
let funnelFooterButtonText;
if (!timeSlotInfo || !timeSlotInfo.timeSlot.date) {
funnelFooterButtonText = "Continue";
} else {
funnelFooterButtonText = `Select ${this.convertSelectedDateToShortMonthAndDay(
timeSlotInfo.timeSlot.date
)}`;
if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) {
funnelFooterButtonText += ` at ${this.getDisplayTextForMilitaryTime(
timeSlotInfo.timeSlot.startTime
)}`;
} else if (
this.appointmentType === AppointmentTypeStrings.MOBILE &&
!timeSlotInfo.isPremiumAppointment
) {
funnelFooterButtonText += ` at ${this.getDisplayTextForMilitaryTime(
timeSlotInfo.timeSlot.startTime,
true
)} - ${this.getDisplayTextForMilitaryTime(
timeSlotInfo.timeSlot.endTime,
true
)}`;
}
}
this.$refs.funnelFooter.updateButtonText(funnelFooterButtonText);
},
convertSelectedDateToShortMonthAndDay(selectedDate) {
// This conversion ensures we don't get get GMT induced date changes
const dateObject = convertDateStringToDate(selectedDate);
// Ex: April 25
return dateObject.toLocaleDateString("en-us", { month: "short", day: "numeric" });
},
getDisplayTextForMilitaryTime(militaryTimeInput, shouldTrimMinutesIfEmpty = false) {
// Expected input: "HH:MM"
let hours = parseInt(militaryTimeInput.split(":")[0]);
const minutes = militaryTimeInput.split(":")[1];
const meridianNotation = hours > 11 ? "PM" : "AM";
if (hours > 12) {
hours -= 12;
}
if (shouldTrimMinutesIfEmpty && minutes === "00") {
return `${hours} ${meridianNotation}`;
} else {
return `${hours}:${minutes} ${meridianNotation}`;
}
},
backButtonAction() {
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
},
forwardButtonAction() {
this.updateSupportingItems();
this.dispatchStoreAction(
this.storeActions.SAVE_SCHEDULE,
this.selectedTimeSlotInfo.timeSlot,
false false
); );
} else {
return result.data;
}
});
this.$router.navigateWithSaving(this.navigationScenarios.CLICKED_FORWARD, this.$route); const alertReasonsPromise = locationAlerts.methods.loadInitialData(
store.getters.order.serviceLocation.zipCodeCtu,
store.getters.order.serviceLocation.provider?.address?.zipCodeCtu
);
// Settle promises and get results
const promiseResultMap = [
{
resultKey: "cmsContent",
promise: cmsContentPromise,
}, },
updateSupportingItems() { {
const supportingItems = this.getSupportingItems(); resultKey: "alertReasons",
promise: alertReasonsPromise,
},
{
resultKey: "datePickerInitialData",
promise: datePickerInitialDataPromise,
},
{
resultKey: "premiumFeeWithPrice",
promise: premiumFeeWithPricePromise,
},
];
// if we have a premium fee(early bird), then save/update supporting items const resultMap = await settleAllPromises(promiseResultMap);
if (
// Call the "next" function to complete the transition to this page.
next((vm) => {
vm.setCmsContent(resultMap.cmsContent);
vm.$refs.datePicker.initializeComponent(resultMap.datePickerInitialData);
vm.$refs.locationAlerts.initializeComponent(resultMap.alertReasons);
vm.selectableDatesData = resultMap.datePickerInitialData.initialShopTimeSlotsResponse;
vm.mobilePremiumAppointmentFee = resultMap.premiumFeeWithPrice
? resultMap.premiumFeeWithPrice[0]
: null;
vm.updateFooterButtonText(vm.selectedTimeSlotInfo);
});
},
computed: {
ChangeShopLinkText() {
return this.getCmsContent("ChangeShopLink", "Text");
},
ChangeShopLink() {
// Splits content when brackets are found in text so that text can be looped through and router-link can be injected when needed
return this.splitCopyOnCMSPlaceHolder(this.ChangeShopLinkText);
},
appointmentType() {
return this.$store.getters.order.serviceLocation.appointmentType;
},
timeSlotsForSelectedDate() {
if (!this.selectedDate) {
return null;
}
return this.selectableDatesData.days?.find(
(selectableDate) => selectableDate.date === this.selectedDate
);
},
},
methods: {
splitCopyOnCMSPlaceHolder,
arePagePrerequisitesValid() {
const serviceLocation = store.getters.order.serviceLocation;
const serviceLocationPreReqs =
serviceLocation.zipCode &&
serviceLocation.zipCodeCtu &&
serviceLocation.appointmentType &&
(serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE ||
serviceLocation.provider.providerNumber);
const paymentInfo = store.getters.payment.isInsurance !== null;
const supportingItems = store.getters.lineItems.supportingItems !== null;
const damageInfo =
store.getters.order.damage.isRepair ||
(store.getters.order.lineItems?.glassParts != null &&
store.getters.order.lineItems.glassParts.length > 0);
return serviceLocationPreReqs && paymentInfo && supportingItems && damageInfo;
},
async getAvailableDatesMethod(startDate, endDate) {
const newShopTimeSlots = await getAvailableDates(
startDate,
endDate,
this.appointmentType,
this.$store.getters.order.serviceLocation.provider.providerNumber
);
// ADD API CALL RESULTS TO EXISTING DATE DATA
this.selectableDatesData.days = this.selectableDatesData.days.concat(
newShopTimeSlots.days
);
return newShopTimeSlots;
},
getAvailableDates,
getServiceZipCtuCodeFromStore() {
return store.getters.order.serviceLocation.zipCodeCtu;
},
openInshopTimeSlotsModal() {
this.$refs["timeSlotModalQuestion"].openModal();
},
getSelectedDate() {
return store.getters.order.schedule.date;
},
getSelectedTimeSlotInfo() {
const supportingItems = this.getSupportingItems();
const isPremiumAppointment =
!!supportingItems.filter((lineItem) => lineItem.partType === PREMIUM_FEE_PART_TYPE)
.length > 0;
const selectedTimeSlotInfo = {
timeSlot: store.getters.order.schedule,
isPremiumAppointment: isPremiumAppointment,
};
return selectedTimeSlotInfo;
},
getSupportingItems() {
return store.getters.lineItems.supportingItems;
},
timeSlotModalClosed() {
// Clear the selectedDate if no timeSlot has been selected
if (this.selectedTimeSlotInfo.timeSlot.routeCode == null) {
this.selectedDate = null;
}
},
updateFooterButtonText(timeSlotInfo) {
let navbarButtonText;
if (!timeSlotInfo || !timeSlotInfo.timeSlot.date) {
navbarButtonText = "Continue";
} else {
navbarButtonText = `Select ${this.convertSelectedDateToShortMonthAndDay(
timeSlotInfo.timeSlot.date
)}`;
if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) {
navbarButtonText += ` at ${this.getDisplayTextForMilitaryTime(
timeSlotInfo.timeSlot.startTime
)}`;
} else if (
this.appointmentType === AppointmentTypeStrings.MOBILE && this.appointmentType === AppointmentTypeStrings.MOBILE &&
this.selectedTimeSlotInfo?.isPremiumAppointment !timeSlotInfo.isPremiumAppointment
) { ) {
const earlyBirdIndex = supportingItems.findIndex( navbarButtonText += ` at ${this.getDisplayTextForMilitaryTime(
(item) => item.partType == PREMIUM_FEE_PART_TYPE timeSlotInfo.timeSlot.startTime,
); true
)} - ${this.getDisplayTextForMilitaryTime(
timeSlotInfo.timeSlot.endTime,
true
)}`;
}
}
this.$refs.navbar.updateButtonText(navbarButtonText);
},
convertSelectedDateToShortMonthAndDay(selectedDate) {
// This conversion ensures we don't get get GMT induced date changes
const dateObject = convertDateStringToDate(selectedDate);
// Ex: April 25
return dateObject.toLocaleDateString("en-us", { month: "short", day: "numeric" });
},
getDisplayTextForMilitaryTime(militaryTimeInput, shouldTrimMinutesIfEmpty = false) {
// Expected input: "HH:MM"
let hours = parseInt(militaryTimeInput.split(":")[0]);
const minutes = militaryTimeInput.split(":")[1];
const meridianNotation = hours > 11 ? "PM" : "AM";
if (earlyBirdIndex >= 0) { if (hours > 12) {
supportingItems[earlyBirdIndex].laborAmount = hours -= 12;
this.mobilePremiumAppointmentFee.laborAmount; }
supportingItems[earlyBirdIndex].selingPrice =
this.mobilePremiumAppointmentFee.selingPrice;
supportingItems[earlyBirdIndex].kitPrice =
this.mobilePremiumAppointmentFee.kitPrice;
} else {
supportingItems.push(this.mobilePremiumAppointmentFee);
}
if (shouldTrimMinutesIfEmpty && minutes === "00") {
return `${hours} ${meridianNotation}`;
} else {
return `${hours}:${minutes} ${meridianNotation}`;
}
},
backButtonAction() {
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
},
forwardButtonAction() {
this.updateSupportingItems();
this.dispatchStoreAction(
this.storeActions.SAVE_SCHEDULE,
this.selectedTimeSlotInfo.timeSlot,
false
);
this.$router.navigateWithSaving(this.navigationScenarios.CLICKED_FORWARD, this.$route);
},
updateSupportingItems() {
const supportingItems = this.getSupportingItems();
// if we have a premium fee(early bird), then save/update supporting items
if (
this.appointmentType === AppointmentTypeStrings.MOBILE &&
this.selectedTimeSlotInfo?.isPremiumAppointment
) {
const earlyBirdIndex = supportingItems.findIndex(
(item) => item.partType == PREMIUM_FEE_PART_TYPE
);
if (earlyBirdIndex >= 0) {
supportingItems[earlyBirdIndex].laborAmount =
this.mobilePremiumAppointmentFee.laborAmount;
supportingItems[earlyBirdIndex].selingPrice =
this.mobilePremiumAppointmentFee.selingPrice;
supportingItems[earlyBirdIndex].kitPrice =
this.mobilePremiumAppointmentFee.kitPrice;
} else {
supportingItems.push(this.mobilePremiumAppointmentFee);
}
this.dispatchStoreAction(
this.storeActions.SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING,
supportingItems,
false
);
} else {
// if it's not a mobile and/or premium early bird, then make sure we remove any that may have been added
const removeEarlyBirdIndex = supportingItems.findIndex(
(item) => item.partType == PREMIUM_FEE_PART_TYPE
);
if (removeEarlyBirdIndex >= 0) {
supportingItems.splice(removeEarlyBirdIndex, 1);
this.dispatchStoreAction( this.dispatchStoreAction(
this.storeActions.SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING, this.storeActions.SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING,
supportingItems, supportingItems,
false false
); );
} else {
// if it's not a mobile and/or premium early bird, then make sure we remove any that may have been added
const removeEarlyBirdIndex = supportingItems.findIndex(
(item) => item.partType == PREMIUM_FEE_PART_TYPE
);
if (removeEarlyBirdIndex >= 0) {
supportingItems.splice(removeEarlyBirdIndex, 1);
this.dispatchStoreAction(
this.storeActions.SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING,
supportingItems,
false
);
}
} }
}, }
}, },
watch: { },
selectedDate(newValue, oldValue) { watch: {
// Clear time slot selection if date selected changes selectedDate(newValue, oldValue) {
if (newValue !== oldValue) { // Clear time slot selection if date selected changes
this.selectedTimeSlotInfo = { if (newValue !== oldValue) {
timeSlot: { this.selectedTimeSlotInfo = {
date: null, timeSlot: {
routeCode: null, date: null,
startTime: null, routeCode: null,
endTime: null, startTime: null,
jobMaxMinutes: null, endTime: null,
jobMinMinutes: null, jobMaxMinutes: null,
}, jobMinMinutes: null,
isPremiumAppointment: null, },
}; isPremiumAppointment: null,
} };
}, }
selectedTimeSlotInfo(newValue) {
this.updateFooterButtonText(newValue);
},
}, },
components: { selectedTimeSlotInfo(newValue) {
funnelHeader, this.updateFooterButtonText(newValue);
funnelFooter,
funnelSubHeader,
Form,
loadingModal,
datePicker,
locationAlerts,
timeSlotModalQuestion,
textBlock,
}, },
},
components: {
funnelHeader,
navbar,
funnelSubHeader,
Form,
loadingModal,
datePicker,
locationAlerts,
timeSlotModalQuestion,
textBlock,
},
}; };
</script> </script>
<style lang="scss"> <style lang="scss">
.text-link-small { .text-link-small {
a, a,
.btn-link { .btn-link {
font-size: 0.875rem; font-size: 0.875rem;
line-height: 1.5; line-height: 1.5;
} }
} }
.funnel-sub-header { .funnel-sub-header {
h5.dark-header { h5.dark-header {
margin-bottom: 0.25rem; margin-bottom: 0.25rem;
} }
} }
</style> </style>

View file

@ -1,87 +1,109 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }"> <Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
<div class="page-container-grouped-styles"> <loadingModal ref="loadingModal" />
<loadingModal ref="loadingModal" /> <div class="container-fluid">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <div class="row">
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" class="mt-5" /> <div class="col">
<serviceZipModalQuestion <funnelHeader cmsWidgetName="FunnelHeaderWidget" />
v-model="serviceZipCodeQuestion" </div>
ref="serviceZipCodeQuestion" </div>
:mobileFeePart="mobileFeePart" <div class="row">
@updated-mobile-fee-part="setMobileFeePart" <div class="col-md-2 col-lg-3">&nbsp;</div>
@updated-serviceability="setServiceabilityDetails" <div class="col-md-8 col-lg-6">
@updated-contains-military-base="setContainsMilitaryBase" <funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" class="mt-5" />
linkWidgetName="ServiceZipLinkWidget"
modalWidgetName="ServiceZipModalWidget" <serviceZipModalQuestion
:onZipUpdateCallback="reloadShopData" /> v-model="serviceZipCodeQuestion"
<alert ref="serviceZipCodeQuestion"
ref="alertMilitaryBaseZip" :mobileFeePart="mobileFeePart"
class="my-5" @updated-mobile-fee-part="setMobileFeePart"
cmsWidgetName="AlertMilitaryBaseZipWidget" @updated-serviceability="setServiceabilityDetails"
v-if="displayMilitaryZipAlert" @updated-contains-military-base="setContainsMilitaryBase"
alertClass="alert-warning" /> linkWidgetName="ServiceZipLinkWidget"
<alert modalWidgetName="ServiceZipModalWidget"
ref="alertMobileOnly" :onZipUpdateCallback="reloadShopData" />
class="my-5"
cmsWidgetName="AlertMobileOnlyWidget" <alert
v-if="displayServiceableMobileOnly" ref="alertMilitaryBaseZip"
alertClass="alert-warning" /> class="my-5"
<alert cmsWidgetName="AlertMilitaryBaseZipWidget"
ref="alertRecalNoMobile" v-if="displayMilitaryZipAlert"
class="my-5" alertClass="alert-warning" />
cmsWidgetName="AlertRecalNoMobileWidget"
v-if="displayRecalibrationWarning" <alert
@text-link-clicked="openModalAction" ref="alertMobileOnly"
alertClass="alert-warning" /> class="my-5"
<alert cmsWidgetName="AlertMobileOnlyWidget"
ref="alertInshopOnly" v-if="displayServiceableMobileOnly"
class="my-5" alertClass="alert-warning" />
cmsWidgetName="AlertInshopOnlyWidget"
v-if="displayServiceableInshopOnly" <alert
alertClass="alert-warning" /> ref="alertRecalNoMobile"
<alert class="my-5"
ref="alertNoShops" cmsWidgetName="AlertRecalNoMobileWidget"
class="my-5" v-if="displayRecalibrationWarning"
cmsWidgetName="AlertNoShopsWidget" @text-link-clicked="openModalAction"
v-if="displayNoShopsAlert" alertClass="alert-warning" />
alertClass="alert-warning" />
<appointmentTypeQuestion <alert
v-model="selectedAppointmentType" ref="alertInshopOnly"
v-show="isAppointmentTypeDisplayed" class="my-5"
:isServiceableMobile="isServiceableMobile" cmsWidgetName="AlertInshopOnlyWidget"
:isServiceableInshop="isServiceableInshop" v-if="displayServiceableInshopOnly"
:isDisplayed="isAppointmentTypeDisplayed" alertClass="alert-warning" />
ref="appointmentTypeQuestion"
groupName="appointmentTypeQuestion" <alert
cmsWidgetName="AppointmentTypeQuestionWidget" ref="alertNoShops"
validationRules="option-required" /> class="my-5"
<mobileLocationModalQuestions cmsWidgetName="AlertNoShopsWidget"
customComponentId="mobileLocationQuestions" v-if="displayNoShopsAlert"
v-if="selectedAppointmentType === 'Mobile'" alertClass="alert-warning" />
v-model="mobileLocationQuestions"
:mobileFeePart="mobileFeePart" <appointmentTypeQuestion
@updated-mobile-fee-part="setMobileFeePart" v-model="selectedAppointmentType"
@updated-serviceability="setServiceabilityDetails" v-show="isAppointmentTypeDisplayed"
@updated-contains-military-base="setContainsMilitaryBase" :isServiceableMobile="isServiceableMobile"
validationRules="mobile-location-required" :isServiceableInshop="isServiceableInshop"
ref="mobileLocationQuestions" :isDisplayed="isAppointmentTypeDisplayed"
linkWidgetName="MobileLocationLinkWidget" ref="appointmentTypeQuestion"
modalWidgetName="MobileLocationModalWidget" groupName="appointmentTypeQuestion"
:onZipUpdateCallback="reloadShopData" /> cmsWidgetName="AppointmentTypeQuestionWidget"
<shopQuestion validationRules="option-required" />
ref="shopQuestion"
v-show="isShopQuestionDisplayed" <mobileLocationModalQuestions
v-model="selectedProvider" customComponentId="mobileLocationQuestions"
:selectedAppointmentType="selectedAppointmentType" v-if="selectedAppointmentType === 'Mobile'"
:isDisplayed="isShopQuestionDisplayed" v-model="mobileLocationQuestions"
cmsWidgetName="ShopQuestionWidget" /> :mobileFeePart="mobileFeePart"
<contentGroupModal ref="RecalModal" cmsWidgetName="RecalModal" /> @updated-mobile-fee-part="setMobileFeePart"
<funnel-footer @updated-serviceability="setServiceabilityDetails"
cmsWidgetName="FunnelFooterWidget" @updated-contains-military-base="setContainsMilitaryBase"
ref="funnelFooter" validationRules="mobile-location-required"
:isForwardActionDisabled="!meta.valid || displayNoShopsAlert" ref="mobileLocationQuestions"
@back-clicked="backButtonAction" linkWidgetName="MobileLocationLinkWidget"
@ForwardClicked="forwardButtonAction" /> modalWidgetName="MobileLocationModalWidget"
:onZipUpdateCallback="reloadShopData" />
<shopQuestion
ref="shopQuestion"
v-show="isShopQuestionDisplayed"
v-model="selectedProvider"
:selectedAppointmentType="selectedAppointmentType"
:isDisplayed="isShopQuestionDisplayed"
cmsWidgetName="ShopQuestionWidget" />
<contentGroupModal ref="RecalModal" cmsWidgetName="RecalModal" />
<navbar
cmsWidgetName="FunnelFooterWidget"
ref="navbar"
:isForwardActionDisabled="!meta.valid || displayNoShopsAlert"
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" />
</div>
<div class="col-md-2 col-lg-3">&nbsp;</div>
</div>
</div> </div>
</Form> </Form>
</template> </template>
@ -95,7 +117,7 @@ import appointmentTypeQuestion from "@/layouts/service-location/appointment-type
import shopQuestion from "@/layouts/service-location/shop-question/shop-question"; import shopQuestion from "@/layouts/service-location/shop-question/shop-question";
import funnelHeader from "@/fmg-components/funnel-header/funnel-header"; import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
import funnelFooter from "@/fmg-components/funnel-footer/funnel-footer"; import navbar from "@/fmg-components/nav-bar/nav-bar";
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header"; import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue"; import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
import { Form } from "vee-validate"; import { Form } from "vee-validate";
@ -240,7 +262,7 @@ export default {
set: function (newValue) { set: function (newValue) {
this.streetAddress = newValue.addressQuestions.streetAddress; this.streetAddress = newValue.addressQuestions.streetAddress;
this.apartmentNumberOrBusinessName = this.apartmentNumberOrBusinessName =
newValue.addressQuestions.apartmentNumberOrBusinessName; newValue.addressQuestions.apartmentNumberOrBusinessName;
this.city = newValue.addressQuestions.city; this.city = newValue.addressQuestions.city;
this.state = newValue.addressQuestions.state; this.state = newValue.addressQuestions.state;
this.zipCode = newValue.addressQuestions.zipCode; this.zipCode = newValue.addressQuestions.zipCode;
@ -369,10 +391,10 @@ export default {
setServiceabilityDetails(serviceabilityDetails) { setServiceabilityDetails(serviceabilityDetails) {
this.isGlassServiceableInshop = serviceabilityDetails.isGlassServiceableInshop; this.isGlassServiceableInshop = serviceabilityDetails.isGlassServiceableInshop;
this.isRecalibrationServiceableInshop = this.isRecalibrationServiceableInshop =
serviceabilityDetails.isRecalibrationServiceableInshop; serviceabilityDetails.isRecalibrationServiceableInshop;
this.isGlassServiceableMobile = serviceabilityDetails.isGlassServiceableMobile; this.isGlassServiceableMobile = serviceabilityDetails.isGlassServiceableMobile;
this.isRecalibrationServiceableMobile = this.isRecalibrationServiceableMobile =
serviceabilityDetails.isRecalibrationServiceableMobile; serviceabilityDetails.isRecalibrationServiceableMobile;
}, },
backButtonAction() { backButtonAction() {
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route); this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
@ -456,7 +478,7 @@ export default {
appointmentTypeQuestion, appointmentTypeQuestion,
mobileLocationModalQuestions, mobileLocationModalQuestions,
funnelHeader, funnelHeader,
funnelFooter, navbar,
funnelSubHeader, funnelSubHeader,
Form, Form,
loadingModal, loadingModal,

View file

@ -1,65 +1,81 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }"> <Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
<div class="page-container-grouped-styles"> <div class="container-fluid">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <div class="row">
<vehicleBanner <div class="col">
cmsWidgetName="VehicleBannerWidget" <funnelHeader cmsWidgetName="FunnelHeaderWidget" />
:displayGenericVehicleImage="false" /> </div>
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" /> </div>
<div class="fade-on-route-transition sub-container make-tall"> <div class="row">
<alert <div class="col-md-2 col-lg-3">&nbsp;</div>
ref="vehicleChangeAlert" <div class="col-md-8 col-lg-6">
v-if="shouldDisplayVehicleChangeAlert" <vehicleBanner
class="mt-5 mb-0" cmsWidgetName="VehicleBannerWidget"
cmsWidgetName="VehicleChangeAlert" :displayGenericVehicleImage="false" />
alertClass="alert-warning"
:isDismissible="false" /> <funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
<damageLocationQuestion
ref="damageLocation" <alert
cmsWidgetName="DamageLocationQuestion" ref="vehicleChangeAlert"
v-model="selectedDamageLocations" v-if="shouldDisplayVehicleChangeAlert"
groupName="DamageLocationQuestion" /> class="mt-5 mb-0"
<windshieldOptions cmsWidgetName="VehicleChangeAlert"
ref="windshieldOptions" alertClass="alert-warning"
v-model="selectedWindshieldOptions" :isDismissible="false" />
:hasRepairReplaceConflict="hasRepairReplaceConflict"
:hasSplitSingleConflict="hasSplitSingleConflict" <damageLocationQuestion
:selectedDamageLocations="selectedDamageLocations" /> ref="damageLocation"
<alert cmsWidgetName="DamageLocationQuestion"
v-if="hasRepairReplaceConflict" v-model="selectedDamageLocations"
class="my-5" groupName="DamageLocationQuestion" />
cmsWidgetName="HasReplacementConflict"
alertClass="alert-danger" <windshieldOptions
:isDismissible="false" /> ref="windshieldOptions"
<sideDoorOptions v-model="selectedWindshieldOptions"
ref="sideDoorOptions" :hasRepairReplaceConflict="hasRepairReplaceConflict"
cmsWidgetName="SideDoorSideQuestion" :hasSplitSingleConflict="hasSplitSingleConflict"
groupName="SideDoorSideQuestion" :selectedDamageLocations="selectedDamageLocations" />
v-model="sideDoorOptionsData"
v-show="!hasRepairReplaceConflict" <alert
:selectedDamageLocations="selectedDamageLocations" /> v-if="hasRepairReplaceConflict"
<replaceOptionsQuestion class="my-5"
ref="backGlassOptions" cmsWidgetName="HasReplacementConflict"
cmsWidgetName="RearReplaceOptionsQuestion" alertClass="alert-danger"
:isAvailable="isRearWindowDamageLocation && !hasRepairReplaceConflict" :isDismissible="false" />
v-model="selectedRearReplaceOptions"
groupName="BackGlassReplaceOptionsQuestion" <sideDoorOptions
validationRules="replace-options-required" /> ref="sideDoorOptions"
<funnel-footer cmsWidgetName="SideDoorSideQuestion"
cmsWidgetName="FunnelFooterWidget" groupName="SideDoorSideQuestion"
:isForwardActionDisabled="!meta.valid" v-model="sideDoorOptionsData"
:isBackButtonHidden="shouldHideBackButton" v-show="!hasRepairReplaceConflict"
@back-clicked="backButtonAction" :selectedDamageLocations="selectedDamageLocations" />
@ForwardClicked="forwardButtonAction" />
<replaceOptionsQuestion
ref="backGlassOptions"
cmsWidgetName="RearReplaceOptionsQuestion"
:isAvailable="isRearWindowDamageLocation && !hasRepairReplaceConflict"
v-model="selectedRearReplaceOptions"
groupName="BackGlassReplaceOptionsQuestion"
validationRules="replace-options-required" />
<navbar
cmsWidgetName="FunnelFooterWidget"
:isForwardActionDisabled="!meta.valid"
:isBackButtonHidden="shouldHideBackButton"
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" />
</div>
<div class="col-md-2 col-lg-3">&nbsp;</div>
</div>
</div> </div>
</div>
</Form> </Form>
</template> </template>
<script> <script>
// Components // Components
import funnelHeader from "@/fmg-components/funnel-header/funnel-header"; import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
import funnelFooter from "@/fmg-components/funnel-footer/funnel-footer"; import navbar from "@/fmg-components/nav-bar/nav-bar";
import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner"; import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner";
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header"; import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
import sideDoorOptions from "@/layouts/vehicle-damage/side-door-options/side-door-options"; import sideDoorOptions from "@/layouts/vehicle-damage/side-door-options/side-door-options";
@ -219,7 +235,7 @@ export default {
}) })
) { ) {
windShieldOptions.selectedWindshieldDamageType = windShieldOptions.selectedWindshieldDamageType =
damageLocationsSelected.REPLACE; damageLocationsSelected.REPLACE;
windShieldOptions.selectedWindshieldReplaceOptions.push( windShieldOptions.selectedWindshieldReplaceOptions.push(
damageLocationsSelected.SINGLE damageLocationsSelected.SINGLE
); );
@ -234,7 +250,7 @@ export default {
}) })
) { ) {
windShieldOptions.selectedWindshieldDamageType = windShieldOptions.selectedWindshieldDamageType =
damageLocationsSelected.REPLACE; damageLocationsSelected.REPLACE;
windShieldOptions.selectedWindshieldReplaceOptions.push( windShieldOptions.selectedWindshieldReplaceOptions.push(
damageLocationsSelected.DRIVER damageLocationsSelected.DRIVER
); );
@ -249,7 +265,7 @@ export default {
}) })
) { ) {
windShieldOptions.selectedWindshieldDamageType = windShieldOptions.selectedWindshieldDamageType =
damageLocationsSelected.REPLACE; damageLocationsSelected.REPLACE;
windShieldOptions.selectedWindshieldReplaceOptions.push( windShieldOptions.selectedWindshieldReplaceOptions.push(
damageLocationsSelected.PASSENGER damageLocationsSelected.PASSENGER
); );
@ -319,7 +335,7 @@ export default {
isWindshieldRepair: this.isWindshieldRepair, isWindshieldRepair: this.isWindshieldRepair,
selectedGlassToReplace: this.selectedGlassToReplace(), selectedGlassToReplace: this.selectedGlassToReplace(),
selectedWindshieldChipCount: selectedWindshieldChipCount:
this.selectedWindshieldOptions.selectedWindshieldChipCount, this.selectedWindshieldOptions.selectedWindshieldChipCount,
}, },
false false
); );
@ -342,9 +358,9 @@ export default {
const payment = this.$store.getters.payment; const payment = this.$store.getters.payment;
const vehicleChangedDuringPolicyLookupInHeritage = const vehicleChangedDuringPolicyLookupInHeritage =
payment.isInsurance && payment.isInsurance &&
payment.insuranceCoverage.coverageStatus && payment.insuranceCoverage.coverageStatus &&
payment.insuranceCoverage.coverageStatus !== ""; payment.insuranceCoverage.coverageStatus !== "";
if (vehicleChangedDuringPolicyLookupInHeritage) { if (vehicleChangedDuringPolicyLookupInHeritage) {
if (store.getters.damage.isRepair) { if (store.getters.damage.isRepair) {
this.$router.navigateWithSaving( this.$router.navigateWithSaving(
@ -354,7 +370,7 @@ export default {
} else { } else {
this.$router.navigateWithSaving( this.$router.navigateWithSaving(
this.navigationScenarios this.navigationScenarios
.CLICKED_FORWARD_WITH_REPLACE_AND_VERIFIED_INSURANCE, .CLICKED_FORWARD_WITH_REPLACE_AND_VERIFIED_INSURANCE,
this.$route this.$route
); );
} }
@ -436,7 +452,7 @@ export default {
return ( return (
this.isWindshieldDamageLocation && this.isWindshieldDamageLocation &&
this.selectedWindshieldOptions.selectedWindshieldDamageType === this.selectedWindshieldOptions.selectedWindshieldDamageType ===
damageLocationsSelected.REPAIR damageLocationsSelected.REPAIR
); );
}, },
isDriverSideReplace() { isDriverSideReplace() {
@ -464,10 +480,10 @@ export default {
if ( if (
!this.selectedDamageLocations?.includes("Windshield") || !this.selectedDamageLocations?.includes("Windshield") ||
this.selectedWindshieldOptions.selectedWindshieldDamageType === this.selectedWindshieldOptions.selectedWindshieldDamageType ===
damageLocationsSelected.REPAIR || damageLocationsSelected.REPAIR ||
!this.selectedWindshieldOptions.selectedWindshieldReplaceOptions !this.selectedWindshieldOptions.selectedWindshieldReplaceOptions
) )
return false; return false;
return ( return (
this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some( this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some(
@ -486,14 +502,14 @@ export default {
); );
} }
) || ) ||
this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some( this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some(
(selectedPassengerWindshield) => { (selectedPassengerWindshield) => {
return ( return (
selectedPassengerWindshield.toUpperCase() === selectedPassengerWindshield.toUpperCase() ===
damageLocationsSelected.PASSENGER.toUpperCase() damageLocationsSelected.PASSENGER.toUpperCase()
); );
} }
)) ))
); );
}, },
shouldDisplayVehicleChangeAlert() { shouldDisplayVehicleChangeAlert() {
@ -509,7 +525,7 @@ export default {
components: { components: {
funnelHeader, funnelHeader,
funnelFooter, navbar,
vehicleBanner, vehicleBanner,
funnelSubHeader, funnelSubHeader,
sideDoorOptions, sideDoorOptions,

View file

@ -542,7 +542,7 @@ function setupMocks({ pageHeaderWidgetHeaderText = {}, mountOptionsMockData = {}
partQuestionRearWrapper.vm.initializeComponent = glassPartQuestion.methods.initializeComponent; partQuestionRearWrapper.vm.initializeComponent = glassPartQuestion.methods.initializeComponent;
wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent; wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent;
wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn(); wrapper.vm.$refs.navbar.removeLoader = jest.fn();
wrapper.vm.$refs.loadingModal.showModal = jest.fn(); wrapper.vm.$refs.loadingModal.showModal = jest.fn();
// wrapper.vm.$refs.onSubmit = jest.fn(); // wrapper.vm.$refs.onSubmit = jest.fn();
// wrapper.vm.$refs.onInvalidSubmit = jest.fn(); // wrapper.vm.$refs.onInvalidSubmit = jest.fn();

View file

@ -31,9 +31,9 @@
:colorAnswers="item.colorAnswers" :colorAnswers="item.colorAnswers"
:alreadyPopulatedPartsData="alreadyPopulatedPartsData" /> :alreadyPopulatedPartsData="alreadyPopulatedPartsData" />
</div> </div>
<funnelFooter <navbar
cmsWidgetName="FunnelFooterWidget" cmsWidgetName="FunnelFooterWidget"
ref="funnelFooter" ref="navbar"
:isForwardActionDisabled="isForwardActionDisabled" :isForwardActionDisabled="isForwardActionDisabled"
@back-clicked="navigateBack" @back-clicked="navigateBack"
@ForwardClicked="forwardButtonAction" /> @ForwardClicked="forwardButtonAction" />
@ -48,7 +48,7 @@ import glassPartQuestion from "@/layouts/vehicle-parts/glass-part-question/glass
import funnelHeader from "@/fmg-components/funnel-header/funnel-header"; import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner"; import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner";
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header"; import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
import funnelFooter from "@/fmg-components/funnel-footer/funnel-footer"; import navbar from "@/fmg-components/nav-bar/nav-bar";
import alert from "@/ux-components/alert/alert"; import alert from "@/ux-components/alert/alert";
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue"; import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
// Supporting Files // Supporting Files
@ -178,7 +178,7 @@ export default {
} }
// If no parts could be matched, throw an error (isForwardActionDisabled is based off of matchedParts) // If no parts could be matched, throw an error (isForwardActionDisabled is based off of matchedParts)
if (this.isForwardActionDisabled) { if (this.isForwardActionDisabled) {
this.$refs.funnelFooter.removeLoader(); this.$refs.navbar.removeLoader();
throw new Error("Could not match any parts to the selected parts"); throw new Error("Could not match any parts to the selected parts");
} }
@ -221,7 +221,7 @@ export default {
funnelHeader, funnelHeader,
vehicleBanner, vehicleBanner,
funnelSubHeader, funnelSubHeader,
funnelFooter, navbar,
alert, alert,
loadingModal, loadingModal,
}, },

View file

@ -1,73 +1,72 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }"> <Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
<div class="page-container-grouped-styles position-relative"> <div class="container-fluid">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <div class="row">
<div class="select-car"> <div class="col">
<div class="row"> <funnelHeader cmsWidgetName="FunnelHeaderWidget" />
<div class="col">
<div class="select-car-form rounded">
<funnelSubHeader
cmsWidgetName="FunnelSubHeaderWidget"
class="siteSubHeader" />
<vehicleQuestion
ref="vehicleYearQuestion"
v-model="selectedYear"
:isDisabled="!yearOptions.length"
:options="yearOptions"
cmsWidgetName="VehicleYearQuestion"
validationRules="year-required"
placeHolderText="Select year"
inputId="yearQuestionField" />
<vehicleQuestion
ref="vehicleMakeQuestion"
v-model="selectedMake"
:isDisabled="!makeOptions.length"
:options="makeOptions"
cmsWidgetName="VehicleMakeQuestion"
validationRules="make-required"
placeHolderText="Select make"
inputId="makeQuestionField" />
<vehicleQuestion
ref="vehicleModelQuestion"
v-model="selectedModel"
cmsWidgetName="VehicleModelQuestion"
:isDisabled="!modelOptions.length"
:options="modelOptions"
validationRules="model-required"
placeHolderText="Select model"
inputId="modelQuestionField" />
<vehicleQuestion
ref="vehicleStyleQuestion"
v-model="selectedStyle"
cmsWidgetName="VehicleStyleQuestion"
:isDisabled="!styleOptions.length"
:options="styleOptions"
validationRules="style-required"
placeHolderText="Select style"
inputId="styleQuestionField" />
<vehicleBanner
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="displayGeneric"
:displayVehicleImage="imageUrl"
:vehicleCategory="category"
:displayUnmatchedVehicleIcon="unmatchedVehicleIcon"
class="mt-5 vehicle-footer"
ref="banner" />
<funnelFooter
cmsWidgetName="FunnelFooterWidget"
@ForwardClicked="forwardButtonAction"
:isForwardActionDisabled="!meta.valid"
ref="funnelFooter" />
</div>
</div>
</div> </div>
</div> </div>
<div class="row">
<div class="col-md-3 col-lg-4">&nbsp;</div>
<div class="col-md-6 col-lg-4">
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" class="siteSubHeader" />
<vehicleQuestion
ref="vehicleYearQuestion"
v-model="selectedYear"
:isDisabled="!yearOptions.length"
:options="yearOptions"
cmsWidgetName="VehicleYearQuestion"
validationRules="year-required"
placeHolderText="Select year"
inputId="yearQuestionField" />
<vehicleQuestion
ref="vehicleMakeQuestion"
v-model="selectedMake"
:isDisabled="!makeOptions.length"
:options="makeOptions"
cmsWidgetName="VehicleMakeQuestion"
validationRules="make-required"
placeHolderText="Select make"
inputId="makeQuestionField" />
<vehicleQuestion
ref="vehicleModelQuestion"
v-model="selectedModel"
cmsWidgetName="VehicleModelQuestion"
:isDisabled="!modelOptions.length"
:options="modelOptions"
validationRules="model-required"
placeHolderText="Select model"
inputId="modelQuestionField" />
<vehicleQuestion
ref="vehicleStyleQuestion"
v-model="selectedStyle"
cmsWidgetName="VehicleStyleQuestion"
:isDisabled="!styleOptions.length"
:options="styleOptions"
validationRules="style-required"
placeHolderText="Select style"
inputId="styleQuestionField" />
<div class="col-md-3">&nbsp;</div>
<vehicleBanner
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="displayGeneric"
:displayVehicleImage="imageUrl"
:vehicleCategory="category"
:displayUnmatchedVehicleIcon="unmatchedVehicleIcon"
class="mt-5 vehicle-footer"
ref="banner" />
<navbar
cmsWidgetName="FunnelFooterWidget"
@ForwardClicked="forwardButtonAction"
:isForwardActionDisabled="!meta.valid"
ref="navbar" />
</div>
<div class="col-md-3 col-lg-4">&nbsp;</div>
</div>
</div> </div>
</Form> </Form>
</template> </template>
@ -75,7 +74,7 @@
<script> <script>
// Components // Components
import funnelHeader from "@/fmg-components/funnel-header/funnel-header"; import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
import funnelFooter from "@/fmg-components/funnel-footer/funnel-footer"; 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 vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner"; import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner";
import vehicleQuestion from "@/layouts/vehicle/vehicle-question/vehicle-question"; import vehicleQuestion from "@/layouts/vehicle/vehicle-question/vehicle-question";
@ -390,7 +389,7 @@ export default {
components: { components: {
funnelHeader, funnelHeader,
funnelFooter, navbar,
funnelSubHeader, funnelSubHeader,
vehicleBanner, vehicleBanner,
Form, Form,

View file

@ -47,14 +47,14 @@ jest.mock("@/helpers/damage-helper", () => ({
})); }));
describe("vin-lookup.vue", () => { describe("vin-lookup.vue", () => {
it("Should update the funnel-footer forward button when VIN is changed", (done) => { it("Should update the nav-bar forward button when VIN is changed", (done) => {
//Arrange //Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
//Act //Act
wrapper.setData({ vin: "newValue" }); wrapper.setData({ vin: "newValue" });
//Assert //Assert
wrapper.vm.$nextTick(() => { wrapper.vm.$nextTick(() => {
expect(wrapper.vm.$refs.funnelFooter.updateButtonText).toBeCalled(); expect(wrapper.vm.$refs.navbar.updateButtonText).toBeCalled();
done(); done();
}); });
}); });
@ -368,8 +368,8 @@ function mockOutPromises({ carId, isZipValid = true, isZipServiceable = true })
} }
function mockOutStubFunctions(wrapper) { function mockOutStubFunctions(wrapper) {
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); wrapper.vm.$refs.navbar.updateButtonText = jest.fn();
wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn(); wrapper.vm.$refs.navbar.removeLoader = jest.fn();
wrapper.vm.getZipCodeData = jest wrapper.vm.getZipCodeData = jest
.fn() .fn()
.mockReturnValue({ isValid: true, isServiceable: true, state: "OH" }); .mockReturnValue({ isValid: true, isServiceable: true, state: "OH" });

View file

@ -120,9 +120,9 @@
!displayNonServiceableZipAlert !displayNonServiceableZipAlert
" "
alertClass="alert-success" /> alertClass="alert-success" />
<funnelFooter <navbar
cmsWidgetName="FunnelFooterWidget" cmsWidgetName="FunnelFooterWidget"
ref="funnelFooter" ref="navbar"
:isForwardActionDisabled="!meta.valid" :isForwardActionDisabled="!meta.valid"
@back-clicked="backButtonAction" @back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" /> @ForwardClicked="forwardButtonAction" />
@ -134,7 +134,7 @@
<script> <script>
// Components // Components
import funnelHeader from "@/fmg-components/funnel-header/funnel-header"; import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
import funnelFooter from "@/fmg-components/funnel-footer/funnel-footer"; import navbar from "@/fmg-components/nav-bar/nav-bar";
import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner"; import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner";
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header"; import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
import alert from "@/ux-components/alert/alert"; import alert from "@/ux-components/alert/alert";
@ -278,7 +278,7 @@ export default {
const isZipValid = resultMap.zipCodeData.isValid; const isZipValid = resultMap.zipCodeData.isValid;
if (this.serviceZipCode && !isZipValid) { if (this.serviceZipCode && !isZipValid) {
this.displayInvalidZipAlert = true; this.displayInvalidZipAlert = true;
return this.$refs.funnelFooter.removeLoader(); return this.$refs.navbar.removeLoader();
} }
this.displayInvalidZipAlert = false; this.displayInvalidZipAlert = false;
@ -295,7 +295,7 @@ export default {
} }
// Remove loader and stop processing the page. // Remove loader and stop processing the page.
return this.$refs.funnelFooter.removeLoader(); return this.$refs.navbar.removeLoader();
} }
// Check if the CarId is different from the lookup vs what is in state currently. // Check if the CarId is different from the lookup vs what is in state currently.
@ -315,10 +315,10 @@ export default {
); );
// Update button "Continue with..." // Update button "Continue with..."
this.$refs.funnelFooter.updateButtonText( this.$refs.navbar.updateButtonText(
`Continue with ${resultMap.vehicleLookupResponse.year} ${resultMap.vehicleLookupResponse.make} ${resultMap.vehicleLookupResponse.model}` `Continue with ${resultMap.vehicleLookupResponse.year} ${resultMap.vehicleLookupResponse.make} ${resultMap.vehicleLookupResponse.model}`
); );
return this.$refs.funnelFooter.removeLoader(); return this.$refs.navbar.removeLoader();
} }
// Save vin, vehicle, customer and service information // Save vin, vehicle, customer and service information
@ -380,7 +380,7 @@ export default {
this.displayInvalidZipAlert = true; this.displayInvalidZipAlert = true;
} }
return this.$refs.funnelFooter.removeLoader(); return this.$refs.navbar.removeLoader();
}, },
async navigateForward() { async navigateForward() {
if (this.isCarIdDifferent && !this.isSelectedGlassAvailableForVehicle) { if (this.isCarIdDifferent && !this.isSelectedGlassAvailableForVehicle) {
@ -488,7 +488,7 @@ export default {
vin() { vin() {
this.displayVinNotFoundAlert = false; this.displayVinNotFoundAlert = false;
this.displayVinScanFailedAlert = false; this.displayVinScanFailedAlert = false;
this.$refs.funnelFooter.updateButtonText( this.$refs.navbar.updateButtonText(
this.getCmsContent("FunnelFooterWidget", "ForwardButtonText") this.getCmsContent("FunnelFooterWidget", "ForwardButtonText")
); );
}, },
@ -498,7 +498,7 @@ export default {
}, },
components: { components: {
funnelHeader, funnelHeader,
funnelFooter, navbar,
vehicleBanner, vehicleBanner,
funnelSubHeader, funnelSubHeader,
textboxQuestion, textboxQuestion,

View file

@ -100,11 +100,11 @@ export default {
return lineItem.kitPrice + lineItem.laborAmount + lineItem.sellingPrice; return lineItem.kitPrice + lineItem.laborAmount + lineItem.sellingPrice;
}, },
scrollToPageTop() { scrollToPageTop() {
const container = document.getElementsByClassName("page-container-grouped-styles")[0]; const container = document.getElementsByClassName("container-fluid")[0];
container.scrollTo({ top: 0, left: 0, behavior: "smooth" }); container.scrollTo({ top: 0, left: 0, behavior: "smooth" });
}, },
scrollToPageBottom() { scrollToPageBottom() {
const container = document.getElementsByClassName("page-container-grouped-styles")[0]; const container = document.getElementsByClassName("container-fluid")[0];
container.scrollTo({ top: container.scrollHeight, left: 0, behavior: "smooth" }); container.scrollTo({ top: container.scrollHeight, left: 0, behavior: "smooth" });
}, },
}, },

View file

@ -5,15 +5,7 @@ body {
background-color: #fff; background-color: #fff;
color: #4d5151; color: #4d5151;
.container-fluid { .container-fluid {
max-width: 576px; //Remove once desktop app is complete max-width: 1400px;
&.container-shadow {
box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.15); //Use instead of Bootstrap's helper
}
&.make-tall {
height: 100vh;
display: flex;
flex-direction: column;
}
.prevent-squish { .prevent-squish {
overflow-x: unset; overflow-x: unset;
} }
@ -45,7 +37,7 @@ body {
} }
.page-container-grouped-styles { .page-container-grouped-styles {
@extend .container-fluid, .shadow, .rounded-3, .p-2, .position-relative, .make-tall, .px-5; //@extend .container-fluid, .shadow, .rounded-3, .p-2, .position-relative, .make-tall, .px-5;
} }
//Footer modal backdrop adjustments for positioning //Footer modal backdrop adjustments for positioning

View file

@ -170,15 +170,6 @@ $spacers: (
//Enable negative spacing (does NOT work on padding) //Enable negative spacing (does NOT work on padding)
$enable-negative-margins: true; $enable-negative-margins: true;
//Grid breakpoints
$grid-breakpoints: (
xs: 0,
sm: 576px,
md: 838px,
lg: 1074px,
xl: 1416px,
);
//Shadow //Shadow
$box-shadow: 0 0.5rem 1rem rgba($black, 0.15); $box-shadow: 0 0.5rem 1rem rgba($black, 0.15);
$box-shadow-sm: 0 0.125rem 0.25rem rgba($black, 0.075); $box-shadow-sm: 0 0.125rem 0.25rem rgba($black, 0.075);