DigitalConsumer.ISS/src/layouts/service-location/service-location.vue

482 lines
18 KiB
Vue

<template>
<Form
ref="theForm"
v-slot="{ meta }"
@submit="onSubmit"
@invalidSubmit="onInvalidSubmit">
<div class="page-container-grouped-styles">
<siteHeader cmsWidgetName="SiteHeaderWidget" />
<siteSubHeader
cmsWidgetName="SiteSubHeaderWidget"
class="mt-5" />
<div class="main-content-container">
<serviceZipModalQuestion
ref="serviceZipCodeQuestion"
v-model="serviceZipCodeQuestion"
modalWidgetName="ServiceZipModalWidget"
:onZipUpdateCallback="reloadShopData"
@updatedServiceability="setServiceabilityDetails"
@updatedContainsMilitaryBase="setContainsMilitaryBase" />
<alert
v-if="displayMilitaryZipAlert"
ref="alertMilitaryBaseZip"
class="my-5"
cmsWidgetName="AlertMilitaryBaseZipWidget"
alertClass="alert-warning" />
<alert
v-if="displayServiceableMobileOnly"
ref="alertMobileOnly"
class="my-5"
cmsWidgetName="AlertMobileOnlyWidget"
alertClass="alert-warning" />
<alert
v-if="displayRecalibrationWarning"
ref="alertRecalNoMobile"
class="my-5"
cmsWidgetName="AlertRecalNoMobileWidget"
alertClass="alert-warning"
@text-link-clicked="openModalAction" />
<alert
v-if="displayServiceableInshopOnly"
ref="alertInshopOnly"
class="my-5"
cmsWidgetName="AlertInshopOnlyWidget"
alertClass="alert-warning" />
<alert
v-if="displayNoShopsAlert"
ref="alertNoShops"
class="my-5"
cmsWidgetName="AlertNoShopsWidget"
alertClass="alert-warning" />
<appointmentTypeQuestion
v-show="isAppointmentTypeDisplayed"
ref="appointmentTypeQuestion"
v-model="selectedAppointmentType"
:isServiceableMobile="isServiceableMobile"
:isServiceableInshop="isServiceableInshop"
:isDisplayed="isAppointmentTypeDisplayed"
groupName="appointmentTypeQuestion"
cmsWidgetName="AppointmentTypeQuestionWidget"
validationRules="option-required" />
<mobileLocationModalQuestions
v-if="isMobileLocationDisplayed"
ref="mobileLocationQuestions"
v-model="mobileLocationQuestions"
customComponentId="mobileLocationQuestions"
:mobileFeePart="mobileFeePart"
validationRules="mobile-location-required"
linkWidgetName="MobileLocationLinkWidget"
modalWidgetName="MobileLocationModalWidget"
:onZipUpdateCallback="reloadShopData"
@updated-mobile-fee-part="setMobileFeePart"
@updated-serviceability="setServiceabilityDetails"
@updated-contains-military-base="setContainsMilitaryBase" />
<shopQuestion
v-show="isShopQuestionDisplayed"
ref="shopQuestion"
v-model="selectedProvider"
:selectedAppointmentType="selectedAppointmentType"
:isDisplayed="isShopQuestionDisplayed"
cmsWidgetName="ShopQuestionWidget" />
<contentGroupModal
ref="RecalModal"
cmsWidgetName="RecalModal" />
<siteFooter
ref="siteFooter"
class="mt-5"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid || displayNoShopsAlert"
@backClicked="backButtonAction"
@forwardClicked="forwardButtonAction" />
</div>
</div>
</Form>
</template>
<script>
// Import Supporting Files
import { AppointmentTypeStrings } from '@/constants/schedule-constants.js';
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import settleAllPromises from '@/helpers/layout-helper';
import { required } from '@/helpers/validation-rules';
import errorMessages from '@/constants/error-messages';
import { useMainStore } from '@/store';
import { getPricedMobileFeePart, getServiceabilityDetails, getZipCodeData } from '@/helpers/service-location-helper';
// Import Component
import alert from '@/ux-components/alert/alert.vue';
import appointmentTypeQuestion from '@/layouts/service-location/appointment-type-question/appointment-type-question.vue';
import baseFormMixin from '@/mixins/base-form-mixin';
import contentGroupModal from '@/iss-components/content-group-modal/content-group-modal.vue';
import mobileLocationModalQuestions from '@/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue';
import { Form, defineRule } from 'vee-validate';
import shopQuestion from '@/layouts/service-location/shop-question/shop-question.vue';
import siteFooter from '@/iss-components/site-footer/site-footer.vue';
import siteHeader from '@/iss-components/site-header/site-header.vue';
import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header.vue';
import serviceZipModalQuestion from '@/layouts/service-location/service-zip-modal-question/service-zip-modal-question.vue';
// DEFINE VALIDATION RULES
defineRule('mobile-location-required', (value) => {
if (
value.addressQuestions.streetAddress === ''
|| value.addressQuestions.city === ''
|| value.addressQuestions.state === ''
|| value.addressQuestions.zipCode === ''
|| value.isVehicleProtected == null
) {
return errorMessages.MOBILE_LOCATION_REQUIRED;
}
return true;
});
defineRule('selection-required', required(errorMessages.OPTION_REQUIRED));
export default {
name: 'service-location',
components: {
alert,
appointmentTypeQuestion,
contentGroupModal,
mobileLocationModalQuestions,
siteFooter,
siteHeader,
siteSubHeader,
// eslint-disable-next-line vue/no-reserved-component-names
Form,
serviceZipModalQuestion,
shopQuestion
},
mixins: [baseFormMixin],
async beforeRouteEnter(to, from, next) {
// Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
const serviceZipCode = useMainStore().order.serviceLocation.zipCode || useMainStore().order.customer.address.zipCode;
const zipCodeData = getZipCodeData(serviceZipCode);
const mobileFeePartPromise = getPricedMobileFeePart(serviceZipCode);
const serviceabilityDetailsPromise = getServiceabilityDetails(serviceZipCode);
const shopQuestionInitialDataPromise = shopQuestion.methods.loadInitialData(serviceZipCode);
// Settle promises and get results
const promiseResultMap = [
{
resultKey: 'cmsContent',
promise: cmsContentPromise
},
{
resultKey: 'mobileFeePart',
promise: mobileFeePartPromise
},
{
resultKey: 'serviceabilityDetails',
promise: serviceabilityDetailsPromise
},
{
resultKey: 'shopQuestionInitialData',
promise: shopQuestionInitialDataPromise
},
{
resultKey: 'zipCodeData',
promise: zipCodeData
}
];
const resultMap = await settleAllPromises(promiseResultMap);
next((vm) => {
vm.setCmsContent(resultMap.cmsContent);
vm.setData(resultMap.zipCodeData, resultMap.serviceabilityDetails, resultMap.mobileFeePart);
vm.$refs.shopQuestion.initializeComponent(resultMap.shopQuestionInitialData);
});
},
setup() {
const mainStore = useMainStore();
return { mainStore };
},
data() {
return {
streetAddress: this.getServiceAddressFromStore(),
streetAddress2: this.getServiceAddress2FromStore(),
city: this.getServiceCityFromStore(),
state: this.getServiceStateFromStore(),
zipCode: this.getServiceZipCodeFromStore(),
isVehicleProtected: this.getIsVehicleProtectedFromStore(),
isGlassServiceableInshop: null,
isRecalibrationServiceableInshop: null,
isGlassServiceableMobile: null,
isRecalibrationServiceableMobile: null,
selectedAppointmentType: this.getSelectedAppointmentType(),
selectedProvider: this.getSelectedProvider(),
mobileFeePart: null,
zipContainsMilitaryBase: false,
zipCodeCtu: null
};
},
computed: {
questionText() {
return this.getCmsContent('ServiceTypeQuestionWidget', 'QuestionText');
},
answersFromCms() {
return this.getCmsContent('ServiceTypeQuestionWidget', 'Answers');
},
serviceZipCodeQuestion: {
get() {
return {
state: this.state,
zipCode: this.zipCode
};
},
set(newValue) {
if (newValue.zipCode !== this.zipCode) {
this.resetMobileLocation();
this.selectedAppointmentType = null;
this.selectedProvider = null;
}
this.state = newValue.state;
this.zipCode = newValue.zipCode;
// eslint-disable-next-line vue/valid-next-tick
this.$nextTick();
}
},
mobileLocationQuestions: {
get() {
return {
addressQuestions: {
streetAddress: this.streetAddress,
streetAddress2: this.streetAddress2,
city: this.city,
state: this.state,
zipCode: this.zipCode
},
isVehicleProtected: this.isVehicleProtected
};
},
set(newValue) {
this.streetAddress = newValue.addressQuestions.streetAddress;
this.streetAddress2 = newValue.addressQuestions.streetAddress2;
this.city = newValue.addressQuestions.city;
this.state = newValue.addressQuestions.state;
this.zipCode = newValue.addressQuestions.zipCode;
this.isVehicleProtected = newValue.isVehicleProtected;
if (newValue.zipCode !== this.zipCode) {
if (!(this.selectedAppointmentType === AppointmentTypeStrings.MOBILE
|| this.selectedAppointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP)) {
this.selectedAppointmentType = null;
}
this.selectedProvider = null;
}
}
},
isServiceableMobile() {
if (this.isRecalibrationServiceableMobile !== null) {
return this.isGlassServiceableMobile && this.isRecalibrationServiceableMobile;
}
return this.isGlassServiceableMobile;
},
isServiceableInshop() {
if (this.isRecalibrationServiceableInshop !== null) {
return this.isGlassServiceableInshop && this.isRecalibrationServiceableInshop;
}
return this.isGlassServiceableInshop;
},
isShopQuestionDisplayed() {
return (
this.selectedAppointmentType === 'Inshop'
|| this.selectedAppointmentType === 'Dropoff'
);
},
isAppointmentTypeDisplayed() {
return this.zipCode && !this.displayNoShopsAlert;
},
isMobileLocationDisplayed() {
return this.selectedAppointmentType === AppointmentTypeStrings.MOBILE
|| this.selectedAppointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP;
},
requiresInshopRecalibration() {
// Specifically check for isRecalibrationServiceableMobile === false, not null or true.
return (
this.isServiceableInshop
&& this.isGlassServiceableMobile
&& this.isRecalibrationServiceableMobile === false
);
},
displayMilitaryZipAlert() {
return this.zipContainsMilitaryBase && this.isServiceableMobile;
},
displayNoShopsAlert() {
return !this.isServiceableInshop && !this.isServiceableMobile;
},
displayRecalibrationWarning() {
return this.requiresInshopRecalibration;
},
displayServiceableInshopOnly() {
return (
!this.displayRecalibrationWarning
&& this.isServiceableInshop
&& !this.isServiceableMobile
);
},
displayServiceableMobileOnly() {
return this.isServiceableMobile && !this.isServiceableInshop;
}
},
methods: {
arePagePrerequisitesValid() {
return (
useMainStore().lineItems.supportingItems !== null
&& useMainStore().order.serviceLocation.zipCode !== null
);
},
backButtonAction() {
/**
* this.navigationScenarios comes from base-mixin
*/
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
},
async reloadShopData(zipCode) {
await this.$refs.shopQuestion.reloadShopData(zipCode);
},
async forwardButtonAction() {
useMainStore().saveServiceLocation({
address: this.streetAddress,
address2: this.streetAddress2,
city: this.city,
state: this.state,
zipCode: this.zipCode,
zipCodeCtu: this.zipCodeCtu,
appointmentType: this.selectedAppointmentType,
isVehicleProtected: this.isVehicleProtected,
provider: {
providerNumber: this.selectedProvider?.providerNumber,
address: {
streetAddress: this.selectedProvider?.address?.streetAddress,
city: this.selectedProvider?.address?.city,
state: this.selectedProvider?.address?.state,
zipCode: this.selectedProvider?.address?.zipCode,
zipCodeCtu: this.selectedProvider?.address?.zipCodeCtu
}
}
});
this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD, this.$route);
},
openModalAction(modalName) {
this.$refs[modalName].openModal();
},
resetDependentState() {
},
getServiceAddressFromStore() {
return useMainStore().order.serviceLocation.address;
},
getServiceAddress2FromStore() {
return useMainStore().order.serviceLocation.address2;
},
getServiceCityFromStore() {
return useMainStore().order.serviceLocation.city;
},
getServiceStateFromStore() {
return useMainStore().order.serviceLocation.state || useMainStore().order.customer.address.state;
},
getServiceZipCodeFromStore() {
return useMainStore().order.serviceLocation.zipCode || useMainStore().order.customer.address.zipCode;
},
getIsVehicleProtectedFromStore() {
return useMainStore().order.serviceLocation.isVehicleProtected;
},
getSelectedAppointmentType() {
return useMainStore().order.serviceLocation.appointmentType;
},
getSelectedProvider() {
return useMainStore().order.serviceLocation.provider;
},
setData(zipCodeData, serviceabilityDetails, mobileFeePart) {
if (zipCodeData) {
this.zipContainsMilitaryBase = zipCodeData.containsMilitaryBase;
this.zipCodeCtu = zipCodeData.zipCodeCtu;
}
if (serviceabilityDetails) {
this.setServiceabilityDetails(serviceabilityDetails);
}
if (mobileFeePart) {
this.mobileFeePart = mobileFeePart;
}
},
setContainsMilitaryBase(val) {
if (this.zipContainsMilitaryBase !== val) {
this.zipContainsMilitaryBase = val;
}
},
setMobileFeePart(mobileFeePart) {
this.mobileFeePart = mobileFeePart;
},
resetMobileLocation() {
this.streetAddress = '';
this.streetAddress2 = '';
this.city = '';
this.isVehicleProtected = null;
},
setServiceabilityDetails(serviceabilityDetails) {
this.isGlassServiceableInshop = serviceabilityDetails.isGlassServiceableInshop;
this.isRecalibrationServiceableInshop = serviceabilityDetails.isRecalibrationServiceableInshop;
this.isGlassServiceableMobile = serviceabilityDetails.isGlassServiceableMobile;
this.isRecalibrationServiceableMobile = serviceabilityDetails.isRecalibrationServiceableMobile;
}
}
};
</script>
<style lang="scss" scoped>
$page-side-padding: 1.5rem;
.page-container-grouped-styles {
overflow: auto;
.main-content-container {
padding: 0 1.5rem !important;
}
}
.question-text {
& > span {
line-height: 1.5rem;
}
}
.list-card-content p {
&:first-of-type {
line-height: 1.5rem;
}
&:not(:nth-of-type(1)){
line-height: 1.250rem;
}
}
.choose-option{
.button-question >div {
&:first-of-type {
margin-bottom: 0.95rem;
line-height: 1.500rem;
}
}
}
.button-question{
.row.form-test-error{
line-height:1.500rem;
padding-left: 0rem !important;
}
}
.service-location-button-question {
.question-text {
margin-top: 1.5rem;
span {
text-align: center;
}
}
}
</style>