DigitalConsumer.ISS/src/layouts/address-vehicles/address-vehicles.vue
scottkiener-at-safelite 356f97cf33
Merge pull request #1076 from Safelite/feature/skiener/INSR-7748
INSR-7748 | Address-Vehicle page
2026-02-13 12:32:55 -05:00

317 lines
12 KiB
Vue

<template>
<Form
ref="theForm"
v-slot="{ meta }"
@submit="onSubmit"
@invalidSubmit="onInvalidSubmit">
<div class="fade-on-route-transition">
<div class="justify-content-center">
<siteHeader cmsWidgetName="SiteHeaderWidget" />
</div>
<div class="iss-heritage-container-width">
<div class="address-vehicles-container iss-heritage-content-container-width">
<siteSubHeader
cmsWidgetName="SiteSubHeaderWidget"
class="mt-4" />
<alert
v-if="displayNoServiceAlert"
ref="AlertNoService"
class="mt-5"
cmsWidgetName="AlertVehicleSelection"
alertClass="alert-danger"
:isDismissable="false" />
<addressVehiclesQuestion
ref="addressVehiclesQuestion"
v-model="selectedVehicleVin"
:questionText="questionText"
:vehicles="VehiclesForQuestions"
:vehicleSelected="VehicleSelected"
validationRules="vehicle-required"
:isCarIdDifferent="isCarIdDifferent"
:displayMatchedDifferentVehicleAlert="
displayMatchedDifferentVehicleAlert
"
:displayMatchedTwoIdenticalYMMVehicleAlert="
displayMatchedTwoIdenticalYMMVehicleAlert
" />
<div
v-if="splitAlertProvideVinBodyForLink.length"
class="alert-provide-vin my-3">
<span
v-for="copy in splitAlertProvideVinBodyForLink"
:key="copy">
<span
v-if="doesCopyContainRouterLink(copy)"
class="text-body">
<router-link
:to="{
query: {
issPage: `${getRouterLinkRouteFromCopy(
copy
)}`,
},
name: 'root',
}">{{
getRouterLinkDisplayTextFromCopy(copy)
}}</router-link>
</span>
<span
v-else
class="m-0 text-body"
v-html="copy"></span>
</span>
</div>
<siteFooter
ref="siteFooter"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid"
@backClicked="navigateBack"
@forwardClicked="forwardButtonAction" />
</div>
</div>
</div>
</Form>
</template>
<script>
// Import Supporting Files
import settleAllPromises from '@/helpers/layout-helper';
import { useMainStore } from '@/store';
import issPageValues from '@/router/router-constants/issPage-values';
import errorMessages from '@/constants/error-messages';
import { required } from '@/helpers/validation-rules';
import { Form, defineRule } from 'vee-validate';
import { isGlassAvailableForCarId } from '@/helpers/damage-helper';
import {
fetchCmsContentForPage,
doesCopyContainRouterLink,
splitCopyOnCMSPlaceHolder,
getRouterLinkRouteFromCopy,
getRouterLinkDisplayTextFromCopy
} from '@/helpers/cms-content-helper.js';
import routerParams from '@/router/router-constants/router-params';
import vinPagesMixin from '@/mixins/vin-pages-mixin';
// Import Component
import baseFormMixin from '@/mixins/base-form-mixin';
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 alert from '@/ux-components/alert/alert.vue';
import addressVehiclesQuestion from '@/layouts/address-vehicles/address-vehicles-question/address-vehicles-question.vue';
// DEFINE VALIDATION RULES
defineRule('vehicle-required', required(errorMessages.VEHICLE_REQUIRED));
export default {
name: 'address-vehicles',
components: {
siteFooter,
siteHeader,
siteSubHeader,
Form,
alert,
addressVehiclesQuestion
},
mixins: [baseFormMixin, vinPagesMixin],
async beforeRouteEnter(to, from, next) {
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
// Settle promises and get results
const promiseResultMap = [
{
resultKey: 'cmsContent',
promise: cmsContentPromise
}
];
const resultMap = await settleAllPromises(promiseResultMap);
next((vm) => {
vm.setCmsContent(resultMap.cmsContent);
});
},
props: {
validationRules: String
},
setup() {
const mainStore = useMainStore();
return { mainStore };
},
data() {
return {
selectedVehicleVin: '',
isCarIdDifferent: false,
isSelectedGlassAvailableForVehicle: true,
displayMatchedDifferentVehicleAlert: false,
displayMatchedTwoIdenticalYMMVehicleAlert: false,
previouslyEnteredCarId: '',
forwardButtonCarStyle: '',
displayNoServiceAlert: false
};
},
computed: {
vehicleCount() {
return this.VehiclesForQuestions.length;
},
questionText() {
return this.getCmsContent(
'VehicleConfirmationQuestion',
'QuestionText'
).replaceAll('{custom:vehicleCount}', this.vehicleCount);
},
isTwoIdenticalYMMVehicleFound() {
const vinYmmFound = `${this.selectedVehicle?.vehicle.year} ${this.selectedVehicle?.vehicle.make} ${this.selectedVehicle?.vehicle.model}`;
const vinYmmExpected = `${this.mainStore.order.vehicle.year} ${this.mainStore.order.vehicle.make} ${this.mainStore.order.vehicle.model}`;
return vinYmmFound.toLowerCase() === vinYmmExpected.toLowerCase();
},
AlertProvideVinBody() {
return this.getCmsContent('ProvideVinAlert', 'BodyText');
},
splitAlertProvideVinBodyForLink() {
// 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.AlertProvideVinBody);
},
VehiclesForQuestions() {
// Map API result data, to address-vehicles data structure
const mappedData = this.VehiclesFromApi.map((v) => {
const maskSymbol = '*';
const vinStart = maskSymbol.repeat(6);
const vinEnd = v.vin.substring(v.vin.length - 6);
return {
vin: v.vin,
vehicle: v.vehicle,
Text: `${v.vehicle.year} ${v.vehicle.make} ${v.vehicle.model}`,
Name: v.vin,
SubText: `VIN ${vinStart}${vinEnd}`
};
});
return mappedData;
},
VehiclesFromApi() {
return useMainStore().pageData(issPageValues.ADDRESS_VEHICLES);
},
selectedVehicle() {
return this.VehiclesForQuestions.find(({ vin }) => vin === this.selectedVehicleVin);
},
VehicleSelected() {
return this.mainStore.order.vehicle;
}
},
watch: {
selectedVehicleVin: {
handler() {
this.resetWarningsAndErrors();
if (!this.selectedVehicle?.vehicle.canSafeliteService) {
this.displayNoServiceAlert = true;
return this.$refs.siteFooter.disableForwardButton();
}
// does this vehicle match the previously selected carId?
this.isCarIdDifferent =
this.selectedVehicle?.vehicle.carId
!== useMainStore().vehicle.carId;
if (
this.isCarIdDifferent
&& this.selectedVehicle?.vehicle.carId
!== this.previouslyEnteredCarId
) {
this.previouslyEnteredCarId =
this.selectedVehicle?.vehicle.carId;
if (this.isTwoIdenticalYMMVehicleFound) {
const carStyle = this.selectedVehicle?.vehicle.style;
this.displayMatchedTwoIdenticalYMMVehicleAlert = true;
this.forwardButtonCarStyle = carStyle;
} else {
this.displayMatchedDifferentVehicleAlert = true;
}
}
},
deep: true
}
},
methods: {
doesCopyContainRouterLink,
splitCopyOnCMSPlaceHolder,
getRouterLinkRouteFromCopy,
getRouterLinkDisplayTextFromCopy,
arePagePrerequisitesValid() {
if (useMainStore().order.vehicle.carId) {
return true;
}
return false;
},
async forwardButtonAction() {
const vinLookup = await useMainStore().lookupVehicleByVin(this.selectedVehicle.vin);
if (!vinLookup) {
return;
}
this.isSelectedGlassAvailableForVehicle = await isGlassAvailableForCarId(vinLookup.data.carId);
await useMainStore().saveVin(
{
vehicleInfo: Object.assign(this.selectedVehicle.vehicle, {
vin: this.selectedVehicle.vin
}),
isSelectedGlassAvailableForVehicle:
this.isSelectedGlassAvailableForVehicle
},
false
);
await this.navigateForward();
},
async navigateForward() {
// If the vehicle selected on this page is different from the one originally entered and the selected glass is not available
// for that vehicle, then navigate back to "vehicle-damage" and display vehicle changed alert on that page.
if (
this.isCarIdDifferent
&& !this.isSelectedGlassAvailableForVehicle
) {
this.$router.navigate(
this.navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
this.$route,
{},
{ [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: true }
);
} else {
await this.navigateForwardWithSingleCarMatch();
}
},
resetWarningsAndErrors() {
this.displayMatchedDifferentVehicleAlert = false;
this.displayMatchedTwoIdenticalYMMVehicleAlert = false;
this.forwardButtonCarStyle = '';
}
}
};
</script>
<style lang="scss" scoped>
.iss-heritage-container-width {
.address-vehicles-container {
position: relative;
min-height: 1px;
padding-left: .9375rem;
padding-right: .9375rem;
}
}
.alert-provide-vin {
font-size: 0.75rem;
line-height: 1.4;
a {
//Per Devyn. This can't be documented in Figma so there is a comment with the Prototype mocks on the Quote page in Figma
text-underline-offset: 4px;
line-height: inherit;
font-weight: $font-weight-bold;
}
}
#multiple-vehicles-alert p {
margin-bottom: 0 !important; // Overrides extra margin-bottom on alert body text
}
.overflow-scroll {
height: calc(100% - 180px);
overflow-x: hidden !important;
}
</style>