WIP Resubmit pages with new formatting.

This commit is contained in:
Bryan Mauger 2024-04-16 10:32:19 -04:00
parent bb1909a8c3
commit c725c0472a
20 changed files with 2441 additions and 2050 deletions

View file

@ -1,13 +1,18 @@
<!-- Documented in confluence https://safelite.atlassian.net/wiki/spaces/DC/pages/76644418/Button+Question+Component --> <!-- Documented in confluence https://safelite.atlassian.net/wiki/spaces/DC/pages/76644418/Button+Question+Component -->
<template> <template>
<div <div
:class="isOverflowScrollable :class="
? 'button-question button-question-overflow' isOverflowScrollable
: 'button-question'"> ? 'button-question button-question-overflow'
: 'button-question'
">
<div <div
v-if="questionText && answers && answers.length > 0" v-if="questionText && answers && answers.length > 0"
class="question-text d-flex" class="question-text d-flex"
:class="{'small-question-text': isSmallQuestionText,'small-question-label-text':isSmallQuestionLabelText}"> :class="{
'small-question-text': isSmallQuestionText,
'small-question-label-text': isSmallQuestionLabelText,
}">
<span class="fw-bold w-100">{{ questionText }}</span> <span class="fw-bold w-100">{{ questionText }}</span>
</div> </div>
<div class="w-100 d-flex justify-content-center"> <div class="w-100 d-flex justify-content-center">
@ -22,9 +27,11 @@
:data-focus-target="formatString(groupName)" :data-focus-target="formatString(groupName)"
tabindex="-1"> tabindex="-1">
{{ questionText }} {{ questionText }}
{{ isMultiSelect && answers && answers.length > 1 {{
? "Select one or more options below." isMultiSelect && answers && answers.length > 1
: "Select an option below." }} ? 'Select one or more options below.'
: 'Select an option below.'
}}
</legend> </legend>
<div :class="getComponentLoopWrapperClasses"> <div :class="getComponentLoopWrapperClasses">
<div <div
@ -54,12 +61,12 @@
:setLastValuePushedToGa="setLastValuePushedToGa" :setLastValuePushedToGa="setLastValuePushedToGa"
:suppressError="suppressError" /> :suppressError="suppressError" />
<!-- For nested questions --> <!-- For nested questions -->
<transition <transition name="fade" mode="out-in">
name="fade"
mode="out-in">
<div <div
v-if="typeof selectedValues == 'string' && v-if="
selectedValues == answer.value"> typeof selectedValues == 'string' &&
selectedValues == answer.value
">
<slot></slot> <slot></slot>
</div> </div>
</transition> </transition>
@ -67,11 +74,8 @@
</div> </div>
</fieldset> </fieldset>
</div> </div>
<div <div v-if="!suppressError" class="row form-test-error mt-1">
v-if="!suppressError" <error-message :name="formatString(groupName)"></error-message>
class="row form-test-error mt-1">
<error-message
:name="formatString(groupName)"></error-message>
</div> </div>
</div> </div>
</template> </template>
@ -92,16 +96,16 @@ export default {
listCard, listCard,
ErrorMessage, ErrorMessage,
radio, radio,
providerPrefRadio providerPrefRadio,
}, },
props: { props: {
buttonTypeString: { buttonTypeString: {
type: String, type: String,
default: 'listButton' default: 'listButton',
}, },
buttonTypeObject: { buttonTypeObject: {
type: Object, type: Object,
default: null default: null,
}, },
isMultiSelect: Boolean, isMultiSelect: Boolean,
groupName: String, groupName: String,
@ -109,16 +113,16 @@ export default {
answers: Array, answers: Array,
textPosition: { textPosition: {
type: String, type: String,
default: 'text-center' default: 'text-center',
}, },
selectingInitiatesLoad: Boolean, selectingInitiatesLoad: Boolean,
loaderColor: { loaderColor: {
type: String, type: String,
default: 'blue' default: 'blue',
}, },
loaderPosition: { loaderPosition: {
type: String, type: String,
default: 'right' default: 'right',
}, },
isRequired: Boolean, isRequired: Boolean,
isOverflowScrollable: Boolean, isOverflowScrollable: Boolean,
@ -133,7 +137,7 @@ export default {
additionalButtonData: Object, additionalButtonData: Object,
additionalButtonStyling: String, additionalButtonStyling: String,
isSmallQuestionText: Boolean, isSmallQuestionText: Boolean,
isSmallQuestionLabelText: Boolean isSmallQuestionLabelText: Boolean,
}, },
emits: ['update:modelValue'], emits: ['update:modelValue'],
setup(props) { setup(props) {
@ -142,11 +146,18 @@ export default {
const fieldOptions = { const fieldOptions = {
value: modelValue, value: modelValue,
initialValue: null initialValue: null,
}; };
const { errorMessage, handleBlur, handleChange, meta, validate, errors, resetField } = const {
useField(props.groupName, props.validationRules, fieldOptions); errorMessage,
handleBlur,
handleChange,
meta,
validate,
errors,
resetField,
} = useField(props.groupName, props.validationRules, fieldOptions);
return { return {
errorMessage, errorMessage,
@ -155,12 +166,12 @@ export default {
validate, validate,
meta, meta,
errors, errors,
resetField resetField,
}; };
}, },
data() { data() {
return { return {
lastValuePushedToGa: null lastValuePushedToGa: null,
}; };
}, },
computed: { computed: {
@ -169,8 +180,8 @@ export default {
const baseClasses = this.isOverflowScrollable const baseClasses = this.isOverflowScrollable
? 'container-fluid overflow-scroll position-absolute px-5 pt-1 py-0' ? 'container-fluid overflow-scroll position-absolute px-5 pt-1 py-0'
: this.buttonTypeString === 'listCard' : this.buttonTypeString === 'listCard'
? 'w-100' ? 'w-100'
: ''; : '';
const SmallQuestionTextClass = this.isSmallQuestionText const SmallQuestionTextClass = this.isSmallQuestionText
? `${baseClasses}small-question-text` ? `${baseClasses}small-question-text`
@ -191,7 +202,8 @@ export default {
classes = 'd-flex flex-row p-0'; classes = 'd-flex flex-row p-0';
break; break;
case 'listCard': case 'listCard':
classes = 'row g-2 g-md-5 justify-content-center mb-1 flex-nowrap'; classes =
'row g-2 g-md-5 justify-content-center mb-1 flex-nowrap';
if (this.isWide) { if (this.isWide) {
classes += ' flex-column'; classes += ' flex-column';
} }
@ -228,32 +240,48 @@ export default {
} }
// Determine number of buttons and add class accordingly. // Determine number of buttons and add class accordingly.
// This is to accommodate unique spacing per design specs. // This is to accommodate unique spacing per design specs.
if (this.buttonTypeString === 'listCard' && this.buttonsInfo.length > 2) { if (
this.buttonTypeString === 'listCard' &&
this.buttonsInfo.length > 2
) {
classes += ' two-list-card-width'; classes += ' two-list-card-width';
} }
if (this.buttonTypeString === 'listCard' && this.buttonsInfo.length === 1) { if (
this.buttonTypeString === 'listCard' &&
this.buttonsInfo.length === 1
) {
classes += ' one-list-card-width'; classes += ' one-list-card-width';
} }
return classes; return classes;
}, },
buttonsInfo() { buttonsInfo() {
return (Array.isArray(this.answers) ? this.answers : [])?.map((answer) => ({ return (Array.isArray(this.answers) ? this.answers : [])?.map(
buttonLabel: answer.buttonLabel ?? answer.Text ?? answer, (answer) => ({
altText: answer.altText ?? (answer.Name ? answer.Name : answer), buttonLabel: answer.buttonLabel ?? answer.Text ?? answer,
buttonLabelSubCopy: answer.buttonLabelSubCopy ?? answer.SubText, altText:
buttonBodyCopy: answer.buttonBodyCopy ?? answer.buttonBodyCopy, answer.altText ?? (answer.Name ? answer.Name : answer),
buttonAuxiliaryCopy: answer.buttonAuxiliaryCopy ?? answer.buttonAuxiliaryCopy, buttonLabelSubCopy:
buttonFooterCopy: answer.buttonFooterCopy ?? answer.buttonFooterCopy, answer.buttonLabelSubCopy ?? answer.SubText,
buttonImage: answer.buttonImage ?? answer.AnswerImageUrl, buttonBodyCopy:
buttonImageId: answer.buttonImageId ?? answer.ImageId, answer.buttonBodyCopy ?? answer.buttonBodyCopy,
groupName: this.formatString(this.groupName), buttonAuxiliaryCopy:
value: answer.buttonAuxiliaryCopy ??
answer.value answer.buttonAuxiliaryCopy,
?? (this.useTextForValue && answer.Text ? answer.Text : answer.Name) buttonFooterCopy:
?? (typeof answer !== 'object' ? answer : null) answer.buttonFooterCopy ?? answer.buttonFooterCopy,
})); buttonImage: answer.buttonImage ?? answer.AnswerImageUrl,
buttonImageId: answer.buttonImageId ?? answer.ImageId,
groupName: this.formatString(this.groupName),
value:
answer.value ??
(this.useTextForValue && answer.Text
? answer.Text
: answer.Name) ??
(typeof answer !== 'object' ? answer : null),
})
);
}, },
selectedValues: { selectedValues: {
get() { get() {
@ -261,24 +289,25 @@ export default {
}, },
set(selectedAnswers) { set(selectedAnswers) {
this.$emit('update:modelValue', selectedAnswers); this.$emit('update:modelValue', selectedAnswers);
} },
} },
}, },
watch: { watch: {
modelValue(newValue) { modelValue(newValue) {
this.resetField({ this.resetField({
value: newValue value: newValue,
}); });
} },
}, },
beforeMount() { beforeMount() {
if (this.buttonTypeObject) { if (this.buttonTypeObject) {
this.$options.components[this.buttonTypeString] = this.buttonTypeObject; this.$options.components[this.buttonTypeString] =
this.buttonTypeObject;
} }
}, },
beforeUnmount() { beforeUnmount() {
this.resetField({ this.resetField({
value: '' value: '',
}); });
}, },
methods: { methods: {
@ -287,8 +316,8 @@ export default {
}, },
setLastValuePushedToGa(lastValuePushedToGa) { setLastValuePushedToGa(lastValuePushedToGa) {
this.lastValuePushedToGa = lastValuePushedToGa; this.lastValuePushedToGa = lastValuePushedToGa;
} },
} },
}; };
</script> </script>
@ -334,17 +363,17 @@ export default {
.small-question-label-text { .small-question-label-text {
&.question-text { &.question-text {
span { span {
text-align: left; text-align: left;
margin: 0 0 0.25rem 0; margin: 0 0 0.25rem 0;
} }
} }
&.question-text { &.question-text {
margin: 0; margin: 0;
} }
&fieldset { &fieldset {
.ui-radio { .ui-radio {
margin: 0; margin: 0;
} }
} }
} }
</style> </style>

View file

@ -6,7 +6,10 @@
tabindex="-1" tabindex="-1"
aria-labelledby="ModalComponentLabel" aria-labelledby="ModalComponentLabel"
aria-hidden="true" aria-hidden="true"
v-on="{ 'hidden.bs.modal': onModalClosed, 'shown.bs.modal': onModalOpened }"> v-on="{
'hidden.bs.modal': onModalClosed,
'shown.bs.modal': onModalOpened,
}">
<div class="modal-dialog modal-dialog-centered"> <div class="modal-dialog modal-dialog-centered">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header mb-2 mt-2"> <div class="modal-header mb-2 mt-2">
@ -33,7 +36,10 @@
class="w-100" class="w-100"
loaderColor="white" loaderColor="white"
:buttonText="footerButtonText" :buttonText="footerButtonText"
:class="(isButtonDisabled || isFooterButtonDisabled) && 'form-test-invalid'" :class="
(isButtonDisabled || isFooterButtonDisabled) &&
'form-test-invalid'
"
@clickEvent="validateAndEmit" /> @clickEvent="validateAndEmit" />
</div> </div>
</div> </div>
@ -50,23 +56,25 @@ export default {
// eslint-disable-next-line vue/multi-word-component-names // eslint-disable-next-line vue/multi-word-component-names
name: 'modal', name: 'modal',
components: { components: {
modalButtonMain modalButtonMain,
}, },
props: { props: {
modalId: String, modalId: String,
headerText: String, headerText: String,
footerButtonText: String, footerButtonText: String,
onModalOpenedCallback: { onModalOpenedCallback: {
type: Function type: Function,
}, },
onModalClosedCallback: { onModalClosedCallback: {
type: Function type: Function,
}, },
isButtonDisabled: Boolean isButtonDisabled: Boolean,
}, },
emits: ['footer-button-event', 'isModalOpened'], emits: ['footer-button-event', 'isModalOpened'],
setup(props) { setup(props) {
const modalId = props.modalId ? props.modalId : `modal-${crypto.randomUUID()}`; const modalId = props.modalId
? props.modalId
: `modal-${crypto.randomUUID()}`;
const { meta, validate, resetForm } = useForm(); const { meta, validate, resetForm } = useForm();
@ -75,7 +83,7 @@ export default {
modalId, modalId,
meta, meta,
validate, validate,
resetForm resetForm,
}; };
}, },
computed: { computed: {
@ -84,7 +92,7 @@ export default {
return !this.meta.valid; return !this.meta.valid;
} }
return !this.meta.dirty || !this.meta.valid; return !this.meta.dirty || !this.meta.valid;
} },
}, },
methods: { methods: {
async validateAndEmit() { async validateAndEmit() {
@ -106,16 +114,20 @@ export default {
this.onModalClosedCallback?.(); this.onModalClosedCallback?.();
}, },
openModal() { openModal() {
const modal = Modal.getOrCreateInstance(document.getElementById(this.modalId)); const modal = Modal.getOrCreateInstance(
document.getElementById(this.modalId)
);
modal?.show(); modal?.show();
this.$emit('isModalOpened', true); this.$emit('isModalOpened', true);
}, },
closeModal() { closeModal() {
const modal = Modal.getInstance(document.getElementById(this.modalId)); const modal = Modal.getInstance(
document.getElementById(this.modalId)
);
modal?.hide(); modal?.hide();
this.$emit('isModalOpened', false); this.$emit('isModalOpened', false);
} },
} },
}; };
</script> </script>

View file

@ -1,6 +1,6 @@
<template> <template>
<div class="footerImage"> <div class="footerImage">
<img :src="footerImageURL" /> <img :src="footerImageURL" />
</div> </div>
</template> </template>
<script> <script>
@ -11,15 +11,18 @@ export default {
data() { data() {
return { return {
widget: { widget: {
footer: 'SiteFooterWidget' footer: 'SiteFooterWidget',
} },
}; };
}, },
computed: { computed: {
footerImageURL() { footerImageURL() {
return this.getCmsContent(this.widget.footer, widgetFields.FOOTER_WIDGET.FOOTER_IMAGE_URL); return this.getCmsContent(
} this.widget.footer,
} widgetFields.FOOTER_WIDGET.FOOTER_IMAGE_URL
);
},
},
}; };
</script> </script>

View file

@ -1,50 +1,46 @@
<template> <template>
<div> <div>
<footer <footer id="infoBox" class="footer container-fluid g-5 my-5 px-0">
id="infoBox" <div
class="footer container-fluid g-5 my-5 px-0"> class="row d-flex vw-100 mx-0"
<div :class="[
class="row d-flex vw-100 mx-0" isStackedVertically
:class="[ ? 'flex-column align-items-stretch'
isStackedVertically : 'flex-row-reverse align-items-center',
? 'flex-column align-items-stretch' ]">
: 'flex-row-reverse align-items-center', <div id="stacked" class="col button-col d-flex px-0">
]"> <buttonMain
<div v-if="!isForwardButtonHidden"
id="stacked" ref="buttonMain"
class="col button-col d-flex px-0"> isPrimary
<buttonMain :buttonText="buttonText"
v-if="!isForwardButtonHidden" loaderColor="white"
ref="buttonMain" :class="
isPrimary (disableForwardAction || isForwardActionDisabled) &&
:buttonText="buttonText" 'form-test-invalid'
loaderColor="white" "
:class=" :aria-disabled="isForwardActionDisabled"
(disableForwardAction || isForwardActionDisabled) && :isDisabled="isForwardActionDisabled"
'form-test-invalid' data-bs-target="#footerModal"
" data-bs-dismiss="modal"
:aria-disabled="isForwardActionDisabled" data-test-id="site-footer-main-button"
:isDisabled="isForwardActionDisabled" @clickEvent="buttonClick" />
data-bs-target="#footerModal" </div>
data-bs-dismiss="modal" <div
data-test-id="site-footer-main-button" v-if="!isBackButtonHidden"
@clickEvent="buttonClick" /> class="col-auto link-col py-1 px-0 text-break">
</div> <textLink
<div linkType="navigation"
v-if="!isBackButtonHidden" :text="backLink"
class="col-auto link-col py-1 px-0 text-break"> href="javascript:void(0)"
<textLink data-bs-target="#footerModal"
linkType="navigation" data-bs-dismiss="modal"
:text="backLink" data-test-id="site-footer-back-button"
href="javascript:void(0)" @clickEvent="linkClick" />
data-bs-target="#footerModal" </div>
data-bs-dismiss="modal" </div>
data-test-id="site-footer-back-button" </footer>
@clickEvent="linkClick" /> </div>
</div>
</div>
</footer>
</div>
</template> </template>
<script> <script>
@ -56,21 +52,21 @@ export default {
name: 'site-footer', name: 'site-footer',
components: { components: {
textLink, textLink,
buttonMain buttonMain,
}, },
props: { props: {
isForwardActionDisabled: Boolean, isForwardActionDisabled: Boolean,
isBackButtonHidden: { type: Boolean, default: false }, isBackButtonHidden: { type: Boolean, default: false },
isForwardButtonHidden: { type: Boolean, default: false }, isForwardButtonHidden: { type: Boolean, default: false },
cmsWidgetName: String, cmsWidgetName: String,
isStackedVertically: { type: Boolean, default: false } isStackedVertically: { type: Boolean, default: false },
}, },
emits: ['backClicked', 'forwardClicked'], emits: ['backClicked', 'forwardClicked'],
data() { data() {
return { return {
paddingHeight: 0, paddingHeight: 0,
customButtontext: '', customButtontext: '',
disableForwardAction: false disableForwardAction: false,
}; };
}, },
computed: { computed: {
@ -83,13 +79,16 @@ export default {
: this.getCmsContent(this.cmsWidgetName, 'ForwardButtonText'); : this.getCmsContent(this.cmsWidgetName, 'ForwardButtonText');
}, },
footerImageURL() { footerImageURL() {
const output = this.getCmsContent(this.cmsWidgetName, 'FooterImageURL'); const output = this.getCmsContent(
this.cmsWidgetName,
'FooterImageURL'
);
return output; return output;
}, },
includeFooterImage() { includeFooterImage() {
const currentPageName = this.getPageNameByQueryString(); const currentPageName = this.getPageNameByQueryString();
return currentPageName.toLowerCase() === issPageValues.WELCOME_PAGE; return currentPageName.toLowerCase() === issPageValues.WELCOME_PAGE;
} },
}, },
mounted() { mounted() {
this.paddingHeight = this.includeFooterImage this.paddingHeight = this.includeFooterImage
@ -129,67 +128,67 @@ export default {
}, },
enableForwardAction() { enableForwardAction() {
this.disableForwardAction = false; this.disableForwardAction = false;
} },
} },
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.footer { .footer {
display: flex;
overflow: visible;
a {
display: flex; display: flex;
justify-content: center; overflow: visible;
} a {
.col, display: flex;
.col-auto, justify-content: center;
.col button { }
width: 100%;
}
@media only screen and (min-width: 340px) {
.col, .col,
.col-auto, .col-auto,
.col button { .col button {
width: auto; width: 100%;
justify-content: flex-end;
} }
.btn-primary { @media only screen and (min-width: 340px) {
width: auto; .col,
.col-auto,
.col button {
width: auto;
justify-content: flex-end;
}
.btn-primary {
width: auto;
}
a {
display: flex;
justify-content: flex-start;
}
} }
a {
display: flex;
justify-content: flex-start;
}
}
& > .container-fluid { & > .container-fluid {
overflow-x: visible; // needed to fix hidden footer on some iphones overflow-x: visible; // needed to fix hidden footer on some iphones
}
div.flex-column {
.btn-primary {
width: 100%;
justify-content: center;
} }
div.link-col { div.flex-column {
display: flex; .btn-primary {
justify-content: center; width: 100%;
margin-top: 1.25rem; justify-content: center;
}
div.link-col {
display: flex;
justify-content: center;
margin-top: 1.25rem;
}
} }
}
} }
.footerImage { .footerImage {
text-align: center; text-align: center;
position: sticky; position: sticky;
} }
@media only screen and (max-width: 340px) { @media only screen and (max-width: 340px) {
.footerImage { .footerImage {
bottom: 78px; bottom: 78px;
} }
} }
#siteFooterImage { #siteFooterImage {
width: 100%; width: 100%;
} }
</style> </style>

View file

@ -31,51 +31,49 @@
<div class="bar3"></div> <div class="bar3"></div>
</button> </button>
</div> </div>
<div class="modal-dialog modal-fullscreen"> <div class="modal-dialog modal-fullscreen">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header visually-hidden"> <div class="modal-header visually-hidden">
<h5 <h5 id="footerModalLabel" class="modal-title">
id="footerModalLabel" Footer Navigation
class="modal-title"> </h5>
Footer Navigation </div>
</h5> <div class="modal-body d-flex flex-column">
<textLink
linkType="navigation"
text="Terms of service"
href="//www.safelite.com/terms-of-use"
target="_blank" />
<textLink
linkType="navigation"
text="Your privacy choices"
href="//www.safelite.com/privacy-center"
target="_blank">
<template #after-text>
<img
class="ccpa-icon"
data-id="ccpa-icon"
src="~@/assets/img/icons/ccpa-icon.svg"
alt="Your privacy choices" />
</template>
</textLink>
<textLink
linkType="navigation"
text="Warranty"
href="//www.safelite.com/national-lifetime-warranty"
target="_blank" />
<textLink
linkType="navigation"
text="Notice at collection"
href="https://www.safelite.com/ccpa-privacy-policy"
target="_blank" />
</div>
<div class="modal-footer d-flex justify-content-start">
&copy; {{ new Date().getFullYear() }} Safelite Group
</div>
</div> </div>
<div class="modal-body d-flex flex-column">
<textLink
linkType="navigation"
text="Terms of service"
href="//www.safelite.com/terms-of-use"
target="_blank" />
<textLink
linkType="navigation"
text="Your privacy choices"
href="//www.safelite.com/privacy-center"
target="_blank">
<template #after-text>
<img
class="ccpa-icon"
data-id="ccpa-icon"
src="~@/assets/img/icons/ccpa-icon.svg"
alt="Your privacy choices" />
</template>
</textLink>
<textLink
linkType="navigation"
text="Warranty"
href="//www.safelite.com/national-lifetime-warranty"
target="_blank" />
<textLink
linkType="navigation"
text="Notice at collection"
href="https://www.safelite.com/ccpa-privacy-policy"
target="_blank" />
</div>
<div class="modal-footer d-flex justify-content-start">
&copy; {{ new Date().getFullYear() }} Safelite Group
</div> </div>
</div> </div>
</div>
</div>
</template> </template>
<script> <script>
@ -85,12 +83,12 @@ import { Modal } from 'bootstrap';
export default { export default {
name: 'menu-modal', name: 'menu-modal',
components: { components: {
textLink textLink,
}, },
data() { data() {
return { return {
isActive: false, isActive: false,
currentFooterAndHeaderHeight: 0 currentFooterAndHeaderHeight: 0,
}; };
}, },
methods: { methods: {
@ -98,20 +96,26 @@ export default {
if (this.isActive) { if (this.isActive) {
// hide modal // hide modal
this.isActive = false; this.isActive = false;
const modal = Modal.getInstance(document.getElementById('footerModal')); const modal = Modal.getInstance(
document.getElementById('footerModal')
);
modal?.hide(); modal?.hide();
} else { } else {
// show modal // show modal
this.currentFooterAndHeaderHeight = this.getFooterInfoBoxHeight() + 56; this.currentFooterAndHeaderHeight =
this.getFooterInfoBoxHeight() + 56;
this.isActive = true; this.isActive = true;
const modal = Modal.getOrCreateInstance(document.getElementById('footerModal')); const modal = Modal.getOrCreateInstance(
document.getElementById('footerModal')
);
modal?.show(); modal?.show();
document.querySelector('.fade-on-route-transition').scrollTo({ document.querySelector('.fade-on-route-transition').scrollTo({
top: 0, behavior: 'instant' top: 0,
behavior: 'instant',
}); });
} }
} },
} },
}; };
</script> </script>
@ -126,14 +130,14 @@ export default {
width: 1.5rem; width: 1.5rem;
height: 1.5rem; height: 1.5rem;
border-radius: 50%; border-radius: 50%;
box-shadow: 0 2px 8px 0 rgba(0,0,0,0.2); box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.2);
background-color: $white; background-color: $white;
position: relative; position: relative;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
padding: 0;//Required to prevent 'squish' on iPhone padding: 0; //Required to prevent 'squish' on iPhone
.bar1, .bar1,
.bar2, .bar2,
.bar3 { .bar3 {
@ -146,7 +150,9 @@ export default {
&.active .bar1 { &.active .bar1 {
transform: rotate(-45deg) translate(-3px, 3px); transform: rotate(-45deg) translate(-3px, 3px);
} }
&.active .bar2 {opacity: 0;} &.active .bar2 {
opacity: 0;
}
&.active .bar3 { &.active .bar3 {
transform: rotate(45deg) translate(-3px, -3px); transform: rotate(45deg) translate(-3px, -3px);
} }
@ -184,7 +190,7 @@ export default {
.menu-modal-container { .menu-modal-container {
position: absolute; position: absolute;
padding: 1.47rem 1rem 1.47rem 1.47rem; padding: 1.47rem 1rem 1.47rem 1.47rem;
right: .5rem; right: 0.5rem;
top: -4.5rem; top: -4.5rem;
button { button {
border: none; border: none;
@ -195,8 +201,8 @@ export default {
box-shadow: none; box-shadow: none;
background-color: $white; background-color: $white;
position: relative; position: relative;
right: -.5rem; right: -0.5rem;
top: .5rem; top: 0.5rem;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;

View file

@ -1,104 +1,103 @@
<template> <template>
<Form <Form
ref="contact-details-form" ref="contact-details-form"
v-slot="{ meta }" v-slot="{ meta }"
@submit="onSubmit" @submit="onSubmit"
@invalidSubmit="onInvalidSubmit"> @invalidSubmit="onInvalidSubmit">
<div class="container-fluid fade-on-route-transition"> <div class="container-fluid fade-on-route-transition">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6 px-0 px-md-2"> <div class="col-md-6 px-0 px-md-2">
<siteHeader <siteHeader
ref="siteHeader" ref="siteHeader"
:cmsWidgetName="widget.siteHeader" /> :cmsWidgetName="widget.siteHeader" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<siteSubHeader
id="sub-header"
ref="siteSubHeader"
class="mt-5"
:cmsWidgetName="widget.siteSubHeader" />
<textboxQuestion
ref="firstNameQuestion"
v-model="firstName"
class="mt-6"
inputId="firstName"
:cmsWidgetName="widget.firstNameQuestion"
isRequired
:validationRules="rules.firstName" />
<textboxQuestion
ref="lastNameQuestion"
v-model="lastName"
class="mt-5"
inputId="lastName"
:cmsWidgetName="widget.lastNameQuestion"
isRequired
:validationRules="rules.lastName" />
<textboxQuestion
ref="emailQuestion"
v-model="emailAddress"
class="mt-5"
inputId="emailAddress"
:cmsWidgetName="widget.emailQuestion"
isRequired
:validationRules="rules.emailAddress" />
<textboxQuestion
ref="phoneNumberQuestion"
v-model="phoneNumber"
class="mt-5"
inputId="phoneNumber"
:cmsWidgetName="widget.phoneNumberQuestion"
isRequired
:mask="phoneMask"
:validationRules="rules.phoneNumber" />
<checkbox
ref="requestTextUpdatesCheckbox"
v-model="requestTextUpdates"
:isChecked="requestTextUpdates"
class="mt-3"
checkboxName="requestTextUpdates"
buttonID="requestTextUpdates"
:checkboxLabel="requestTextUpdatesCheckboxText" />
<textareaQuestion
ref="notesQuestion"
v-model="notesForTechnician"
class="mt-5"
inputId="technicianNotes"
:isDisabled="false"
:isRequired="false"
:cmsWidgetName="widget.notesQuestion"
maxLength="250"
:inputRows="4" />
<p ref="disclaimerText" class="caption dark-gray mt-6">
{{ textUpdateDisclaimerText }} I also agree to
Safelite's
<textLink
ref="privacyPolicyLink"
class="normal-line-height"
linkType="text"
text="Privacy Policy"
href="//www.safelite.com/privacy-center" />
and
<textLink
ref="termsOfUseLink"
class="normal-line-height"
linkType="text"
text="Terms of Use"
href="//www.safelite.com/terms-of-use" />.
</p>
<siteFooter
ref="siteFooter"
class="my-5"
:cmsWidgetName="widget.siteFooter"
:isForwardActionDisabled="!meta.valid"
@forwardClicked="forwardButtonAction"
@backClicked="navigateBack" />
</div>
</div>
</div> </div>
</div> </Form>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<siteSubHeader
id="sub-header"
ref="siteSubHeader"
class="mt-5"
:cmsWidgetName="widget.siteSubHeader" />
<textboxQuestion
ref="firstNameQuestion"
v-model="firstName"
class="mt-6"
inputId="firstName"
:cmsWidgetName="widget.firstNameQuestion"
isRequired
:validationRules="rules.firstName" />
<textboxQuestion
ref="lastNameQuestion"
v-model="lastName"
class="mt-5"
inputId="lastName"
:cmsWidgetName="widget.lastNameQuestion"
isRequired
:validationRules="rules.lastName" />
<textboxQuestion
ref="emailQuestion"
v-model="emailAddress"
class="mt-5"
inputId="emailAddress"
:cmsWidgetName="widget.emailQuestion"
isRequired
:validationRules="rules.emailAddress" />
<textboxQuestion
ref="phoneNumberQuestion"
v-model="phoneNumber"
class="mt-5"
inputId="phoneNumber"
:cmsWidgetName="widget.phoneNumberQuestion"
isRequired
:mask="phoneMask"
:validationRules="rules.phoneNumber" />
<checkbox
ref="requestTextUpdatesCheckbox"
v-model="requestTextUpdates"
:isChecked="requestTextUpdates"
class="mt-3"
checkboxName="requestTextUpdates"
buttonID="requestTextUpdates"
:checkboxLabel="requestTextUpdatesCheckboxText" />
<textareaQuestion
ref="notesQuestion"
v-model="notesForTechnician"
class="mt-5"
inputId="technicianNotes"
:isDisabled="false"
:isRequired="false"
:cmsWidgetName="widget.notesQuestion"
maxLength="250"
:inputRows="4" />
<p
ref="disclaimerText"
class="caption dark-gray mt-6">
{{ textUpdateDisclaimerText }} I also agree to Safelite's
<textLink
ref="privacyPolicyLink"
class="normal-line-height"
linkType="text"
text="Privacy Policy"
href="//www.safelite.com/privacy-center" />
and
<textLink
ref="termsOfUseLink"
class="normal-line-height"
linkType="text"
text="Terms of Use"
href="//www.safelite.com/terms-of-use" />.
</p>
<siteFooter
ref="siteFooter"
class="my-5"
:cmsWidgetName="widget.siteFooter"
:isForwardActionDisabled="!meta.valid"
@forwardClicked="forwardButtonAction"
@backClicked="navigateBack" />
</div>
</div>
</div>
</Form>
</template> </template>
<script> <script>
// Components // Components
@ -129,7 +128,7 @@ export default {
siteFooter, siteFooter,
// eslint-disable-next-line vue/no-reserved-component-names // eslint-disable-next-line vue/no-reserved-component-names
Form, Form,
textLink textLink,
}, },
mixins: [BaseFormMixin], mixins: [BaseFormMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
@ -146,7 +145,7 @@ export default {
emailAddress, emailAddress,
servicePhone, servicePhone,
requestTextUpdates, requestTextUpdates,
notesForTechnician notesForTechnician,
} = useMainStore().contactInfo; } = useMainStore().contactInfo;
return { return {
firstName, firstName,
@ -165,22 +164,25 @@ export default {
requestTextUpdates: 'TextContentWidget', requestTextUpdates: 'TextContentWidget',
notesQuestion: 'NotesQuestionWidget', notesQuestion: 'NotesQuestionWidget',
disclaimer: 'TextUpdateDisclaimerWidget', disclaimer: 'TextUpdateDisclaimerWidget',
siteFooter: 'SiteFooterWidget' siteFooter: 'SiteFooterWidget',
}, },
rules: { rules: {
firstName: globalRules.FIRST_NAME_REQUIRED, firstName: globalRules.FIRST_NAME_REQUIRED,
lastName: globalRules.LAST_NAME_REQUIRED, lastName: globalRules.LAST_NAME_REQUIRED,
emailAddress: `${globalRules.EMAIL_ADDRESS_REQUIRED}|${globalRules.EMAIL_ADDRESS_FORMAT}`, emailAddress: `${globalRules.EMAIL_ADDRESS_REQUIRED}|${globalRules.EMAIL_ADDRESS_FORMAT}`,
phoneNumber: `${globalRules.PHONE_NUMBER_REQUIRED}|${globalRules.PHONE_NUMBER_FORMAT}` phoneNumber: `${globalRules.PHONE_NUMBER_REQUIRED}|${globalRules.PHONE_NUMBER_FORMAT}`,
} },
}; };
}, },
computed: { computed: {
/** /**
* @returns {string} Returns the CMS text associated with the "get text updates" checkbox. * @returns {string} Returns the CMS text associated with the "get text updates" checkbox.
*/ */
requestTextUpdatesCheckboxText() { requestTextUpdatesCheckboxText() {
return `${this.getCmsContent(this.widget.requestTextUpdates, 'Text')}*`; return `${this.getCmsContent(
this.widget.requestTextUpdates,
'Text'
)}*`;
}, },
/** /**
* @returns {string} Returns the CMS text associated with the "get text updates" checkbox. * @returns {string} Returns the CMS text associated with the "get text updates" checkbox.
@ -190,7 +192,7 @@ export default {
}, },
phoneMask() { phoneMask() {
return MaskaFormattedMasks.PHONE_NUMBER; return MaskaFormattedMasks.PHONE_NUMBER;
} },
}, },
methods: { methods: {
/** /**
@ -202,37 +204,41 @@ export default {
lastName: this.lastName, lastName: this.lastName,
emailAddress: this.emailAddress, emailAddress: this.emailAddress,
requestTextUpdates: this.requestTextUpdates, requestTextUpdates: this.requestTextUpdates,
notesForTechnician: this.notesForTechnician notesForTechnician: this.notesForTechnician,
}; };
useMainStore().updateContactInfo(contactInfo); useMainStore().updateContactInfo(contactInfo);
if (this.requestTextUpdates) { if (this.requestTextUpdates) {
useMainStore().updatePhoneNumbers({ useMainStore().updatePhoneNumbers({
service: this.phoneNumber, service: this.phoneNumber,
alternative: this.phoneNumber alternative: this.phoneNumber,
}); });
} else { } else {
useMainStore().updatePhoneNumbers({ useMainStore().updatePhoneNumbers({
home: this.phoneNumber, home: this.phoneNumber,
service: this.phoneNumber service: this.phoneNumber,
}); });
} }
const scenario = useMainStore().order.serviceLocation.IsSafeliteProvider === false const scenario =
? this.navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP useMainStore().order.serviceLocation.IsSafeliteProvider ===
: this.navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE_SHOP; false
? this.navigationScenarios
.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP
: this.navigationScenarios
.CLICKED_FORWARD_WITH_SAFELITE_SHOP;
this.$router.navigate(scenario, this.$route); this.$router.navigate(scenario, this.$route);
} },
} },
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.dark-gray { .dark-gray {
color: $darker-gray; color: $darker-gray;
} }
.normal-line-height { .normal-line-height {
line-height: normal; line-height: normal;
} }
</style> </style>

View file

@ -1,99 +1,97 @@
<template> <template>
<Form <Form
ref="theForm" ref="theForm"
v-slot="{ meta }" v-slot="{ meta }"
@submit="onSubmit" @submit="onSubmit"
@invalidSubmit="onInvalidSubmit"> @invalidSubmit="onInvalidSubmit">
<div class="container-fluid fade-on-route-transition"> <div class="container-fluid fade-on-route-transition">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6 px-0 px-md-2"> <div class="col-md-6 px-0 px-md-2">
<loadingModal <loadingModal
ref="loadingModal" ref="loadingModal"
:textSlides="loadingText" /> :textSlides="loadingText" />
<siteHeader <siteHeader
ref="siteHeader" ref="siteHeader"
cmsWidgetName="SiteHeaderWidget" /> cmsWidgetName="SiteHeaderWidget" />
</div>
</div>
<div class="row justify-content-center pt-5">
<div class="col-md-6 col-xl-4">
<h5
ref="siteSubHeader"
class="text-center text-black"
v-html="coverageStatementSubHeader"></h5>
<div
ref="explanatoryText"
class="body-text text-center mt-2"
v-html="explanatoryText"></div>
<div
ref="secondaryText"
class="text-center mt-4 mb-1 fw-bold text-black"
v-html="secondaryText"></div>
<div
v-if="verifiedDeductible"
class="d-flex justify-content-center cost">
{{ deductibleForDisplay }}
</div>
<div
v-if="isQuoteDisplayed"
class="d-flex justify-content-center cost mb-0">
{{ servicePriceForDisplay }}
</div>
<div
v-if="verifiedITAC"
class="d-flex justify-content-center mb-4 deductible-text">
{{ deductibleText }}&nbsp;
<span class="text-success fw-bold">{{
deductibleForDisplay
}}</span>
</div>
<alert
v-if="verifiedITAC"
ref="verifiedITACAlert"
class="mb-5"
cmsWidgetName="VerifiedITACAlert"
:manualHeadline="verifiedItacAlertHeader"
:manualCopy="verifiedItacAlertBody"
alertClass="alert-success"
:isDismissible="false">
</alert>
<div
class="fw-bold text-black mt-5 mb-2"
v-html="nextStepsHeader"></div>
<div class="body-text" v-html="nextStepsBody"></div>
<buttonQuestion
v-if="isQuoteDisplayed"
v-model="selectedProvider"
cmsWidgetName="ServiceProviderQuestion"
:questionText="serviceProviderQuestionText"
:answers="serviceProviderQuestionAnswers"
groupName="ServiceProviderQuestionOption"
buttonTypeString="listButton"
isRequired
:validationRules="rules.selectionRequired">
</buttonQuestion>
<text-block
v-if="isQuoteDisplayed"
cmsWidgetName="DisclaimerWidget"
typeStyle="caption" />
<siteFooter
ref="siteFooter"
class="mt-5"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid"
@backClicked="navigateBackByVehicleQuestions"
@forwardClicked="navigateForward" />
</div>
</div>
</div> </div>
</div> <recalModal ref="RecalModal" cmsWidgetName="RecalModal" />
<div class="row justify-content-center pt-5"> <contentGroupModal
<div class="col-md-6 col-xl-4"> ref="DeductibleModal"
<h5 cmsWidgetName="DeductibleModal"
ref="siteSubHeader" class="deductible-modal" />
class="text-center text-black" </Form>
v-html="coverageStatementSubHeader"></h5>
<div
ref="explanatoryText"
class="body-text text-center mt-2"
v-html="explanatoryText"></div>
<div
ref="secondaryText"
class="text-center mt-4 mb-1 fw-bold text-black"
v-html="secondaryText"></div>
<div
v-if="verifiedDeductible"
class="d-flex justify-content-center cost">
{{ deductibleForDisplay }}
</div>
<div
v-if="isQuoteDisplayed"
class="d-flex justify-content-center cost mb-0">
{{ servicePriceForDisplay }}
</div>
<div
v-if="verifiedITAC"
class="d-flex justify-content-center mb-4 deductible-text">
{{ deductibleText }}&nbsp;
<span class="text-success fw-bold">{{ deductibleForDisplay }}</span>
</div>
<alert
v-if="verifiedITAC"
ref="verifiedITACAlert"
class="mb-5"
cmsWidgetName="VerifiedITACAlert"
:manualHeadline="verifiedItacAlertHeader"
:manualCopy="verifiedItacAlertBody"
alertClass="alert-success"
:isDismissible="false">
</alert>
<div
class="fw-bold text-black mt-5 mb-2"
v-html="nextStepsHeader"></div>
<div
class="body-text"
v-html="nextStepsBody"></div>
<buttonQuestion
v-if="isQuoteDisplayed"
v-model="selectedProvider"
cmsWidgetName="ServiceProviderQuestion"
:questionText="serviceProviderQuestionText"
:answers="serviceProviderQuestionAnswers"
groupName="ServiceProviderQuestionOption"
buttonTypeString="listButton"
isRequired
:validationRules="rules.selectionRequired">
</buttonQuestion>
<text-block
v-if="isQuoteDisplayed"
cmsWidgetName="DisclaimerWidget"
typeStyle="caption" />
<siteFooter
ref="siteFooter"
class="mt-5"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid"
@backClicked="navigateBackByVehicleQuestions"
@forwardClicked="navigateForward" />
</div>
</div>
</div>
<recalModal
ref="RecalModal"
cmsWidgetName="RecalModal" />
<contentGroupModal
ref="DeductibleModal"
cmsWidgetName="DeductibleModal"
class="deductible-modal" />
</Form>
</template> </template>
<script> <script>
// Import Component // Import Component
@ -111,7 +109,7 @@ import textBlock from '@/digital-components/text-block/text-block.vue';
import { import {
fetchCmsContentForPage, fetchCmsContentForPage,
setupModalLinks, setupModalLinks,
processIfStatements processIfStatements,
} from '@/helpers/cms-content-helper.js'; } from '@/helpers/cms-content-helper.js';
import settleAllPromises from '@/helpers/layout-helper.js'; import settleAllPromises from '@/helpers/layout-helper.js';
import { getDamageString } from '@/helpers/damage-helper.js'; import { getDamageString } from '@/helpers/damage-helper.js';
@ -141,24 +139,25 @@ export default {
contentGroupModal, contentGroupModal,
buttonQuestion, buttonQuestion,
loadingModal, loadingModal,
textBlock textBlock,
}, },
mixins: [baseFormMixin, vehicleQuestionsMixin], mixins: [baseFormMixin, vehicleQuestionsMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
// Call APIs // Call APIs
const cmsContentPromise = fetchCmsContentForPage(to?.query?.issPage); const cmsContentPromise = fetchCmsContentForPage(to?.query?.issPage);
const supportingItemsPromise = await useMainStore().getSupportingItems(); const supportingItemsPromise =
await useMainStore().getSupportingItems();
// Settle promises and get results // Settle promises and get results
const promiseResultMap = [ const promiseResultMap = [
{ {
resultKey: 'cmsContent', resultKey: 'cmsContent',
promise: cmsContentPromise promise: cmsContentPromise,
}, },
{ {
resultKey: 'supportingItems', resultKey: 'supportingItems',
promise: supportingItemsPromise promise: supportingItemsPromise,
} },
]; ];
const resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
@ -167,23 +166,29 @@ export default {
: []; : [];
const availableLineItems = [ const availableLineItems = [
...(resultMap.supportingItems ?? []), ...(resultMap.supportingItems ?? []),
...(clonedGlassParts ?? []) ...(clonedGlassParts ?? []),
]; ];
let hasBailedOut = false; let hasBailedOut = false;
let pricingResults = []; let pricingResults = [];
if ( if (
useMainStore().policy.policyLookupSuccessful useMainStore().policy.policyLookupSuccessful &&
&& useMainStore().vehicle.policyVehicleId >= 0 useMainStore().vehicle.policyVehicleId >= 0
) { ) {
await useMainStore().getFinalDeductible(); await useMainStore().getFinalDeductible();
pricingResults = await useMainStore() pricingResults = await useMainStore()
.getPriceOrderItems(availableLineItems) .getPriceOrderItems(availableLineItems)
.catch((err) => { .catch((err) => {
useMainStore().setBailout(bailoutMessage.pricingResponseError( useMainStore().setBailout(
availableLineItems.map((li) => li.partNumber), bailoutMessage.pricingResponseError(
{ code: err.code, message: err.message, data: err.data } availableLineItems.map((li) => li.partNumber),
)); {
code: err.code,
message: err.message,
data: err.data,
}
)
);
hasBailedOut = true; hasBailedOut = true;
next(`/?issPage=${issPageValues.BAILOUT_PAGE}`); next(`/?issPage=${issPageValues.BAILOUT_PAGE}`);
}); });
@ -218,10 +223,10 @@ export default {
loadingText: [ loadingText: [
'Connecting to your insurance company', 'Connecting to your insurance company',
'Nearly there', 'Nearly there',
'Finishing up' 'Finishing up',
], ],
rules: { rules: {
selectionRequired: globalRules.OPTION_REQUIRED selectionRequired: globalRules.OPTION_REQUIRED,
}, },
supportingItems: null, supportingItems: null,
widget: { widget: {
@ -229,8 +234,8 @@ export default {
verifiedItacAlert: 'VerifiedITACAlert', verifiedItacAlert: 'VerifiedITACAlert',
explanatoryText: 'ExplanatoryTextWidget', explanatoryText: 'ExplanatoryTextWidget',
nextStep: 'NextStepsWidget', nextStep: 'NextStepsWidget',
serviceProviderQuestion: 'ServiceProviderQuestion' serviceProviderQuestion: 'ServiceProviderQuestion',
} },
}; };
}, },
computed: { computed: {
@ -250,7 +255,10 @@ export default {
return this.getCmsContent( return this.getCmsContent(
this.widget.verifiedItacAlert, this.widget.verifiedItacAlert,
widgetFields.ALERT_WIDGET.BODY_TEXT widgetFields.ALERT_WIDGET.BODY_TEXT
)?.replaceAll('{custom:costSavings}', this.itacCostSavingsForDisplay); )?.replaceAll(
'{custom:costSavings}',
this.itacCostSavingsForDisplay
);
}, },
secondaryText() { secondaryText() {
return this.getTextFromCmsWithCustomIfStatements( return this.getTextFromCmsWithCustomIfStatements(
@ -294,33 +302,37 @@ export default {
}, },
verifiedITAC() { verifiedITAC() {
return ( return (
this.policyLookupSuccessful this.policyLookupSuccessful &&
&& !this.isNoComp !this.isNoComp &&
&& this.deductibleValue > this.totalServicePrice this.deductibleValue > this.totalServicePrice
); );
}, },
coveredAndServicePriceAboveOrEqualDeductible() { coveredAndServicePriceAboveOrEqualDeductible() {
return ( return (
!this.verifiedNoComp && this.totalServicePrice >= this.deductibleValue !this.verifiedNoComp &&
this.totalServicePrice >= this.deductibleValue
); );
}, },
verifiedDeductible() { verifiedDeductible() {
return useMainStore().isClaimRegistrationRequired return useMainStore().isClaimRegistrationRequired
? this.registerClaimSuccessful ? this.registerClaimSuccessful &&
&& this.coveredAndServicePriceAboveOrEqualDeductible this.coveredAndServicePriceAboveOrEqualDeductible &&
&& this.deductibleValue !== null this.deductibleValue !== null
: this.policyLookupSuccessful : this.policyLookupSuccessful &&
&& this.coveredAndServicePriceAboveOrEqualDeductible; this.coveredAndServicePriceAboveOrEqualDeductible;
}, },
unverified() { unverified() {
return ( return (
!this.verifiedDeductible && !this.verifiedITAC && !this.verifiedNoComp !this.verifiedDeductible &&
!this.verifiedITAC &&
!this.verifiedNoComp
); );
}, },
isADAS() { isADAS() {
const parts = useMainStore().order.lineItems.glassParts; const parts = useMainStore().order.lineItems.glassParts;
return ( return (
parts !== null && !!parts.find((part) => part.requiresRecalibration) parts !== null &&
!!parts.find((part) => part.requiresRecalibration)
); );
}, },
totalServicePrice() { totalServicePrice() {
@ -352,21 +364,22 @@ export default {
}, },
shouldRegisterClaim() { shouldRegisterClaim() {
return ( return (
this.policyLookupSuccessful this.policyLookupSuccessful &&
&& useMainStore().vehicle.policyVehicleId != null useMainStore().vehicle.policyVehicleId != null &&
&& useMainStore().vehicle.policyVehicleId >= 0 useMainStore().vehicle.policyVehicleId >= 0 &&
&& useMainStore().isClaimRegistrationRequired useMainStore().isClaimRegistrationRequired &&
&& !useMainStore().isClaimAlreadyRegistered !useMainStore().isClaimAlreadyRegistered &&
&& (this.coveredAndServicePriceAboveOrEqualDeductible || this.verifiedITAC) (this.coveredAndServicePriceAboveOrEqualDeductible ||
this.verifiedITAC)
); );
} },
}, },
watch: { watch: {
selectedProvider() { selectedProvider() {
const buttonText = const buttonText =
this.selectedProvider === SAFELITE_PROVIDER this.selectedProvider === SAFELITE_PROVIDER
? 'Continue with Safelite' ? 'Continue with Safelite'
: 'Safelite'; : 'Safelite';
this.$refs.siteFooter.updateButtonText(buttonText); this.$refs.siteFooter.updateButtonText(buttonText);
}, },
nextStepsBody(newValue, oldValue) { nextStepsBody(newValue, oldValue) {
@ -374,7 +387,7 @@ export default {
setupModalLinks(this, 'RecalModal'); setupModalLinks(this, 'RecalModal');
setupModalLinks(this, 'DeductibleModal'); setupModalLinks(this, 'DeductibleModal');
} }
} },
}, },
mounted() { mounted() {
setupModalLinks(this); setupModalLinks(this);
@ -386,9 +399,9 @@ export default {
async initializeComponent() { async initializeComponent() {
useMainStore().updatePolicyITACFlag(this.verifiedITAC); useMainStore().updatePolicyITACFlag(this.verifiedITAC);
const coverageStatus = const coverageStatus =
this.verifiedITAC || this.verifiedNoComp this.verifiedITAC || this.verifiedNoComp
? coverageStatuses.VERIFIED ? coverageStatuses.VERIFIED
: coverageStatuses.PENDING; : coverageStatuses.PENDING;
useMainStore().updateCoverageStatus(coverageStatus); useMainStore().updateCoverageStatus(coverageStatus);
if (this.shouldRegisterClaim) { if (this.shouldRegisterClaim) {
await useMainStore() await useMainStore()
@ -402,17 +415,27 @@ export default {
useMainStore().updateSupportingItems(this.supportingItems); useMainStore().updateSupportingItems(this.supportingItems);
this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD); this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD);
} else if (this.verifiedITAC || this.verifiedNoComp) { } else if (this.verifiedITAC || this.verifiedNoComp) {
useMainStore().updateIsSafeliteProvider(this.selectedProvider === SAFELITE_PROVIDER); useMainStore().updateIsSafeliteProvider(
this.selectedProvider === SAFELITE_PROVIDER
);
if (this.selectedProvider === SAFELITE_PROVIDER) { if (this.selectedProvider === SAFELITE_PROVIDER) {
useMainStore().updateSupportingItems(this.supportingItems); useMainStore().updateSupportingItems(this.supportingItems);
this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE); this.navigateWithScenario(
navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE
);
} else { } else {
useMainStore().setBailout(bailoutMessage.RequestCallback()); useMainStore().setBailout(bailoutMessage.RequestCallback());
this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP); this.navigateWithScenario(
navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP
);
} }
} else { } else {
useMainStore().setBailout(bailoutMessage.coverageStatementInvalidState()); useMainStore().setBailout(
this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD_WITH_INVALID_STATE); bailoutMessage.coverageStatementInvalidState()
);
this.navigateWithScenario(
navigationScenarios.CLICKED_FORWARD_WITH_INVALID_STATE
);
} }
}, },
navigateWithScenario(scenario) { navigateWithScenario(scenario) {
@ -443,9 +466,13 @@ export default {
case 'nonADASRepair': case 'nonADASRepair':
return this.isRepair; return this.isRepair;
case 'deductibleOverZero': case 'deductibleOverZero':
return this.verifiedDeductible && this.deductibleValue !== 0; // TODO what if deductible is negative? return (
this.verifiedDeductible && this.deductibleValue !== 0
); // TODO what if deductible is negative?
case 'isDeductibleZero': case 'isDeductibleZero':
return this.verifiedDeductible && this.deductibleValue === 0; return (
this.verifiedDeductible && this.deductibleValue === 0
);
default: default:
return null; return null;
} }
@ -455,59 +482,59 @@ export default {
}, },
setBaseServiceLineItems(lineItems) { setBaseServiceLineItems(lineItems) {
this.baseServiceLineItems = lineItems; this.baseServiceLineItems = lineItems;
} },
} },
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.cost { .cost {
color: $green; color: $green;
font-size: 2rem; font-size: 2rem;
font-weight: $font-weight-light; font-weight: $font-weight-light;
line-height: 2.75rem; line-height: 2.75rem;
} }
.deductible-text { .deductible-text {
line-height: 1.5rem; line-height: 1.5rem;
} }
:deep p { :deep p {
line-height: 1.5rem; line-height: 1.5rem;
font-size: 0.875rem; font-size: 0.875rem;
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
strong { strong {
color: $black; color: $black;
} }
} }
:deep .question-text { :deep .question-text {
margin-top: 1.5rem; margin-top: 1.5rem;
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
font-size: 1rem; font-size: 1rem;
line-height: 1.5rem; line-height: 1.5rem;
& > span { & > span {
text-align: left; text-align: left;
} }
} }
:deep .deductible-modal { :deep .deductible-modal {
p { p {
margin-bottom: 0 !important; margin-bottom: 0 !important;
font-size: 1rem; font-size: 1rem;
line-height: 1.625rem; line-height: 1.625rem;
} }
img.mb-4 { img.mb-4 {
margin: 0 !important; margin: 0 !important;
} }
h5 { h5 {
color: black; color: black;
} }
p:last-child { p:last-child {
margin-top: 0.5rem; margin-top: 0.5rem;
} }
} }
.modal { .modal {
overflow: hidden; overflow: hidden;
} }
</style> </style>

View file

@ -1,46 +1,46 @@
<template> <template>
<Form <Form
ref="duplicate-check-form" ref="duplicate-check-form"
v-slot="{ meta }" v-slot="{ meta }"
@submit="onSubmit" @submit="onSubmit"
@invalidSubmit="onInvalidSubmit"> @invalidSubmit="onInvalidSubmit">
<div class="container-fluid fade-on-route-transition"> <div class="container-fluid fade-on-route-transition">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6 px-0 px-md-2"> <div class="col-md-6 px-0 px-md-2">
<siteHeader <siteHeader
ref="siteHeader" ref="siteHeader"
:cmsWidgetName="widget.siteHeader" /> :cmsWidgetName="widget.siteHeader" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<siteSubHeader
id="sub-header"
ref="siteSubHeader"
class="mt-5 duplicate-check-subheader"
:cmsWidgetName="widget.siteSubHeader" />
<buttonQuestion
ref="buttonQuestion"
v-model="selectedAnswer"
class="duplicate-check-question"
:cmsWidgetName="widget.existingOrNewQuestion"
:questionText="questionText"
:answers="answers"
groupName="existingOrNewQuestionOption"
buttonTypeString="listButton"
isRequired
:validationRules="rules.selectionRequired" />
<siteFooter
ref="siteFooter"
class="my-5"
:cmsWidgetName="widget.siteFooter"
:isForwardActionDisabled="!meta.valid"
@forwardClicked="forwardButtonAction"
@backClicked="navigateBack" />
</div>
</div>
</div> </div>
</div> </Form>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<siteSubHeader
id="sub-header"
ref="siteSubHeader"
class="mt-5 duplicate-check-subheader"
:cmsWidgetName="widget.siteSubHeader" />
<buttonQuestion
ref="buttonQuestion"
v-model="selectedAnswer"
class="duplicate-check-question"
:cmsWidgetName="widget.existingOrNewQuestion"
:questionText="questionText"
:answers="answers"
groupName="existingOrNewQuestionOption"
buttonTypeString="listButton"
isRequired
:validationRules="rules.selectionRequired" />
<siteFooter
ref="siteFooter"
class="my-5"
:cmsWidgetName="widget.siteFooter"
:isForwardActionDisabled="!meta.valid"
@forwardClicked="forwardButtonAction"
@backClicked="navigateBack" />
</div>
</div>
</div>
</Form>
</template> </template>
<script> <script>
@ -66,7 +66,7 @@ export default {
buttonQuestion, buttonQuestion,
siteFooter, siteFooter,
// eslint-disable-next-line vue/no-reserved-component-names // eslint-disable-next-line vue/no-reserved-component-names
Form Form,
}, },
mixins: [BaseFormMixin], mixins: [BaseFormMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
@ -83,11 +83,11 @@ export default {
siteHeader: 'SiteHeaderWidget', siteHeader: 'SiteHeaderWidget',
siteSubHeader: 'SiteSubHeaderWidget', siteSubHeader: 'SiteSubHeaderWidget',
existingOrNewQuestion: 'ExistingOrNewQuestion', existingOrNewQuestion: 'ExistingOrNewQuestion',
siteFooter: 'SiteFooterWidget' siteFooter: 'SiteFooterWidget',
}, },
rules: { rules: {
selectionRequired: globalRules.OPTION_REQUIRED selectionRequired: globalRules.OPTION_REQUIRED,
} },
}; };
}, },
computed: { computed: {
@ -99,7 +99,10 @@ export default {
}, },
answersFromCms() { answersFromCms() {
return ( return (
this.getCmsContent(this.widget.existingOrNewQuestion, 'Answers') ?? [] this.getCmsContent(
this.widget.existingOrNewQuestion,
'Answers'
) ?? []
); );
}, },
getNewOrderSelectionName() { getNewOrderSelectionName() {
@ -111,40 +114,40 @@ export default {
return ( return (
orders?.map((o) => { orders?.map((o) => {
const vehicle = const vehicle =
!!o.vehicleYear && !!o.vehicleMake && !!o.vehicleModel !!o.vehicleYear && !!o.vehicleMake && !!o.vehicleModel
? `${o.vehicleYear} ${o.vehicleMake} ${o.vehicleModel}` ? `${o.vehicleYear} ${o.vehicleMake} ${o.vehicleModel}`
: null; : null;
const dateOfLoss = const dateOfLoss =
o.responseDate == null o.responseDate == null
? '' ? ''
: new Date(o.responseDate).toLocaleDateString(); : new Date(o.responseDate).toLocaleDateString();
const subtext = const subtext =
vehicle && o.responseDate vehicle && o.responseDate
? `${vehicle}, ${dateOfLoss}` ? `${vehicle}, ${dateOfLoss}`
: (vehicle ?? '').concat(dateOfLoss); : (vehicle ?? '').concat(dateOfLoss);
return { return {
Text: duplicateOrderText, Text: duplicateOrderText,
Name: o.referralNumber, Name: o.referralNumber,
SubText: toTitleCase(subtext), SubText: toTitleCase(subtext),
value: o value: o,
}; };
}) ?? [] }) ?? []
); );
}, },
answers() { answers() {
return [...this.duplicateOrders, ...this.answersFromCms]; return [...this.duplicateOrders, ...this.answersFromCms];
} },
}, },
methods: { methods: {
/** /**
* @summary Steps to perform when forward button clicked. * @summary Steps to perform when forward button clicked.
*/ */
async forwardButtonAction() { async forwardButtonAction() {
if ( if (
this.selectedAnswer !== null this.selectedAnswer !== null &&
&& typeof this.selectedAnswer === 'object' typeof this.selectedAnswer === 'object'
) { ) {
await useMainStore() await useMainStore()
.loadSession(this.selectedAnswer) .loadSession(this.selectedAnswer)
@ -203,31 +206,31 @@ export default {
this.$route this.$route
); );
} }
} },
} },
}; };
</script> </script>
<style lang="scss"> <style lang="scss">
.duplicate-check-subheader { .duplicate-check-subheader {
.subheader-secondary { .subheader-secondary {
margin-top: map-get($spacers, 2); margin-top: map-get($spacers, 2);
} }
p { p {
span { span {
font-size: $h6-font-size; font-size: $h6-font-size;
}
} }
}
} }
.duplicate-check-question { .duplicate-check-question {
.question-text { .question-text {
justify-content: left; justify-content: left;
display: inline-flex !important; display: inline-flex !important;
margin-top: map-get($spacers, 4); margin-top: map-get($spacers, 4);
margin-bottom: map-get($spacers, 2); margin-bottom: map-get($spacers, 2);
} }
.form-test-error { .form-test-error {
margin-top: 0 !important; margin-top: 0 !important;
} }
} }
</style> </style>

View file

@ -1,40 +1,40 @@
<template> <template>
<Form <Form
ref="theForm" ref="theForm"
v-slot="{ meta }" v-slot="{ meta }"
@submit="onSubmit" @submit="onSubmit"
@invalidSubmit="onInvalidSubmit"> @invalidSubmit="onInvalidSubmit">
<div class="container-fluid fade-on-route-transition"> <div class="container-fluid fade-on-route-transition">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6 px-0 px-md-2"> <div class="col-md-6 px-0 px-md-2">
<siteHeader <siteHeader
class="mb-2 header" class="mb-2 header"
cmsWidgetName="SiteHeaderWidget" /> cmsWidgetName="SiteHeaderWidget" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<div class="select-car-form rounded text-center">
<vehicleBanner
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="displayGeneric"
class="mt-2 mb-4" />
<policyVehiclesQuestion
v-model="selectedVehicleVin"
cmsWidgetName="PolicyVehiclesQuestion"
:vehicles="VehiclesForQuestions"
:validationRules="rules.optionRequired" />
<siteFooter
ref="siteFooter"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid"
@ForwardClicked="forwardButtonAction"
@backClicked="backButtonAction" />
</div>
</div>
</div>
</div> </div>
</div> </Form>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<div class="select-car-form rounded text-center">
<vehicleBanner
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="displayGeneric"
class="mt-2 mb-4" />
<policyVehiclesQuestion
v-model="selectedVehicleVin"
cmsWidgetName="PolicyVehiclesQuestion"
:vehicles="VehiclesForQuestions"
:validationRules="rules.optionRequired" />
<siteFooter
ref="siteFooter"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid"
@ForwardClicked="forwardButtonAction"
@backClicked="backButtonAction" />
</div>
</div>
</div>
</div>
</Form>
</template> </template>
<script> <script>
// Components // Components
@ -55,7 +55,7 @@ import {
deductibleForSelectedVehicle, deductibleForSelectedVehicle,
endorsementsForSelectedVehicle, endorsementsForSelectedVehicle,
noCoverageForSelectedVehicle, noCoverageForSelectedVehicle,
repairWaivedForSelectedVehicle repairWaivedForSelectedVehicle,
} from '@/helpers/policy-vehicle-helper'; } from '@/helpers/policy-vehicle-helper';
export default { export default {
@ -66,11 +66,13 @@ export default {
vehicleBanner, vehicleBanner,
policyVehiclesQuestion, policyVehiclesQuestion,
// eslint-disable-next-line vue/no-reserved-component-names // eslint-disable-next-line vue/no-reserved-component-names
Form Form,
}, },
mixins: [BaseFormMixin], mixins: [BaseFormMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
const cmsContentPromise = await fetchCmsContentForPage(to.query.issPage); const cmsContentPromise = await fetchCmsContentForPage(
to.query.issPage
);
next((vm) => { next((vm) => {
vm.setCmsContent(cmsContentPromise); vm.setCmsContent(cmsContentPromise);
}); });
@ -88,8 +90,8 @@ export default {
displayGeneric: true, displayGeneric: true,
policyVinFound: true, policyVinFound: true,
rules: { rules: {
optionRequired: globalRules.OPTION_REQUIRED optionRequired: globalRules.OPTION_REQUIRED,
} },
}; };
}, },
computed: { computed: {
@ -97,18 +99,18 @@ export default {
// Map API result data, to address-vehicles data structure // Map API result data, to address-vehicles data structure
const vehicles = this.policyVehicles; const vehicles = this.policyVehicles;
const mappedData = const mappedData =
vehicles?.map((v) => { vehicles?.map((v) => {
const maskSymbol = 'X'; const maskSymbol = 'X';
const vinStart = maskSymbol.repeat(v.vin.length - 6); const vinStart = maskSymbol.repeat(v.vin.length - 6);
const vinEnd = v.vin.substring(v.vin.length - 6); const vinEnd = v.vin.substring(v.vin.length - 6);
return { return {
vin: v.vin, vin: v.vin,
vehicle: v, vehicle: v,
Text: `${v.vehicleYear} ${v.vehicleMake} ${v.vehicleModel}`, Text: `${v.vehicleYear} ${v.vehicleMake} ${v.vehicleModel}`,
Name: v.vin, Name: v.vin,
SubText: `VIN ${vinStart}${vinEnd}` SubText: `VIN ${vinStart}${vinEnd}`,
}; };
}) ?? []; }) ?? [];
return mappedData; return mappedData;
}, },
noCoverageForSelectedVehicle() { noCoverageForSelectedVehicle() {
@ -124,9 +126,11 @@ export default {
return repairWaivedForSelectedVehicle(this.selectedPolicyVehicle); return repairWaivedForSelectedVehicle(this.selectedPolicyVehicle);
}, },
selectedVehicle() { selectedVehicle() {
const vehicle = this.mainStore.lookupVehicleByVin(this.selectedVehicleVin); const vehicle = this.mainStore.lookupVehicleByVin(
this.selectedVehicleVin
);
return vehicle; return vehicle;
} },
}, },
watch: { watch: {
async selectedVehicleVin(value) { async selectedVehicleVin(value) {
@ -150,10 +154,12 @@ export default {
// save selected vehicle to the store // save selected vehicle to the store
this.mainStore.updateVehicle(vehicle.data); this.mainStore.updateVehicle(vehicle.data);
this.displayGeneric = false; this.displayGeneric = false;
this.selectedPolicyVehicle = this.policyVehicles.find((p) => p.vin === value); this.selectedPolicyVehicle = this.policyVehicles.find(
(p) => p.vin === value
);
} }
} }
} },
}, },
beforeMount() { beforeMount() {
if (this.mainStore.order.vehicle.vin) { if (this.mainStore.order.vehicle.vin) {
@ -165,14 +171,22 @@ export default {
methods: { methods: {
backButtonAction() { backButtonAction() {
useMainStore().issConfig.disabledFields.policyNumber = true; useMainStore().issConfig.disabledFields.policyNumber = true;
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route); this.$router.navigate(
this.navigationScenarios.CLICKED_BACK,
this.$route
);
}, },
async forwardButtonAction() { async forwardButtonAction() {
if ( if (
this.selectedVehicleVin !== vehicleSelectionOptions.VEHICLE_NOT_LISTED this.selectedVehicleVin !==
vehicleSelectionOptions.VEHICLE_NOT_LISTED
) { ) {
const vehicleLookupResponse = await this.lookupVehicleByVin(this.selectedVehicleVin); const vehicleLookupResponse = await this.lookupVehicleByVin(
const vehicle = this.policyVehicles.find((pv) => pv.vin === this.selectedVehicleVin); this.selectedVehicleVin
);
const vehicle = this.policyVehicles.find(
(pv) => pv.vin === this.selectedVehicleVin
);
if (vehicleLookupResponse.error) { if (vehicleLookupResponse.error) {
if (vehicleLookupResponse.status === 404) { if (vehicleLookupResponse.status === 404) {
@ -188,27 +202,32 @@ export default {
vin: vehicle.vin, vin: vehicle.vin,
noCoverage: this.noCoverageForSelectedVehicle, noCoverage: this.noCoverageForSelectedVehicle,
deductible: this.deductibleForSelectedVehicle, deductible: this.deductibleForSelectedVehicle,
repairWaived: this.repairWaivedForSelectedVehicle repairWaived: this.repairWaivedForSelectedVehicle,
}); });
this.policyVinFound = false; this.policyVinFound = false;
return this.navigateForward(); return this.navigateForward();
} }
this.mainStore.setBailout(bailoutMessage.vehicleLookupError( this.mainStore.setBailout(
vehicle.vin, bailoutMessage.vehicleLookupError(
vehicleLookupResponse.data vehicle.vin,
)); vehicleLookupResponse.data
)
);
return this.navigateForward(); return this.navigateForward();
} }
this.vehicleFromLookup = Object.assign(vehicleLookupResponse.data, { this.vehicleFromLookup = Object.assign(
policyVehicleId: vehicle.id, vehicleLookupResponse.data,
vin: this.selectedVehicleVin, {
noCoverage: this.noCoverageForSelectedVehicle, policyVehicleId: vehicle.id,
deductible: this.deductibleForSelectedVehicle, vin: this.selectedVehicleVin,
repairWaived: this.repairWaivedForSelectedVehicle, noCoverage: this.noCoverageForSelectedVehicle,
endorsements: this.endorsementsForSelectedVehicle deductible: this.deductibleForSelectedVehicle,
}); repairWaived: this.repairWaivedForSelectedVehicle,
endorsements: this.endorsementsForSelectedVehicle,
}
);
useMainStore().updateVehicle(this.vehicleFromLookup); useMainStore().updateVehicle(this.vehicleFromLookup);
} }
@ -223,7 +242,8 @@ export default {
{} {}
); );
} else if ( } else if (
this.selectedVehicleVin === vehicleSelectionOptions.VEHICLE_NOT_LISTED this.selectedVehicleVin ===
vehicleSelectionOptions.VEHICLE_NOT_LISTED
) { ) {
this.$router.navigate( this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_NON_LISTED_VEHICLE, this.navigationScenarios.CLICKED_FORWARD_NON_LISTED_VEHICLE,
@ -232,9 +252,13 @@ export default {
{} {}
); );
} else if ( } else if (
this.endorsementsForSelectedVehicle?.length > 0 this.endorsementsForSelectedVehicle?.length > 0 &&
&& (this.endorsementsForSelectedVehicle?.includes(endorsementOptions.PARKING_GUARD) (this.endorsementsForSelectedVehicle?.includes(
|| this.endorsementsForSelectedVehicle?.includes(endorsementOptions.EDUCATOR)) endorsementOptions.PARKING_GUARD
) ||
this.endorsementsForSelectedVehicle?.includes(
endorsementOptions.EDUCATOR
))
) { ) {
this.$router.navigate( this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_WITH_ENDORSEMENTS, this.navigationScenarios.CLICKED_FORWARD_WITH_ENDORSEMENTS,
@ -242,7 +266,8 @@ export default {
); );
} else if (!this.policyVinFound) { } else if (!this.policyVinFound) {
this.$router.navigate( this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND, this.navigationScenarios
.CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND,
this.$route, this.$route,
{}, {},
{} {}
@ -263,21 +288,21 @@ export default {
return { return {
error: true, error: true,
status: responseError.status, status: responseError.status,
data: responseError.data data: responseError.data,
}; };
} }
} },
} },
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.header { .header {
margin-bottom: 0rem !important; margin-bottom: 0rem !important;
} }
:deep(.form-test-error) { :deep(.form-test-error) {
text-align: left; text-align: left;
margin-top: 0rem !important; margin-top: 0rem !important;
line-height: 1.5rem; line-height: 1.5rem;
} }
</style> </style>
<!-- est comment --> <!-- est comment -->

View file

@ -1,68 +1,63 @@
<template> <template>
<Form <Form ref="theForm" @submit="onSubmit" @invalidSubmit="onInvalidSubmit">
ref="theForm" <div class="container-fluid fade-on-route-transition">
@submit="onSubmit" <div class="row justify-content-center">
@invalidSubmit="onInvalidSubmit"> <div class="col-md-6 px-0 px-md-2">
<div class="container-fluid fade-on-route-transition"> <siteHeader
<div class="row justify-content-center"> class="mb-2 header"
<div class="col-md-6 px-0 px-md-2"> cmsWidgetName="SiteHeaderWidget" />
<siteHeader </div>
class="mb-2 header" </div>
cmsWidgetName="SiteHeaderWidget" /> <div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<siteSubHeader
id="sub-header"
cmsWidgetName="SiteSubHeader"
class="mb-5 mt-4" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<buttonQuestion
ref="buttonQuestion"
v-model="selectedProvider"
questionText="Select an option:"
:answers="prefAnswers"
groupName="prefQuestions"
buttonTypeString="providerPrefRadio"
:validationRules="rules.optionRequired"
isRequired />
<siteFooter
ref="siteFooter"
class="mt-5"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="isForwardActionDisabled"
@backClicked="navigateBack"
@forwardClicked="forwardButtonAction" />
</div>
</div>
<recalModal ref="recalModal" cmsWidgetName="RecalModal" />
<steeringModal
ref="StateSteeringModal"
cmsWidgetName="StateSteeringModal" />
<shopPreferenceModal
ref="ShopPreferenceDrawer"
cmsWidgetName="ShopPreferenceDrawer"
:showSteeringLink="showSteeringLink"
@openSteering="openStateSteeringModal" />
<tpaRecalModal
ref="TPARecalModal"
cmsWidgetName="TPARecalModal"
:ackError="ackError"
@buttonClick="navigateWithTPAAck" />
</div> </div>
</div> </Form>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<siteSubHeader
id="sub-header"
cmsWidgetName="SiteSubHeader"
class="mb-5 mt-4" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<buttonQuestion
ref="buttonQuestion"
v-model="selectedProvider"
questionText="Select an option:"
:answers="prefAnswers"
groupName="prefQuestions"
buttonTypeString="providerPrefRadio"
:validationRules="rules.optionRequired"
isRequired />
<siteFooter
ref="siteFooter"
class="mt-5"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="isForwardActionDisabled"
@backClicked="navigateBack"
@forwardClicked="forwardButtonAction" />
</div>
</div>
<recalModal
ref="recalModal"
cmsWidgetName="RecalModal" />
<steeringModal
ref="StateSteeringModal"
cmsWidgetName="StateSteeringModal" />
<shopPreferenceModal
ref="ShopPreferenceDrawer"
cmsWidgetName="ShopPreferenceDrawer"
:showSteeringLink="showSteeringLink"
@openSteering="openStateSteeringModal" />
<tpaRecalModal
ref="TPARecalModal"
cmsWidgetName="TPARecalModal"
:ackError="ackError"
@buttonClick="navigateWithTPAAck" />
</div>
</Form>
</template> </template>
<script> <script>
// Import Supporting Files // Import Supporting Files
import { import {
fetchCmsContentForPage, fetchCmsContentForPage,
setupModalLinks setupModalLinks,
} from '@/helpers/cms-content-helper'; } from '@/helpers/cms-content-helper';
import settleAllPromises from '@/helpers/layout-helper'; import settleAllPromises from '@/helpers/layout-helper';
import errorMessages from '@/constants/error-messages'; import errorMessages from '@/constants/error-messages';
@ -98,7 +93,7 @@ export default {
recalModal, recalModal,
steeringModal, steeringModal,
shopPreferenceModal, shopPreferenceModal,
tpaRecalModal tpaRecalModal,
}, },
mixins: [baseFormMixin], mixins: [baseFormMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
@ -107,8 +102,8 @@ export default {
const promiseResultMap = [ const promiseResultMap = [
{ {
resultKey: 'cmsContent', resultKey: 'cmsContent',
promise: cmsContentPromise promise: cmsContentPromise,
} },
]; ];
const resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
next((vm) => { next((vm) => {
@ -126,8 +121,8 @@ export default {
showSteeringLink: false, showSteeringLink: false,
tpaAcknowledgement: false, tpaAcknowledgement: false,
rules: { rules: {
optionRequired: globalRules.OPTION_REQUIRED optionRequired: globalRules.OPTION_REQUIRED,
} },
}; };
}, },
computed: { computed: {
@ -137,35 +132,44 @@ export default {
prefAnswers() { prefAnswers() {
const cmsAnswersContent = [ const cmsAnswersContent = [
{ {
cmsWidgetName: options.SAFELITE cmsWidgetName: options.SAFELITE,
}, },
{ {
cmsWidgetName: options.TPA cmsWidgetName: options.TPA,
} },
]; ];
// if cms content has not yet loaded, skip // if cms content has not yet loaded, skip
if ( if (
!this.getCmsContent(cmsAnswersContent[0].cmsWidgetName, 'HeaderText') !this.getCmsContent(
|| this.getCmsContent(cmsAnswersContent[0].cmsWidgetName, 'HeaderText') cmsAnswersContent[0].cmsWidgetName,
=== '' 'HeaderText'
) ||
this.getCmsContent(
cmsAnswersContent[0].cmsWidgetName,
'HeaderText'
) === ''
) { ) {
return {}; return {};
} }
const modifiedAnswers = cmsAnswersContent.map((answer) => ({ const modifiedAnswers = cmsAnswersContent.map((answer) => ({
value: answer.cmsWidgetName, value: answer.cmsWidgetName,
buttonLabel: this.getHeaderTextFromCms(answer.cmsWidgetName), buttonLabel: this.getHeaderTextFromCms(answer.cmsWidgetName),
buttonLabelSubCopy: this.getSubheaderTextFromCms(answer.cmsWidgetName), buttonLabelSubCopy: this.getSubheaderTextFromCms(
buttonBodyCopy: this.getBodyTextFromCms(answer.cmsWidgetName) answer.cmsWidgetName
),
buttonBodyCopy: this.getBodyTextFromCms(answer.cmsWidgetName),
})); }));
return modifiedAnswers; return modifiedAnswers;
}, },
ackError() { ackError() {
return errorMessages.ACKNOWLEDGEMENT_REQUIRED; return errorMessages.ACKNOWLEDGEMENT_REQUIRED;
} },
}, },
mounted() { mounted() {
setupModalLinks(this); setupModalLinks(this);
const pageData = this.mainStore.pageData(issPageValues.PROVIDER_PREFERENCE); const pageData = this.mainStore.pageData(
issPageValues.PROVIDER_PREFERENCE
);
this.selectedProvider = pageData?.selectedProvider this.selectedProvider = pageData?.selectedProvider
? pageData.selectedProvider ? pageData.selectedProvider
: null; : null;
@ -196,9 +200,11 @@ export default {
navigateWithTPAAck() { navigateWithTPAAck() {
this.mainStore.saveProviderPreferenceData({ this.mainStore.saveProviderPreferenceData({
selectedProvider: this.selectedProvider, selectedProvider: this.selectedProvider,
tpaAcknowledgement: this.tpaAcknowledgement tpaAcknowledgement: this.tpaAcknowledgement,
}); });
this.navigateForward(this.navigationScenarios.CLICKED_FORWARD_WITH_TPA_ENABLED); this.navigateForward(
this.navigationScenarios.CLICKED_FORWARD_WITH_TPA_ENABLED
);
}, },
forwardButtonAction() { forwardButtonAction() {
if (this.selectedProvider) { if (this.selectedProvider) {
@ -206,7 +212,9 @@ export default {
switch (this.selectedProvider) { switch (this.selectedProvider) {
case options.SAFELITE: case options.SAFELITE:
this.mainStore.updateIsSafeliteProvider(true); this.mainStore.updateIsSafeliteProvider(true);
scenario = this.navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE; scenario =
this.navigationScenarios
.CLICKED_FORWARD_WITH_SAFELITE;
break; break;
case options.TPA: case options.TPA:
this.mainStore.updateIsSafeliteProvider(false); this.mainStore.updateIsSafeliteProvider(false);
@ -217,11 +225,15 @@ export default {
return; return;
} }
scenario = scenario =
this.navigationScenarios.CLICKED_FORWARD_WITH_TPA_ENABLED; this.navigationScenarios
.CLICKED_FORWARD_WITH_TPA_ENABLED;
} else { } else {
this.mainStore.setBailout(bailoutMessage.TPANotEnabled()); this.mainStore.setBailout(
bailoutMessage.TPANotEnabled()
);
scenario = scenario =
this.navigationScenarios.CLICKED_FORWARD_WITH_TPA_DISABLED; this.navigationScenarios
.CLICKED_FORWARD_WITH_TPA_DISABLED;
} }
break; break;
default: default:
@ -229,34 +241,34 @@ export default {
this.navigateForward(scenario); this.navigateForward(scenario);
this.mainStore.saveProviderPreferenceData({ this.mainStore.saveProviderPreferenceData({
selectedProvider: this.selectedProvider, selectedProvider: this.selectedProvider,
tpaAcknowledgement: this.tpaAcknowledgement tpaAcknowledgement: this.tpaAcknowledgement,
}); });
} }
}, },
openStateSteeringModal() { openStateSteeringModal() {
this.$refs.StateSteeringModal.openModal(); this.$refs.StateSteeringModal.openModal();
} },
} },
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
#sub-header span { #sub-header span {
color: $black; color: $black;
} }
.question-text { .question-text {
margin-top: 0; margin-top: 0;
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
& > span { & > span {
text-align: left; text-align: left;
} }
} }
:deep(.safeliteLogo) { :deep(.safeliteLogo) {
background-image: url(~@/assets/img/icons/safelite-logo.svg); background-image: url(~@/assets/img/icons/safelite-logo.svg);
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: 4.5rem; background-size: 4.5rem;
margin: 0 0.25rem 0 0.25rem; margin: 0 0.25rem 0 0.25rem;
padding: 0 2.5rem 0 2.5rem; padding: 0 2.5rem 0 2.5rem;
} }
</style> </style>

View file

@ -1,77 +1,79 @@
<template> <template>
<Form <Form
ref="theForm" ref="theForm"
v-slot="{ meta }" v-slot="{ meta }"
@submit="onSubmit" @submit="onSubmit"
@invalidSubmit="onInvalidSubmit"> @invalidSubmit="onInvalidSubmit">
<div class="container-fluid fade-on-route-transition"> <div class="container-fluid fade-on-route-transition">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6 px-0 px-md-2"> <div class="col-md-6 px-0 px-md-2">
<siteHeader <siteHeader
class="mb-2 header" class="mb-2 header"
cmsWidgetName="SiteHeaderWidget" /> cmsWidgetName="SiteHeaderWidget" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<siteSubHeader
cmsWidgetName="ScheduleSubHeaderWidget"
subTextClasses="text-center small sub-text"
class="mt-4" />
<template v-if="ChangeShopLink.length">
<textBlock
cmsWidgetName="ChangeShopLink"
justifyText="center"
class="mb-3 text-link-small"
:marginTopSizeOverride="1" />
</template>
<div class="main-content-container">
<locationAlerts
ref="locationAlerts"
cmsWidgetPrefix="LocationAlert-" />
<datePicker
ref="datePicker"
v-model="selectedDate"
customComponentId="dateQuestion"
selectableDatesSetting="custom"
class="text-link-small"
:customSelectableDatesCallback="
getAvailableDatesMethod
"
validationRules="date-required"
@dateClicked="openInshopTimeSlotsModal" />
<timeSlotModalQuestion
ref="timeSlotModalQuestion"
v-model="selectedTimeSlotInfo"
customComponentId="timeSlotModalQuestion"
cmsWidgetName="TimeSlotModalQuestion"
mobilePremiumCmsWidgetName="MobilePremiumTimeSlotModal"
mobileCmsWidgetName="MobileTimeSlotModal"
dropoffCmsWidgetName="DropOffTimeSlotModal"
sameDayDropOffCmsWidgetName="SameDayDropOffTimeSlotModal"
overnightDropOffCmsWidgetName="OvernightDropOffTimeSlotModal"
:selectedDate="selectedDate"
:appointmentType="appointmentType"
:premiumAppointmentFee="mobilePremiumAppointmentFee"
:timeSlotsForSelectedDate="timeSlotsForSelectedDate"
:estimatedServiceMinutesMinimum="
selectableDatesData.estimatedServiceMinutesMinimum
"
:estimatedServiceMinutesMaximum="
selectableDatesData.estimatedServiceMinutesMaximum
"
validationRules="time-slot-selection-required"
@timeSlotModalClosed="timeSlotModalClosed" />
<siteFooter
ref="navbar"
class="mt-5"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid"
@backClicked="navigateBack"
@forwardClicked="forwardButtonAction" />
</div>
</div>
</div>
</div> </div>
</div> </Form>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<siteSubHeader
cmsWidgetName="ScheduleSubHeaderWidget"
subTextClasses="text-center small sub-text"
class="mt-4" />
<template v-if="ChangeShopLink.length">
<textBlock
cmsWidgetName="ChangeShopLink"
justifyText="center"
class="mb-3 text-link-small"
:marginTopSizeOverride="1" />
</template>
<div class="main-content-container">
<locationAlerts
ref="locationAlerts"
cmsWidgetPrefix="LocationAlert-" />
<datePicker
ref="datePicker"
v-model="selectedDate"
customComponentId="dateQuestion"
selectableDatesSetting="custom"
class="text-link-small"
:customSelectableDatesCallback="getAvailableDatesMethod"
validationRules="date-required"
@dateClicked="openInshopTimeSlotsModal" />
<timeSlotModalQuestion
ref="timeSlotModalQuestion"
v-model="selectedTimeSlotInfo"
customComponentId="timeSlotModalQuestion"
cmsWidgetName="TimeSlotModalQuestion"
mobilePremiumCmsWidgetName="MobilePremiumTimeSlotModal"
mobileCmsWidgetName="MobileTimeSlotModal"
dropoffCmsWidgetName="DropOffTimeSlotModal"
sameDayDropOffCmsWidgetName="SameDayDropOffTimeSlotModal"
overnightDropOffCmsWidgetName="OvernightDropOffTimeSlotModal"
:selectedDate="selectedDate"
:appointmentType="appointmentType"
:premiumAppointmentFee="mobilePremiumAppointmentFee"
:timeSlotsForSelectedDate="timeSlotsForSelectedDate"
:estimatedServiceMinutesMinimum="
selectableDatesData.estimatedServiceMinutesMinimum
"
:estimatedServiceMinutesMaximum="
selectableDatesData.estimatedServiceMinutesMaximum
"
validationRules="time-slot-selection-required"
@timeSlotModalClosed="timeSlotModalClosed" />
<siteFooter
ref="navbar"
class="mt-5"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid"
@backClicked="navigateBack"
@forwardClicked="forwardButtonAction" />
</div>
</div>
</div>
</div>
</Form>
</template> </template>
<script> <script>
// Components // Components
@ -88,16 +90,16 @@ import {
AppointmentTypeStrings, AppointmentTypeStrings,
GET_MOBILE_TIME_SLOTS, GET_MOBILE_TIME_SLOTS,
GET_SHOP_TIME_SLOTS, GET_SHOP_TIME_SLOTS,
PREMIUM_FEE_PART_TYPE PREMIUM_FEE_PART_TYPE,
} from '@/constants/schedule-constants.js'; } from '@/constants/schedule-constants.js';
import { import {
fetchCmsContentForPage, fetchCmsContentForPage,
splitCopyOnCMSPlaceHolder splitCopyOnCMSPlaceHolder,
} from '@/helpers/cms-content-helper'; } from '@/helpers/cms-content-helper';
import { import {
calcDaysBetweenDates, calcDaysBetweenDates,
convertDateStringToDate, convertDateStringToDate,
sumDateString sumDateString,
} from '@/helpers/date-helper'; } from '@/helpers/date-helper';
import settleAllPromises from '@/helpers/layout-helper'; import settleAllPromises from '@/helpers/layout-helper';
import { Form, defineRule } from 'vee-validate'; import { Form, defineRule } from 'vee-validate';
@ -141,7 +143,10 @@ const getAvailableDates = async (
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;
@ -151,15 +156,16 @@ const getAvailableDates = async (
} }
if ( if (
appointmentType === AppointmentTypeStrings.MOBILE appointmentType === AppointmentTypeStrings.MOBILE ||
|| appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP appointmentType ===
AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP
) { ) {
storeActionConfig = { storeActionConfig = {
storeAction: GET_MOBILE_TIME_SLOTS, storeAction: GET_MOBILE_TIME_SLOTS,
payload: { payload: {
startDate: apiStartDate, startDate: apiStartDate,
endDate: apiEndDate endDate: apiEndDate,
} },
}; };
} else { } else {
storeActionConfig = { storeActionConfig = {
@ -168,15 +174,16 @@ const getAvailableDates = async (
startDate: apiStartDate, startDate: apiStartDate,
endDate: apiEndDate, endDate: apiEndDate,
shopAppointmentType: appointmentType, shopAppointmentType: appointmentType,
providerNumber providerNumber,
} },
}; };
} }
if (apiStartDate < apiEndDate) storeActionConfigs.push(storeActionConfig); if (apiStartDate < apiEndDate)
storeActionConfigs.push(storeActionConfig);
} }
const timeSlotsResponsesData = { const timeSlotsResponsesData = {
days: [] days: [],
}; };
function compareDayStrings(a, b) { function compareDayStrings(a, b) {
@ -186,35 +193,37 @@ const getAvailableDates = async (
} }
const makeParallelCalls = async () => { const makeParallelCalls = async () => {
await Promise.all(storeActionConfigs.map(async (storeAction) => { await Promise.all(
let timeSlotsResponse = null; storeActionConfigs.map(async (storeAction) => {
if (storeAction.storeAction === GET_SHOP_TIME_SLOTS) { let timeSlotsResponse = null;
timeSlotsResponse = await useMainStore().getShopTimeSlots( if (storeAction.storeAction === GET_SHOP_TIME_SLOTS) {
storeAction.payload.startDate, timeSlotsResponse = await useMainStore().getShopTimeSlots(
storeAction.payload.endDate, storeAction.payload.startDate,
storeAction.payload.shopAppointmentType, storeAction.payload.endDate,
storeAction.payload.providerNumber storeAction.payload.shopAppointmentType,
); storeAction.payload.providerNumber
} else { );
timeSlotsResponse = await useMainStore().getMobileTimeSlots( } else {
storeAction.payload.startDate, timeSlotsResponse = await useMainStore().getMobileTimeSlots(
storeAction.payload.endDate storeAction.payload.startDate,
); storeAction.payload.endDate
} );
}
timeSlotsResponsesData.estimatedServiceMinutesMinimum = timeSlotsResponsesData.estimatedServiceMinutesMinimum =
timeSlotsResponse.data.estimatedServiceMinutesMinimum; timeSlotsResponse.data.estimatedServiceMinutesMinimum;
timeSlotsResponsesData.estimatedServiceMinutesMaximum = timeSlotsResponsesData.estimatedServiceMinutesMaximum =
timeSlotsResponse.data.estimatedServiceMinutesMaximum; timeSlotsResponse.data.estimatedServiceMinutesMaximum;
timeSlotsResponsesData.days = [ timeSlotsResponsesData.days = [
...timeSlotsResponsesData.days, ...timeSlotsResponsesData.days,
...timeSlotsResponse.data.days ...timeSlotsResponse.data.days,
]; ];
})); })
);
}; };
return makeParallelCalls().then(() => { return makeParallelCalls().then(() => {
// sort days chronologically // sort days chronologically
timeSlotsResponsesData.days.sort(compareDayStrings); timeSlotsResponsesData.days.sort(compareDayStrings);
return timeSlotsResponsesData; return timeSlotsResponsesData;
}); });
@ -231,11 +240,11 @@ export default {
siteFooter, siteFooter,
textBlock, textBlock,
// eslint-disable-next-line vue/no-reserved-component-names // eslint-disable-next-line vue/no-reserved-component-names
Form Form,
}, },
mixins: [BaseFormMixin], mixins: [BaseFormMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
// Call APIs // Call APIs
let preSelectedDate = await useMainStore().order.schedule.date; let preSelectedDate = await useMainStore().order.schedule.date;
if (!preSelectedDate || preSelectedDate.startTime === null) { if (!preSelectedDate || preSelectedDate.startTime === null) {
preSelectedDate = null; preSelectedDate = null;
@ -244,18 +253,20 @@ export default {
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage); const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
const datePickerInitialDataPromise = const datePickerInitialDataPromise =
await datePicker.methods.loadInitialData({ await datePicker.methods.loadInitialData({
selectableDatesSetting: 'custom', selectableDatesSetting: 'custom',
initialViewRowsToShow: 5, initialViewRowsToShow: 5,
customSelectableDatesCallback: getAvailableDates, customSelectableDatesCallback: getAvailableDates,
preSelectedDate preSelectedDate,
}); });
const premiumFeePromise = useMainStore().getMobilePremiumFee(); const premiumFeePromise = useMainStore().getMobilePremiumFee();
const premiumFeeWithPricePromise = premiumFeePromise.then((result) => { const premiumFeeWithPricePromise = premiumFeePromise.then((result) => {
if (result.data) { if (result.data) {
return useMainStore().priceOrderItemsAndSaveServerData(result.data); return useMainStore().priceOrderItemsAndSaveServerData(
result.data
);
} }
return result.data; return result.data;
}); });
@ -269,27 +280,29 @@ export default {
const promiseResultMap = [ const promiseResultMap = [
{ {
resultKey: 'cmsContent', resultKey: 'cmsContent',
promise: cmsContentPromise promise: cmsContentPromise,
}, },
{ {
resultKey: 'alertReasons', resultKey: 'alertReasons',
promise: alertReasonsPromise promise: alertReasonsPromise,
}, },
{ {
resultKey: 'datePickerInitialData', resultKey: 'datePickerInitialData',
promise: datePickerInitialDataPromise promise: datePickerInitialDataPromise,
}, },
{ {
resultKey: 'premiumFeeWithPrice', resultKey: 'premiumFeeWithPrice',
promise: premiumFeeWithPricePromise promise: premiumFeeWithPricePromise,
} },
]; ];
// use resultMap to populate layout content. // use resultMap to populate layout content.
const resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
next((vm) => { next((vm) => {
vm.setCmsContent(resultMap.cmsContent); vm.setCmsContent(resultMap.cmsContent);
vm.$refs.datePicker.initializeComponent(resultMap.datePickerInitialData); vm.$refs.datePicker.initializeComponent(
resultMap.datePickerInitialData
);
vm.$refs.locationAlerts.initializeComponent(resultMap.alertReasons); vm.$refs.locationAlerts.initializeComponent(resultMap.alertReasons);
vm.setData( vm.setData(
resultMap.datePickerInitialData.initialShopTimeSlotsResponse, resultMap.datePickerInitialData.initialShopTimeSlotsResponse,
@ -307,7 +320,7 @@ export default {
selectedDate: this.getSelectedDate(), selectedDate: this.getSelectedDate(),
selectedTimeSlotInfo: this.getSelectedTimeSlotInfo(), selectedTimeSlotInfo: this.getSelectedTimeSlotInfo(),
selectableDatesData: [], selectableDatesData: [],
mobilePremiumAppointmentFee: null mobilePremiumAppointmentFee: null,
}; };
}, },
computed: { computed: {
@ -325,11 +338,13 @@ export default {
return null; return null;
} }
return this.selectableDatesData.days?.find((selectableDate) => selectableDate.date === this.selectedDate); return this.selectableDatesData.days?.find(
(selectableDate) => selectableDate.date === this.selectedDate
);
}, },
supportingItems() { supportingItems() {
return useMainStore().lineItems.supportingItems; return useMainStore().lineItems.supportingItems;
} },
}, },
watch: { watch: {
selectedDate(newValue, oldValue) { selectedDate(newValue, oldValue) {
@ -342,33 +357,34 @@ export default {
startTime: null, startTime: null,
endTime: null, endTime: null,
jobMaxMinutes: null, jobMaxMinutes: null,
jobMinMinutes: null jobMinMinutes: null,
}, },
isPremiumAppointment: null isPremiumAppointment: null,
}; };
} }
}, },
selectedTimeSlotInfo(newValue) { selectedTimeSlotInfo(newValue) {
this.updateFooterButtonText(newValue); this.updateFooterButtonText(newValue);
} },
}, },
methods: { methods: {
splitCopyOnCMSPlaceHolder, splitCopyOnCMSPlaceHolder,
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
const { serviceLocation } = useMainStore().order; const { serviceLocation } = useMainStore().order;
const serviceLocationPreReqs = const serviceLocationPreReqs =
serviceLocation.zipCode serviceLocation.zipCode &&
&& serviceLocation.zipCodeCtu serviceLocation.zipCodeCtu &&
&& serviceLocation.appointmentType serviceLocation.appointmentType &&
&& (serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE (serviceLocation.appointmentType ===
|| serviceLocation.appointmentType AppointmentTypeStrings.MOBILE ||
=== AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP serviceLocation.appointmentType ===
|| serviceLocation.provider.providerNumber); AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP ||
serviceLocation.provider.providerNumber);
const supportingItems = this.supportingItems !== null; const supportingItems = this.supportingItems !== null;
const damageInfo = const damageInfo =
useMainStore().order.damage.isRepair useMainStore().order.damage.isRepair ||
|| (useMainStore().order.lineItems?.glassParts != null (useMainStore().order.lineItems?.glassParts != null &&
&& useMainStore().order.lineItems.glassParts.length > 0); useMainStore().order.lineItems.glassParts.length > 0);
return serviceLocationPreReqs && supportingItems && damageInfo; return serviceLocationPreReqs && supportingItems && damageInfo;
}, },
@ -386,7 +402,8 @@ export default {
this.mainStore.order.serviceLocation.provider.providerNumber this.mainStore.order.serviceLocation.provider.providerNumber
); );
// ADD API CALL RESULTS TO EXISTING DATE DATA // ADD API CALL RESULTS TO EXISTING DATE DATA
this.selectableDatesData.days = this.selectableDatesData.days.concat(newShopTimeSlots.days); this.selectableDatesData.days =
this.selectableDatesData.days.concat(newShopTimeSlots.days);
return newShopTimeSlots; return newShopTimeSlots;
}, },
getAvailableDates, getAvailableDates,
@ -401,13 +418,16 @@ export default {
}, },
getSelectedTimeSlotInfo() { getSelectedTimeSlotInfo() {
const isPremiumAppointment = const isPremiumAppointment =
!!( !!(
this.supportingItems?.filter((lineItem) => lineItem.partType === PREMIUM_FEE_PART_TYPE) ?? [] this.supportingItems?.filter(
).length > 0; (lineItem) =>
lineItem.partType === PREMIUM_FEE_PART_TYPE
) ?? []
).length > 0;
const selectedTimeSlotInfo = { const selectedTimeSlotInfo = {
timeSlot: this.mainStore.order.schedule, timeSlot: this.mainStore.order.schedule,
isPremiumAppointment isPremiumAppointment,
}; };
return selectedTimeSlotInfo; return selectedTimeSlotInfo;
@ -423,12 +443,16 @@ export default {
if (!timeSlotInfo || !timeSlotInfo.timeSlot.date) { if (!timeSlotInfo || !timeSlotInfo.timeSlot.date) {
navbarButtonText = 'Continue'; navbarButtonText = 'Continue';
} else { } else {
navbarButtonText = `Select ${this.convertSelectedDateToShortMonthAndDay(timeSlotInfo.timeSlot.date)}`; navbarButtonText = `Select ${this.convertSelectedDateToShortMonthAndDay(
timeSlotInfo.timeSlot.date
)}`;
if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) { if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) {
navbarButtonText += ` at ${this.getDisplayTextForMilitaryTime(timeSlotInfo.timeSlot.startTime)}`; navbarButtonText += ` at ${this.getDisplayTextForMilitaryTime(
timeSlotInfo.timeSlot.startTime
)}`;
} else if ( } else if (
this.appointmentType === AppointmentTypeStrings.MOBILE this.appointmentType === AppointmentTypeStrings.MOBILE &&
&& !timeSlotInfo.isPremiumAppointment !timeSlotInfo.isPremiumAppointment
) { ) {
navbarButtonText += ` at ${this.getDisplayTextForMilitaryTime( navbarButtonText += ` at ${this.getDisplayTextForMilitaryTime(
timeSlotInfo.timeSlot.startTime, timeSlotInfo.timeSlot.startTime,
@ -446,7 +470,7 @@ export default {
const dateObject = convertDateStringToDate(selectedDate); const dateObject = convertDateStringToDate(selectedDate);
return dateObject.toLocaleDateString('en-us', { return dateObject.toLocaleDateString('en-us', {
month: 'short', month: 'short',
day: 'numeric' day: 'numeric',
}); });
}, },
getDisplayTextForMilitaryTime( getDisplayTextForMilitaryTime(
@ -474,8 +498,8 @@ export default {
this.navigationScenarios.CLICKED_FORWARD, this.navigationScenarios.CLICKED_FORWARD,
this.$route this.$route
); );
} },
} },
}; };
</script> </script>
@ -483,24 +507,24 @@ export default {
$page-side-padding: 1.5rem; $page-side-padding: 1.5rem;
.page-container-grouped-styles { .page-container-grouped-styles {
overflow: auto; overflow: auto;
.main-content-container { .main-content-container {
padding: 0 1.5rem !important; padding: 0 1.5rem !important;
} }
} }
:deep(.text-link-small) { :deep(.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;
} }
} }
:deep(.subheader-primary) { :deep(.subheader-primary) {
h5.dark-header { h5.dark-header {
margin-bottom: 0.25rem !important; margin-bottom: 0.25rem !important;
} }
} }
</style> </style>

View file

@ -1,114 +1,122 @@
<template> <template>
<Form <Form
ref="theForm" ref="theForm"
v-slot="{ meta }" v-slot="{ meta }"
@submit="onSubmit" @submit="onSubmit"
@invalidSubmit="onInvalidSubmit"> @invalidSubmit="onInvalidSubmit">
<div class="container-fluid fade-on-route-transition"> <div class="container-fluid fade-on-route-transition">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6 px-0 px-md-2"> <div class="col-md-6 px-0 px-md-2">
<siteHeader <siteHeader
class="mb-2 header" class="mb-2 header"
cmsWidgetName="SiteHeaderWidget" /> cmsWidgetName="SiteHeaderWidget" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<siteSubHeader
id="sub-header"
cmsWidgetName="SiteSubHeader"
class="mb-5 mt-4" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6">
<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"
@updatedMobileProviderNumber="
setMobileProviderNumber
" />
<contentGroupModal
ref="RecalModal"
cmsWidgetName="RecalModal" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<siteFooter
ref="siteFooter"
class="mt-5"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="
!meta.valid || displayNoShopsAlert
"
@backClicked="navigateBack"
@forwardClicked="forwardButtonAction" />
</div>
</div>
</div> </div>
</div> </Form>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<siteSubHeader
id="sub-header"
cmsWidgetName="SiteSubHeader"
class="mb-5 mt-4" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6">
<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"
@updatedMobileProviderNumber="setMobileProviderNumber" />
<contentGroupModal
ref="RecalModal"
cmsWidgetName="RecalModal" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<siteFooter
ref="siteFooter"
class="mt-5"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid || displayNoShopsAlert"
@backClicked="navigateBack"
@forwardClicked="forwardButtonAction" />
</div>
</div>
</div>
</Form>
</template> </template>
<script> <script>
// Import Supporting Files // Import Supporting Files
@ -121,7 +129,7 @@ import { useMainStore } from '@/store';
import { import {
getPricedMobileFeePart, getPricedMobileFeePart,
getServiceabilityDetails, getServiceabilityDetails,
getZipCodeData getZipCodeData,
} from '@/helpers/service-location-helper'; } from '@/helpers/service-location-helper';
// Import Component // Import Component
@ -140,11 +148,11 @@ import serviceZipModalQuestion from '@/layouts/service-location/service-zip-moda
// DEFINE VALIDATION RULES // DEFINE VALIDATION RULES
defineRule('mobile-location-required', (value) => { defineRule('mobile-location-required', (value) => {
if ( if (
value.addressQuestions.streetAddress === '' value.addressQuestions.streetAddress === '' ||
|| value.addressQuestions.city === '' value.addressQuestions.city === '' ||
|| value.addressQuestions.state === '' value.addressQuestions.state === '' ||
|| value.addressQuestions.zipCode === '' value.addressQuestions.zipCode === '' ||
|| value.isVehicleProtected == null value.isVehicleProtected == null
) { ) {
return errorMessages.MOBILE_LOCATION_REQUIRED; return errorMessages.MOBILE_LOCATION_REQUIRED;
} }
@ -164,46 +172,46 @@ export default {
// eslint-disable-next-line vue/no-reserved-component-names // eslint-disable-next-line vue/no-reserved-component-names
Form, Form,
serviceZipModalQuestion, serviceZipModalQuestion,
shopQuestion shopQuestion,
}, },
mixins: [baseFormMixin], mixins: [baseFormMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
// Call APIs // Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage); const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
const serviceZipCode = const serviceZipCode =
useMainStore().order.serviceLocation.zipCode useMainStore().order.serviceLocation.zipCode ||
|| useMainStore().order.customer.address.zipCode; useMainStore().order.customer.address.zipCode;
const zipCodeData = getZipCodeData(serviceZipCode); const zipCodeData = getZipCodeData(serviceZipCode);
const mobileFeePartPromise = getPricedMobileFeePart(serviceZipCode); const mobileFeePartPromise = getPricedMobileFeePart(serviceZipCode);
const serviceabilityDetailsPromise = const serviceabilityDetailsPromise =
getServiceabilityDetails(serviceZipCode); getServiceabilityDetails(serviceZipCode);
const shopQuestionInitialDataPromise = const shopQuestionInitialDataPromise =
shopQuestion.methods.loadInitialData(serviceZipCode); shopQuestion.methods.loadInitialData(serviceZipCode);
// Settle promises and get results // Settle promises and get results
const promiseResultMap = [ const promiseResultMap = [
{ {
resultKey: 'cmsContent', resultKey: 'cmsContent',
promise: cmsContentPromise promise: cmsContentPromise,
}, },
{ {
resultKey: 'mobileFeePart', resultKey: 'mobileFeePart',
promise: mobileFeePartPromise promise: mobileFeePartPromise,
}, },
{ {
resultKey: 'serviceabilityDetails', resultKey: 'serviceabilityDetails',
promise: serviceabilityDetailsPromise promise: serviceabilityDetailsPromise,
}, },
{ {
resultKey: 'shopQuestionInitialData', resultKey: 'shopQuestionInitialData',
promise: shopQuestionInitialDataPromise promise: shopQuestionInitialDataPromise,
}, },
{ {
resultKey: 'zipCodeData', resultKey: 'zipCodeData',
promise: zipCodeData promise: zipCodeData,
} },
]; ];
const resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
@ -215,7 +223,9 @@ export default {
resultMap.mobileFeePart, resultMap.mobileFeePart,
resultMap.shopQuestionInitialData?.mobileProviderNumber resultMap.shopQuestionInitialData?.mobileProviderNumber
); );
vm.$refs.shopQuestion.initializeComponent(resultMap.shopQuestionInitialData); vm.$refs.shopQuestion.initializeComponent(
resultMap.shopQuestionInitialData
);
}); });
}, },
setup() { setup() {
@ -239,12 +249,15 @@ export default {
mobileFeePart: null, mobileFeePart: null,
mobileProviderNumber: null, mobileProviderNumber: null,
zipContainsMilitaryBase: false, zipContainsMilitaryBase: false,
zipCodeCtu: null zipCodeCtu: null,
}; };
}, },
computed: { computed: {
questionText() { questionText() {
return this.getCmsContent('ServiceTypeQuestionWidget', 'QuestionText'); return this.getCmsContent(
'ServiceTypeQuestionWidget',
'QuestionText'
);
}, },
answersFromCms() { answersFromCms() {
return this.getCmsContent('ServiceTypeQuestionWidget', 'Answers'); return this.getCmsContent('ServiceTypeQuestionWidget', 'Answers');
@ -253,7 +266,7 @@ export default {
get() { get() {
return { return {
state: this.state, state: this.state,
zipCode: this.zipCode zipCode: this.zipCode,
}; };
}, },
set(newValue) { set(newValue) {
@ -268,7 +281,7 @@ export default {
// eslint-disable-next-line vue/valid-next-tick // eslint-disable-next-line vue/valid-next-tick
this.$nextTick(); this.$nextTick();
} },
}, },
mobileLocationQuestions: { mobileLocationQuestions: {
get() { get() {
@ -278,9 +291,9 @@ export default {
streetAddress2: this.streetAddress2, streetAddress2: this.streetAddress2,
city: this.city, city: this.city,
state: this.state, state: this.state,
zipCode: this.zipCode zipCode: this.zipCode,
}, },
isVehicleProtected: this.isVehicleProtected isVehicleProtected: this.isVehicleProtected,
}; };
}, },
set(newValue) { set(newValue) {
@ -294,21 +307,23 @@ export default {
if (newValue.zipCode !== this.zipCode) { if (newValue.zipCode !== this.zipCode) {
if ( if (
!( !(
this.selectedAppointmentType === AppointmentTypeStrings.MOBILE this.selectedAppointmentType ===
|| this.selectedAppointmentType AppointmentTypeStrings.MOBILE ||
=== AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP this.selectedAppointmentType ===
AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP
) )
) { ) {
this.selectedAppointmentType = null; this.selectedAppointmentType = null;
} }
this.selectedProvider = null; this.selectedProvider = null;
} }
} },
}, },
isServiceableMobile() { isServiceableMobile() {
if (this.isRecalibrationServiceableMobile !== null) { if (this.isRecalibrationServiceableMobile !== null) {
return ( return (
this.isGlassServiceableMobile && this.isRecalibrationServiceableMobile this.isGlassServiceableMobile &&
this.isRecalibrationServiceableMobile
); );
} }
return this.isGlassServiceableMobile; return this.isGlassServiceableMobile;
@ -316,7 +331,8 @@ export default {
isServiceableInshop() { isServiceableInshop() {
if (this.isRecalibrationServiceableInshop !== null) { if (this.isRecalibrationServiceableInshop !== null) {
return ( return (
this.isGlassServiceableInshop && this.isRecalibrationServiceableInshop this.isGlassServiceableInshop &&
this.isRecalibrationServiceableInshop
); );
} }
@ -324,8 +340,8 @@ export default {
}, },
isShopQuestionDisplayed() { isShopQuestionDisplayed() {
return ( return (
this.selectedAppointmentType === 'Inshop' this.selectedAppointmentType === 'Inshop' ||
|| this.selectedAppointmentType === 'Dropoff' this.selectedAppointmentType === 'Dropoff'
); );
}, },
isAppointmentTypeDisplayed() { isAppointmentTypeDisplayed() {
@ -333,17 +349,18 @@ export default {
}, },
isMobileLocationDisplayed() { isMobileLocationDisplayed() {
return ( return (
this.selectedAppointmentType === AppointmentTypeStrings.MOBILE this.selectedAppointmentType ===
|| this.selectedAppointmentType AppointmentTypeStrings.MOBILE ||
=== AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP this.selectedAppointmentType ===
AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP
); );
}, },
requiresInshopRecalibration() { requiresInshopRecalibration() {
// Specifically check for isRecalibrationServiceableMobile === false, not null or true. // Specifically check for isRecalibrationServiceableMobile === false, not null or true.
return ( return (
this.isServiceableInshop this.isServiceableInshop &&
&& this.isGlassServiceableMobile this.isGlassServiceableMobile &&
&& this.isRecalibrationServiceableMobile === false this.isRecalibrationServiceableMobile === false
); );
}, },
displayMilitaryZipAlert() { displayMilitaryZipAlert() {
@ -357,20 +374,20 @@ export default {
}, },
displayServiceableInshopOnly() { displayServiceableInshopOnly() {
return ( return (
!this.displayRecalibrationWarning !this.displayRecalibrationWarning &&
&& this.isServiceableInshop this.isServiceableInshop &&
&& !this.isServiceableMobile !this.isServiceableMobile
); );
}, },
displayServiceableMobileOnly() { displayServiceableMobileOnly() {
return this.isServiceableMobile && !this.isServiceableInshop; return this.isServiceableMobile && !this.isServiceableInshop;
} },
}, },
methods: { methods: {
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
return ( return (
useMainStore().lineItems.supportingItems !== null useMainStore().lineItems.supportingItems !== null &&
&& useMainStore().order.serviceLocation.zipCode !== null useMainStore().order.serviceLocation.zipCode !== null
); );
}, },
async reloadShopData(zipCode) { async reloadShopData(zipCode) {
@ -391,8 +408,8 @@ export default {
city: null, city: null,
state: null, state: null,
zipCode: null, zipCode: null,
zipCodeCtu: null zipCodeCtu: null,
} },
}; };
} }
@ -405,7 +422,7 @@ export default {
zipCodeCtu: this.zipCodeCtu, zipCodeCtu: this.zipCodeCtu,
appointmentType: this.selectedAppointmentType, appointmentType: this.selectedAppointmentType,
isVehicleProtected: this.isVehicleProtected, isVehicleProtected: this.isVehicleProtected,
provider provider,
}); });
this.$router.navigate( this.$router.navigate(
@ -428,14 +445,14 @@ export default {
}, },
getServiceStateFromStore() { getServiceStateFromStore() {
return ( return (
useMainStore().order.serviceLocation.state useMainStore().order.serviceLocation.state ||
|| useMainStore().order.customer.address.state useMainStore().order.customer.address.state
); );
}, },
getServiceZipCodeFromStore() { getServiceZipCodeFromStore() {
return ( return (
useMainStore().order.serviceLocation.zipCode useMainStore().order.serviceLocation.zipCode ||
|| useMainStore().order.customer.address.zipCode useMainStore().order.customer.address.zipCode
); );
}, },
getIsVehicleProtectedFromStore() { getIsVehicleProtectedFromStore() {
@ -490,15 +507,15 @@ export default {
}, },
setServiceabilityDetails(serviceabilityDetails) { setServiceabilityDetails(serviceabilityDetails) {
this.isGlassServiceableInshop = this.isGlassServiceableInshop =
serviceabilityDetails.isGlassServiceableInshop; serviceabilityDetails.isGlassServiceableInshop;
this.isRecalibrationServiceableInshop = this.isRecalibrationServiceableInshop =
serviceabilityDetails.isRecalibrationServiceableInshop; serviceabilityDetails.isRecalibrationServiceableInshop;
this.isGlassServiceableMobile = this.isGlassServiceableMobile =
serviceabilityDetails.isGlassServiceableMobile; serviceabilityDetails.isGlassServiceableMobile;
this.isRecalibrationServiceableMobile = this.isRecalibrationServiceableMobile =
serviceabilityDetails.isRecalibrationServiceableMobile; serviceabilityDetails.isRecalibrationServiceableMobile;
} },
} },
}; };
</script> </script>
@ -506,47 +523,47 @@ export default {
$page-side-padding: 1.5rem; $page-side-padding: 1.5rem;
.page-container-grouped-styles { .page-container-grouped-styles {
overflow: auto; overflow: auto;
.main-content-container { .main-content-container {
padding: 0 1.5rem !important; padding: 0 1.5rem !important;
} }
} }
.question-text { .question-text {
& > span { & > span {
line-height: 1.5rem; line-height: 1.5rem;
} }
} }
.list-card-content p { .list-card-content p {
&:first-of-type { &:first-of-type {
line-height: 1.5rem; line-height: 1.5rem;
} }
&:not(:nth-of-type(1)) { &:not(:nth-of-type(1)) {
line-height: 1.25rem; line-height: 1.25rem;
} }
} }
.choose-option { .choose-option {
.button-question > div { .button-question > div {
&:first-of-type { &:first-of-type {
margin-bottom: 0.95rem; margin-bottom: 0.95rem;
line-height: 1.5rem; line-height: 1.5rem;
}
} }
}
} }
.button-question { .button-question {
.row.form-test-error { .row.form-test-error {
line-height: 1.5rem; line-height: 1.5rem;
padding-left: 0rem !important; padding-left: 0rem !important;
} }
} }
.service-location-button-question { .service-location-button-question {
.question-text { .question-text {
margin-top: 1.5rem; margin-top: 1.5rem;
span { span {
text-align: center; text-align: center;
}
} }
}
} }
</style> </style>

View file

@ -1,120 +1,122 @@
<template> <template>
<div class="container-fluid fade-on-route-transition"> <div class="container-fluid fade-on-route-transition">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6 px-0 px-md-2"> <div class="col-md-6 px-0 px-md-2">
<siteHeader <siteHeader
ref="siteHeader" ref="siteHeader"
:cmsWidgetName="widget.siteHeader" /> :cmsWidgetName="widget.siteHeader" />
</div>
</div>
</div>
<div class="container-fluid fade-on-route-transition">
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<Form
id="searchProvidersForm"
@submit="onSubmit"
@invalidSubmit="onInvalidSubmit">
<label
id="tpaSearchQuestionLabel"
for="tpaSearchQuestionField"
class="text-center fs-5 mt-5 mb-0 text-black w-100">
{{ tpaSearchQuestionLabel }}
</label>
<label
id="searchInstructions"
for="tpaSearchQuestionField"
class="text-center small darker-gray w-100 mb-4">
{{ searchInstructionsText }}
</label>
<div class="mb-5">
<textboxQuestion
id="tpaSearchQuestionField"
v-model="zipCode"
inputId="tpaSearchQuestionFieldInput"
:cmsWidgetName="widget.tpaSearchQuestion"
:includeSearchIcon="true"
:displayQuestionText="false"
isRequired
:isDisabled="reloadingProviders"
:validationRules="rules.zipCode"
@clickEvent="searchClick" />
</div>
</Form>
</div>
</div>
</div>
<div class="container-fluid fade-on-route-transition">
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<googleMap
id="map"
class="mb-4"
:addresses="providerAddresses"
:zipCode="mapZipCode" />
<Form
id="providerSelectionForm"
v-slot="{ meta }"
@submit="onSubmit"
@invalidSubmit="onInvalidSubmit">
<dropdownQuestion
id="searchRadiusFilter"
v-model="filter"
:cmsWidgetName="widget.filterByQuestion"
inputId="searchRadiusFilterInput"
:options="filterOptions"
disableAutoFill
:isDisabled="reloadingProviders"
:validationRules="rules.filter" />
<loader
v-if="reloadingProviders"
id="providersLoader"
loaderPosition="center"
loaderColor="blue"
:width="2"
:height="2"
class="my-4" />
<div v-else>
<div class="my-4">
<buttonQuestion
id="selectProviderQuestion"
v-model="selectedProviderNumber"
buttonTypeString="shopListButton"
:buttonTypeObject="shopListButton"
class="radioQuestion"
:answers="providerButtonData"
groupName="chooseShop"
textPosition="text-start"
isRequired
:validationRules="rules.provider"
:additionalButtonData="additionalButtonData" />
<alert
v-if="providers?.length === 0 ?? true"
id="alertNoNetworkProviders"
:cmsWidgetName="widget.noNetworkShopsAlert"
alertClass="alert-warning"
:isDismissible="false"
:manualHeadline="noNetworkShopsAlertHeaderText" />
</div> </div>
<div class="text-center"> </div>
<textLink </div>
id="preferredShopNotListedLink" <div class="container-fluid fade-on-route-transition">
linkType="navigation" <div class="row justify-content-center">
href="javascript:void(0)" <div class="col-md-6 col-xl-4">
:text="shopNotListedModalLink" <Form
@clickEvent="doNotSeeMyShopLinkClick" /> id="searchProvidersForm"
</div> @submit="onSubmit"
<siteFooter @invalidSubmit="onInvalidSubmit">
ref="siteFooter" <label
:cmsWidgetName="widget.siteFooter" id="tpaSearchQuestionLabel"
:isForwardActionDisabled="!meta.valid" for="tpaSearchQuestionField"
@ForwardClicked="forwardButtonAction" class="text-center fs-5 mt-5 mb-0 text-black w-100">
@backClicked="navigateBack" /> {{ tpaSearchQuestionLabel }}
</div> </label>
</Form> <label
</div> id="searchInstructions"
for="tpaSearchQuestionField"
class="text-center small darker-gray w-100 mb-4">
{{ searchInstructionsText }}
</label>
<div class="mb-5">
<textboxQuestion
id="tpaSearchQuestionField"
v-model="zipCode"
inputId="tpaSearchQuestionFieldInput"
:cmsWidgetName="widget.tpaSearchQuestion"
:includeSearchIcon="true"
:displayQuestionText="false"
isRequired
:isDisabled="reloadingProviders"
:validationRules="rules.zipCode"
@clickEvent="searchClick" />
</div>
</Form>
</div>
</div>
</div>
<div class="container-fluid fade-on-route-transition">
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<googleMap
id="map"
class="mb-4"
:addresses="providerAddresses"
:zipCode="mapZipCode" />
<Form
id="providerSelectionForm"
v-slot="{ meta }"
@submit="onSubmit"
@invalidSubmit="onInvalidSubmit">
<dropdownQuestion
id="searchRadiusFilter"
v-model="filter"
:cmsWidgetName="widget.filterByQuestion"
inputId="searchRadiusFilterInput"
:options="filterOptions"
disableAutoFill
:isDisabled="reloadingProviders"
:validationRules="rules.filter" />
<loader
v-if="reloadingProviders"
id="providersLoader"
loaderPosition="center"
loaderColor="blue"
:width="2"
:height="2"
class="my-4" />
<div v-else>
<div class="my-4">
<buttonQuestion
id="selectProviderQuestion"
v-model="selectedProviderNumber"
buttonTypeString="shopListButton"
:buttonTypeObject="shopListButton"
class="radioQuestion"
:answers="providerButtonData"
groupName="chooseShop"
textPosition="text-start"
isRequired
:validationRules="rules.provider"
:additionalButtonData="additionalButtonData" />
<alert
v-if="providers?.length === 0 ?? true"
id="alertNoNetworkProviders"
:cmsWidgetName="widget.noNetworkShopsAlert"
alertClass="alert-warning"
:isDismissible="false"
:manualHeadline="
noNetworkShopsAlertHeaderText
" />
</div>
<div class="text-center">
<textLink
id="preferredShopNotListedLink"
linkType="navigation"
href="javascript:void(0)"
:text="shopNotListedModalLink"
@clickEvent="doNotSeeMyShopLinkClick" />
</div>
<siteFooter
ref="siteFooter"
:cmsWidgetName="widget.siteFooter"
:isForwardActionDisabled="!meta.valid"
@ForwardClicked="forwardButtonAction"
@backClicked="navigateBack" />
</div>
</Form>
</div>
</div>
</div> </div>
</div>
</template> </template>
<script> <script>
// Components // Components
@ -144,7 +146,7 @@ const radiusFilterPairs = [
{ radius: 15, filter: '15 miles' }, { radius: 15, filter: '15 miles' },
{ radius: 25, filter: '25 miles' }, { radius: 25, filter: '25 miles' },
{ radius: 50, filter: '50 miles' }, { radius: 50, filter: '50 miles' },
{ radius: 100, filter: '100 miles' } { radius: 100, filter: '100 miles' },
]; ];
function convertRadiusFilterToInteger(filter) { function convertRadiusFilterToInteger(filter) {
@ -200,12 +202,12 @@ export default {
googleMap, googleMap,
siteFooter, siteFooter,
// eslint-disable-next-line vue/no-reserved-component-names // eslint-disable-next-line vue/no-reserved-component-names
Form Form,
}, },
mixins: [BaseFormMixin], mixins: [BaseFormMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
const { zipCode, filter, providers, providerNumber } = const { zipCode, filter, providers, providerNumber } =
await getInitialSearchData(); await getInitialSearchData();
const cmsContent = await fetchCmsContentForPage(to.query.issPage); const cmsContent = await fetchCmsContentForPage(to.query.issPage);
next(async (vm) => { next(async (vm) => {
@ -223,7 +225,7 @@ export default {
selectedProviderNumber: '', selectedProviderNumber: '',
reloadingProviders: false, reloadingProviders: false,
additionalButtonData: { additionalButtonData: {
displayAvailabilityIndicators: false displayAvailabilityIndicators: false,
}, },
widget: { widget: {
siteHeader: 'SiteHeaderWidget', siteHeader: 'SiteHeaderWidget',
@ -232,23 +234,23 @@ export default {
filterByQuestion: 'FilterByQuestion', filterByQuestion: 'FilterByQuestion',
noNetworkShopsAlert: 'NoNetworkShopsAlertWidget', noNetworkShopsAlert: 'NoNetworkShopsAlertWidget',
shopNotListedLink: 'ShopNotListedLink', shopNotListedLink: 'ShopNotListedLink',
siteFooter: 'SiteFooterWidget' siteFooter: 'SiteFooterWidget',
}, },
rules: { rules: {
zipCode: `${globalRules.ZIP_CODE_REQUIRED}|${globalRules.ZIP_CODE_SEARCH_FORMAT}`, zipCode: `${globalRules.ZIP_CODE_REQUIRED}|${globalRules.ZIP_CODE_SEARCH_FORMAT}`,
filter: globalRules.OPTION_REQUIRED, // TODO do we even need this? filter: globalRules.OPTION_REQUIRED, // TODO do we even need this?
provider: globalRules.OPTION_REQUIRED provider: globalRules.OPTION_REQUIRED,
}, },
shopListButton: shallowRef(shopListButton) shopListButton: shallowRef(shopListButton),
}; };
}, },
computed: { computed: {
filterOptions() { filterOptions() {
const filterByAnswers = const filterByAnswers =
this.getCmsContent( this.getCmsContent(
this.widget.filterByQuestion, this.widget.filterByQuestion,
widgetFields.INPUT_QUESTION_WIDGET.ANSWERS widgetFields.INPUT_QUESTION_WIDGET.ANSWERS
) ?? []; ) ?? [];
const filterByAnswersObj = {}; const filterByAnswersObj = {};
[...filterByAnswers].forEach((answer) => { [...filterByAnswers].forEach((answer) => {
filterByAnswersObj[answer.Name] = answer.Name; filterByAnswersObj[answer.Name] = answer.Name;
@ -284,21 +286,25 @@ export default {
}, },
providerAddresses() { providerAddresses() {
return ( return (
this.providers?.map((provider) => this.getProviderAddress(provider)) this.providers?.map((provider) =>
?? [] this.getProviderAddress(provider)
) ?? []
); );
}, },
providerButtonData() { providerButtonData() {
return ( return (
this.providers?.map((provider) => this.providers?.map((provider) =>
this.getShopButtonDataFromProvider(provider)) ?? [] this.getShopButtonDataFromProvider(provider)
) ?? []
); );
}, },
selectedProviderIsSafeliteShop() { selectedProviderIsSafeliteShop() {
return ( return (
this.providers?.find((p) => p.providerNumber === this.selectedProviderNumber)?.isSafeliteShop ?? false this.providers?.find(
(p) => p.providerNumber === this.selectedProviderNumber
)?.isSafeliteShop ?? false
); );
} },
}, },
watch: { watch: {
async filter() { async filter() {
@ -312,13 +318,15 @@ export default {
providers(newProviders) { providers(newProviders) {
if (this.dataLoaded) { if (this.dataLoaded) {
this.selectedProviderNumber = this.selectedProviderNumber =
newProviders?.length === 1 ?? false newProviders?.length === 1 ?? false
? newProviders[0]?.providerNumber ?? '' ? newProviders[0]?.providerNumber ?? ''
: ''; : '';
} }
}, },
selectedProviderNumber(newNumber) { selectedProviderNumber(newNumber) {
const provider = this.providers?.find((p) => p.providerNumber === newNumber); const provider = this.providers?.find(
(p) => p.providerNumber === newNumber
);
if (provider && this.dataLoaded) { if (provider && this.dataLoaded) {
useMainStore().updateServiceLocation({ useMainStore().updateServiceLocation({
searchFilter: this.filter, searchFilter: this.filter,
@ -330,14 +338,14 @@ export default {
city: provider.address?.city, city: provider.address?.city,
state: provider.address?.state, state: provider.address?.state,
zipCode: provider.address?.zipCode, zipCode: provider.address?.zipCode,
zipCodeCtu: provider.address?.zipCodeCtu zipCodeCtu: provider.address?.zipCodeCtu,
}, },
companyName: provider?.companyName, companyName: provider?.companyName,
phoneNumber: provider?.phoneNumber phoneNumber: provider?.phoneNumber,
} },
}); });
} }
} },
}, },
beforeUpdate() { beforeUpdate() {
if (!this.dataLoaded) { if (!this.dataLoaded) {
@ -373,7 +381,7 @@ export default {
const addressLine1 = toTitleCase(provider?.address?.streetAddress); const addressLine1 = toTitleCase(provider?.address?.streetAddress);
const joinString = const joinString =
addressLine1.length > 0 && addressLine2.length > 0 ? ', ' : ''; addressLine1.length > 0 && addressLine2.length > 0 ? ', ' : '';
return [addressLine1, addressLine2].join(joinString); return [addressLine1, addressLine2].join(joinString);
}, },
async getProviderButtonData() { async getProviderButtonData() {
@ -402,7 +410,8 @@ export default {
} }
const scenario = this.selectedProviderIsSafeliteShop const scenario = this.selectedProviderIsSafeliteShop
? this.navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE_SHOP ? this.navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE_SHOP
: this.navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP; : this.navigationScenarios
.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP;
this.$router.navigate(scenario, this.$route); this.$router.navigate(scenario, this.$route);
}, },
getCustomValueFromString(str) { getCustomValueFromString(str) {
@ -416,10 +425,10 @@ export default {
getShopButtonDataFromProvider(provider) { getShopButtonDataFromProvider(provider) {
const cellNumber = toDisplayPhoneNumber(provider?.phoneNumber); const cellNumber = toDisplayPhoneNumber(provider?.phoneNumber);
const distance = const distance =
provider?.distanceInMiles !== null provider?.distanceInMiles !== null &&
&& !Number.isNaN(parseFloat(provider?.distanceInMiles)) !Number.isNaN(parseFloat(provider?.distanceInMiles))
? +provider.distanceInMiles.toFixed(1) ? +provider.distanceInMiles.toFixed(1)
: null; : null;
return { return {
buttonLabel: provider?.companyName ?? '', buttonLabel: provider?.companyName ?? '',
@ -427,15 +436,15 @@ export default {
buttonBodyCopy: `${this.getProviderAddress(provider)}<br>${ buttonBodyCopy: `${this.getProviderAddress(provider)}<br>${
cellNumber ?? '' cellNumber ?? ''
}`, }`,
value: provider?.providerNumber ?? '' value: provider?.providerNumber ?? '',
}; };
} },
} },
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.darker-gray { .darker-gray {
color: map-get($colors, 'darker-gray'); color: map-get($colors, 'darker-gray');
} }
</style> </style>

View file

@ -15,7 +15,8 @@
ref="vehicleBanner" ref="vehicleBanner"
:cmsWidgetName="widget.vehicleBanner" :cmsWidgetName="widget.vehicleBanner"
:displayGenericVehicleImage="false" :displayGenericVehicleImage="false"
class="mt-5" /> <!-- TODO fix styling --> class="mt-5" />
<!-- TODO fix styling -->
<textBlock <textBlock
ref="subHeaderTitle" ref="subHeaderTitle"
:customText="subHeaderTitle" :customText="subHeaderTitle"
@ -51,9 +52,7 @@
<div <div
v-for="(section, index) in sections" v-for="(section, index) in sections"
:key="section.title"> :key="section.title">
<hr <hr v-if="index !== 0" class="my-3" />
v-if="index !== 0"
class="my-3" />
<reviewBlock <reviewBlock
:id="'review-block-' + index" :id="'review-block-' + index"
:customHeaderText="section.title" :customHeaderText="section.title"
@ -109,13 +108,25 @@ import contactDetailsDrawer from '@/layouts/tpa-submit/contact-details-drawer/co
import siteFooter from '@/iss-components/site-footer/site-footer.vue'; import siteFooter from '@/iss-components/site-footer/site-footer.vue';
// Supporting files // Supporting files
import { fetchCmsContentForPage, processIfStatements, getStringWithCustomValues } from '@/helpers/cms-content-helper.js'; import {
fetchCmsContentForPage,
processIfStatements,
getStringWithCustomValues,
} from '@/helpers/cms-content-helper.js';
import { Form } from 'vee-validate'; import { Form } from 'vee-validate';
import BaseFormMixin from '@/mixins/base-form-mixin.js'; import BaseFormMixin from '@/mixins/base-form-mixin.js';
import widgetFields from '@/constants/cms-widget-fields.js'; import widgetFields from '@/constants/cms-widget-fields.js';
import { useMainStore } from '@/store'; import { useMainStore } from '@/store';
import { toTitleCase, toDisplayPhoneNumber, formatAddress, formatAmountInDollars } from '@/helpers/text-helper.js'; import {
import { getDamageDisplayContent, getLocationAnswer } from '@/helpers/damage-review-content-generator.js'; toTitleCase,
toDisplayPhoneNumber,
formatAddress,
formatAmountInDollars,
} from '@/helpers/text-helper.js';
import {
getDamageDisplayContent,
getLocationAnswer,
} from '@/helpers/damage-review-content-generator.js';
import damageLocationsSelected from '@/constants/damage-locations-selected.js'; import damageLocationsSelected from '@/constants/damage-locations-selected.js';
const VERIFYING_COVERAGE = 'Verifying coverage'; const VERIFYING_COVERAGE = 'Verifying coverage';
@ -132,7 +143,7 @@ export default {
siteFooter, siteFooter,
contactDetailsDrawer, contactDetailsDrawer,
// eslint-disable-next-line vue/no-reserved-component-names // eslint-disable-next-line vue/no-reserved-component-names
Form Form,
}, },
mixins: [BaseFormMixin], mixins: [BaseFormMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
@ -155,41 +166,66 @@ export default {
vehicle: 'VehicleSubTitle', vehicle: 'VehicleSubTitle',
damage: 'DamageSubTitle', damage: 'DamageSubTitle',
shop: 'PreferredShopSubTitle', shop: 'PreferredShopSubTitle',
contactInfo: 'ContactDetailsSubTitle' contactInfo: 'ContactDetailsSubTitle',
}, },
damageLocations: 'DamageLocationsWidget', damageLocations: 'DamageLocationsWidget',
orderDetails: 'OrderDetailsContent', orderDetails: 'OrderDetailsContent',
footer: 'SiteFooterWidget' footer: 'SiteFooterWidget',
}, },
companyName, companyName,
customValueMap: { customValueMap: {
glassShop: companyName glassShop: companyName,
} },
}; };
}, },
computed: { computed: {
subHeaderTitle() { subHeaderTitle() {
return this.getCmsContent(this.widget.siteSubHeader, widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT); return this.getCmsContent(
this.widget.siteSubHeader,
widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT
);
}, },
subHeaderBodyOne() { subHeaderBodyOne() {
return this.getCmsContent(this.widget.siteSubHeader, widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT); return this.getCmsContent(
this.widget.siteSubHeader,
widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT
);
}, },
subHeaderBodyTwo() { subHeaderBodyTwo() {
const cmsContent = this.getCmsContent(this.widget.siteSubHeader, widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT_2); const cmsContent = this.getCmsContent(
this.widget.siteSubHeader,
widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT_2
);
return getStringWithCustomValues(cmsContent, this.customValueMap); return getStringWithCustomValues(cmsContent, this.customValueMap);
}, },
serviceSummaryText() { serviceSummaryText() {
return this.getCmsContent(this.widget.serviceSummary, widgetFields.TEXT_BLOCK_WIDGET.TEXT); return this.getCmsContent(
this.widget.serviceSummary,
widgetFields.TEXT_BLOCK_WIDGET.TEXT
);
}, },
orderDetailsTitle() { orderDetailsTitle() {
return this.getCmsContent(this.widget.orderDetails, widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT); return this.getCmsContent(
this.widget.orderDetails,
widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT
);
}, },
orderDetailsBody() { orderDetailsBody() {
const orderDetailsBodyText = this.getCmsContent(this.widget.orderDetails, widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT); const orderDetailsBodyText = this.getCmsContent(
return processIfStatements(orderDetailsBodyText, 'custom', this.getCustomValueFromString); this.widget.orderDetails,
widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT
);
return processIfStatements(
orderDetailsBodyText,
'custom',
this.getCustomValueFromString
);
}, },
forwardButtonText() { forwardButtonText() {
return this.getCmsContent(this.widget.footer, widgetFields.FOOTER_WIDGET.FORWARD_BUTTON_TEXT); return this.getCmsContent(
this.widget.footer,
widgetFields.FOOTER_WIDGET.FORWARD_BUTTON_TEXT
);
}, },
isVerified() { isVerified() {
return useMainStore().order.payment.insuranceCoverage.isVerified; return useMainStore().order.payment.insuranceCoverage.isVerified;
@ -198,7 +234,9 @@ export default {
return useMainStore().order.currentDeductible; return useMainStore().order.currentDeductible;
}, },
deductibleBoxValue() { deductibleBoxValue() {
return this.isVerified ? formatAmountInDollars(this.currentDeductible) : VERIFYING_COVERAGE; return this.isVerified
? formatAmountInDollars(this.currentDeductible)
: VERIFYING_COVERAGE;
}, },
getVehicleLines() { getVehicleLines() {
const { year, make, model } = useMainStore().order.vehicle; const { year, make, model } = useMainStore().order.vehicle;
@ -208,15 +246,27 @@ export default {
return [line]; return [line];
}, },
locationAnswers() { locationAnswers() {
return this.getInputQuestionWidgetAnswersNullSafe(this.widget.damageLocations); return this.getInputQuestionWidgetAnswersNullSafe(
this.widget.damageLocations
);
}, },
driverSideDamageAnswers() { driverSideDamageAnswers() {
const answerContent = getLocationAnswer(damageLocationsSelected.DRIVER, this.locationAnswers); const answerContent = getLocationAnswer(
return this.getInputQuestionWidgetAnswersNullSafe(answerContent?.SubWidgetName); damageLocationsSelected.DRIVER,
this.locationAnswers
);
return this.getInputQuestionWidgetAnswersNullSafe(
answerContent?.SubWidgetName
);
}, },
passengerSideDamageAnswers() { passengerSideDamageAnswers() {
const answerContent = getLocationAnswer(damageLocationsSelected.PASSENGER, this.locationAnswers); const answerContent = getLocationAnswer(
return this.getInputQuestionWidgetAnswersNullSafe(answerContent?.SubWidgetName); damageLocationsSelected.PASSENGER,
this.locationAnswers
);
return this.getInputQuestionWidgetAnswersNullSafe(
answerContent?.SubWidgetName
);
}, },
getDamageLines() { getDamageLines() {
const { glassToReplace, isRepair } = useMainStore().order.damage; const { glassToReplace, isRepair } = useMainStore().order.damage;
@ -229,76 +279,108 @@ export default {
); );
}, },
getPreferredShopLines() { getPreferredShopLines() {
const { phoneNumber, address } = useMainStore().order.serviceLocation.provider; const { phoneNumber, address } =
useMainStore().order.serviceLocation.provider;
const { streetAddress, city, state, zipCode } = address; const { streetAddress, city, state, zipCode } = address;
const displayAddress = formatAddress(streetAddress, null, city, state, zipCode); const displayAddress = formatAddress(
streetAddress,
null,
city,
state,
zipCode
);
const displayPhoneNumber = toDisplayPhoneNumber(phoneNumber); const displayPhoneNumber = toDisplayPhoneNumber(phoneNumber);
return [toTitleCase(this.companyName ?? ''), displayAddress, displayPhoneNumber]; return [
toTitleCase(this.companyName ?? ''),
displayAddress,
displayPhoneNumber,
];
}, },
getContactInfoLines() { getContactInfoLines() {
const { firstName, lastName, emailAddress, servicePhone } = useMainStore().contactInfo; const { firstName, lastName, emailAddress, servicePhone } =
useMainStore().contactInfo;
return [ return [
`${firstName} ${lastName}`, `${firstName} ${lastName}`,
emailAddress ?? '', emailAddress ?? '',
toDisplayPhoneNumber(servicePhone) toDisplayPhoneNumber(servicePhone),
]; ];
} },
}, },
methods: methods: {
{ setSections() {
setSections() { const vehicleScenario = useMainStore().isPolicyVehicle
const vehicleScenario = useMainStore().isPolicyVehicle ? this.navigationScenarios.EDIT_POLICY_VEHICLE
? this.navigationScenarios.EDIT_POLICY_VEHICLE : this.navigationScenarios.EDIT_VEHICLE;
: this.navigationScenarios.EDIT_VEHICLE; this.sections = [
this.sections = [ this.getSection(
this.getSection(this.widget.subheader.vehicle, this.getVehicleLines, () => this.navigate(vehicleScenario)), this.widget.subheader.vehicle,
// eslint-disable-next-line max-len this.getVehicleLines,
this.getSection(this.widget.subheader.damage, this.getDamageLines, () => this.navigate(this.navigationScenarios.EDIT_DAMAGE)), () => this.navigate(vehicleScenario)
// eslint-disable-next-line max-len ),
this.getSection(this.widget.subheader.shop, this.getPreferredShopLines, () => this.navigate(this.navigationScenarios.EDIT_PREFERRED_SHOP)), // eslint-disable-next-line max-len
// eslint-disable-next-line max-len this.getSection(
this.getSection(this.widget.subheader.contactInfo, this.getContactInfoLines, this.openContactDetailsModal) this.widget.subheader.damage,
]; this.getDamageLines,
}, () => this.navigate(this.navigationScenarios.EDIT_DAMAGE)
getSection(widgetName, lines, onClick) { ),
return { // eslint-disable-next-line max-len
title: this.getCmsContent(widgetName, widgetFields.TEXT_BLOCK_WIDGET.TEXT), this.getSection(
lines, this.widget.subheader.shop,
onClickEdit: onClick this.getPreferredShopLines,
}; () =>
}, this.navigate(
forwardButtonAction() { this.navigationScenarios.EDIT_PREFERRED_SHOP
this.navigate(this.navigationScenarios.CLICKED_FORWARD); )
}, ),
navigate(scenario) { // eslint-disable-next-line max-len
this.$router.navigate(scenario, this.$route); this.getSection(
}, this.widget.subheader.contactInfo,
getCustomValueFromString(str) { this.getContactInfoLines,
switch (str) { this.openContactDetailsModal
case 'deductibleAboveZero': ),
return this.isVerified && this.currentDeductible !== 0; ];
case 'zeroDeductible': },
return this.isVerified && this.currentDeductible === 0; getSection(widgetName, lines, onClick) {
case 'verifyingCoverage': return {
return !this.isVerified; title: this.getCmsContent(
default: widgetName,
return null; widgetFields.TEXT_BLOCK_WIDGET.TEXT
} ),
}, lines,
openContactDetailsModal() { onClickEdit: onClick,
this.$refs.contactDetailsDrawer.openModal(); };
},
forwardButtonAction() {
this.navigate(this.navigationScenarios.CLICKED_FORWARD);
},
navigate(scenario) {
this.$router.navigate(scenario, this.$route);
},
getCustomValueFromString(str) {
switch (str) {
case 'deductibleAboveZero':
return this.isVerified && this.currentDeductible !== 0;
case 'zeroDeductible':
return this.isVerified && this.currentDeductible === 0;
case 'verifyingCoverage':
return !this.isVerified;
default:
return null;
} }
} },
openContactDetailsModal() {
this.$refs.contactDetailsDrawer.openModal();
},
},
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.tpa-submit__title--line-height { .tpa-submit__title--line-height {
line-height: map-get($spacers, 6); line-height: map-get($spacers, 6);
} }
.text-color--darker-gray { .text-color--darker-gray {
color: $darker-gray color: $darker-gray;
} }
.text-color--black { .text-color--black {
@ -307,7 +389,6 @@ export default {
hr { hr {
opacity: 1; opacity: 1;
color: $gray-350 color: $gray-350;
} }
</style> </style>

View file

@ -1,99 +1,101 @@
<template> <template>
<Form <Form
ref="theForm" ref="theForm"
v-slot="{ meta }" v-slot="{ meta }"
@submit="onSubmit" @submit="onSubmit"
@invalidSubmit="onInvalidSubmit"> @invalidSubmit="onInvalidSubmit">
<div class="container-fluid fade-on-route-transition replace-options-question"> <div
<div class="row justify-content-center"> class="container-fluid fade-on-route-transition replace-options-question">
<div class="col-md-6 px-0 px-md-2"> <div class="row justify-content-center">
<siteHeader cmsWidgetName="SiteHeaderWidget" /> <div class="col-md-6 px-0 px-md-2">
<siteHeader cmsWidgetName="SiteHeaderWidget" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<alert
v-if="shouldDisplayVehicleChangeAlert"
ref="vehicleChangeAlert"
class="mt-5 mb-0"
cmsWidgetName="VehicleChangeAlert"
alertClass="alert-warning"
:isDismissible="false" />
<vehicleBanner
class="mb-4"
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="false" />
<siteSubHeader cmsWidgetName="SiteSubHeaderWidget" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6">
<damageLocationQuestion
ref="damageLocation"
v-model="selectedDamageLocations"
cmsWidgetName="DamageLocationQuestion"
groupName="DamageLocationQuestion" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<windshieldOptions
ref="windshieldOptions"
v-model="selectedWindshieldOptions"
:hasRepairReplaceConflict="hasRepairReplaceConflict"
:hasSplitSingleConflict="hasSplitSingleConflict"
:selectedDamageLocations="selectedDamageLocations"
class="mb-3" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<alert
v-if="hasRepairReplaceConflict"
class="my-5"
cmsWidgetName="HasReplacementConflict"
alertClass="alert-danger"
:isDismissible="false" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<sideDoorOptions
v-show="!hasRepairReplaceConflict"
ref="sideDoorOptions"
v-model="sideDoorOptionsData"
cmsWidgetName="SideDoorSideQuestion"
groupName="SideDoorSideQuestion"
:selectedDamageLocations="selectedDamageLocations"
class="mb-1" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<replaceOptionsQuestion
ref="backGlassOptions"
v-model="selectedRearReplaceOptions"
cmsWidgetName="RearReplaceOptionsQuestion"
:isAvailable="
isRearWindowDamageLocation &&
!hasRepairReplaceConflict
"
groupName="BackGlassReplaceOptionsQuestion"
validationRules="replace-options-required" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<site-footer
ref="siteFooter"
class="mt-5"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid"
@backClicked="navigateBack"
@forwardClicked="forwardButtonAction" />
</div>
</div>
</div> </div>
</div> </Form>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<alert
v-if="shouldDisplayVehicleChangeAlert"
ref="vehicleChangeAlert"
class="mt-5 mb-0"
cmsWidgetName="VehicleChangeAlert"
alertClass="alert-warning"
:isDismissible="false" />
<vehicleBanner
class="mb-4"
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="false" />
<siteSubHeader cmsWidgetName="SiteSubHeaderWidget" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6">
<damageLocationQuestion
ref="damageLocation"
v-model="selectedDamageLocations"
cmsWidgetName="DamageLocationQuestion"
groupName="DamageLocationQuestion" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<windshieldOptions
ref="windshieldOptions"
v-model="selectedWindshieldOptions"
:hasRepairReplaceConflict="hasRepairReplaceConflict"
:hasSplitSingleConflict="hasSplitSingleConflict"
:selectedDamageLocations="selectedDamageLocations"
class="mb-3" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<alert
v-if="hasRepairReplaceConflict"
class="my-5"
cmsWidgetName="HasReplacementConflict"
alertClass="alert-danger"
:isDismissible="false" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<sideDoorOptions
v-show="!hasRepairReplaceConflict"
ref="sideDoorOptions"
v-model="sideDoorOptionsData"
cmsWidgetName="SideDoorSideQuestion"
groupName="SideDoorSideQuestion"
:selectedDamageLocations="selectedDamageLocations"
class="mb-1" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<replaceOptionsQuestion
ref="backGlassOptions"
v-model="selectedRearReplaceOptions"
cmsWidgetName="RearReplaceOptionsQuestion"
:isAvailable="
isRearWindowDamageLocation && !hasRepairReplaceConflict
"
groupName="BackGlassReplaceOptionsQuestion"
validationRules="replace-options-required" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<site-footer
ref="siteFooter"
class="mt-5"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid"
@backClicked="navigateBack"
@forwardClicked="forwardButtonAction" />
</div>
</div>
</div>
</Form>
</template> </template>
<script> <script>
@ -140,7 +142,7 @@ export default {
replaceOptionsQuestion, replaceOptionsQuestion,
// eslint-disable-next-line vue/no-reserved-component-names // eslint-disable-next-line vue/no-reserved-component-names
Form, Form,
alert alert,
}, },
mixins: [BaseFormMixin, vehicleQuestionsMixin], mixins: [BaseFormMixin, vehicleQuestionsMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
@ -148,18 +150,20 @@ export default {
// Call APIs // Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage); const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
const damageOptionsPromise = store.getDamageOptions(store.order.vehicle.carId); const damageOptionsPromise = store.getDamageOptions(
store.order.vehicle.carId
);
// Settle promises and get results // Settle promises and get results
const promiseResultMap = [ const promiseResultMap = [
{ {
resultKey: 'cmsContent', resultKey: 'cmsContent',
promise: cmsContentPromise promise: cmsContentPromise,
}, },
{ {
resultKey: 'damageOptions', resultKey: 'damageOptions',
promise: damageOptionsPromise promise: damageOptionsPromise,
} },
]; ];
const resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
@ -167,13 +171,23 @@ export default {
// Call the "next" function to complete the transition to this page. // Call the "next" function to complete the transition to this page.
next((vm) => { next((vm) => {
vm.setCmsContent(resultMap.cmsContent); vm.setCmsContent(resultMap.cmsContent);
vm.$refs.damageLocation.initializeComponent(resultMap.damageOptions); vm.$refs.damageLocation.initializeComponent(
vm.$refs.sideDoorOptions.initializeComponent( resultMap.damageOptions
resultMap.damageOptions.driverSideOptions.availableReplacementOptions, );
resultMap.damageOptions.passengerSideOptions.availableReplacementOptions vm.$refs.sideDoorOptions.initializeComponent(
resultMap.damageOptions.driverSideOptions
.availableReplacementOptions,
resultMap.damageOptions.passengerSideOptions
.availableReplacementOptions
);
vm.$refs.windshieldOptions.initializeComponent(
resultMap.damageOptions.windshieldOptions
.availableReplacementOptions
);
vm.$refs.backGlassOptions.initializeComponent(
resultMap.damageOptions.backGlassOptions
.availableReplacementOptions
); );
vm.$refs.windshieldOptions.initializeComponent(resultMap.damageOptions.windshieldOptions.availableReplacementOptions);
vm.$refs.backGlassOptions.initializeComponent(resultMap.damageOptions.backGlassOptions.availableReplacementOptions);
}); });
}, },
setup() { setup() {
@ -187,77 +201,100 @@ export default {
sideDoorOptionsData: { sideDoorOptionsData: {
selectedDoorSides: this.getDoorSidesFromStore(), selectedDoorSides: this.getDoorSidesFromStore(),
selectedDriverSideReplaceOptions: selectedDriverSideReplaceOptions:
this.getDriverSideReplaceOptionsFromStore(), this.getDriverSideReplaceOptionsFromStore(),
selectedPassengerSideReplaceOptions: selectedPassengerSideReplaceOptions:
this.getPassengerSideReplaceOptionsFromStore() this.getPassengerSideReplaceOptionsFromStore(),
}, },
selectedWindshieldOptions: this.getWindshieldOptionsFromStore(), selectedWindshieldOptions: this.getWindshieldOptionsFromStore(),
selectedRearReplaceOptions: this.getRearReplaceOptionsFromStore() selectedRearReplaceOptions: this.getRearReplaceOptionsFromStore(),
}; };
}, },
computed: { computed: {
isWindshieldDamageLocation() { isWindshieldDamageLocation() {
return this.selectedDamageLocations.some((selectedDamages) => return this.selectedDamageLocations.some(
selectedDamages.toUpperCase() === damageLocationsCms.WINDSHIELD); (selectedDamages) =>
selectedDamages.toUpperCase() ===
damageLocationsCms.WINDSHIELD
);
}, },
isSideDoorDamageLocation() { isSideDoorDamageLocation() {
return this.selectedDamageLocations.some((selectedDamages) => return this.selectedDamageLocations.some(
selectedDamages.toUpperCase() === damageLocationsCms.SIDEDOOR); (selectedDamages) =>
selectedDamages.toUpperCase() ===
damageLocationsCms.SIDEDOOR
);
}, },
isRearWindowDamageLocation() { isRearWindowDamageLocation() {
return this.selectedDamageLocations.some((selectedDamages) => return this.selectedDamageLocations.some(
selectedDamages.toUpperCase() === damageLocationsCms.REARWINDOW); (selectedDamages) =>
selectedDamages.toUpperCase() ===
damageLocationsCms.REARWINDOW
);
}, },
isWindshieldRepair() { isWindshieldRepair() {
return ( return (
this.isWindshieldDamageLocation this.isWindshieldDamageLocation &&
&& this.selectedWindshieldOptions.selectedWindshieldDamageType this.selectedWindshieldOptions.selectedWindshieldDamageType ===
=== damageLocationsSelected.REPAIR damageLocationsSelected.REPAIR
); );
}, },
isDriverSideReplace() { isDriverSideReplace() {
if (!this.isSideDoorDamageLocation) return false; if (!this.isSideDoorDamageLocation) return false;
return this.sideDoorOptionsData.selectedDoorSides.some((selectedDriverSide) => return this.sideDoorOptionsData.selectedDoorSides.some(
selectedDriverSide.toUpperCase() === damageLocationsCms.DRIVERSIDE); (selectedDriverSide) =>
selectedDriverSide.toUpperCase() ===
damageLocationsCms.DRIVERSIDE
);
}, },
isPassengerSideReplace() { isPassengerSideReplace() {
if (!this.isSideDoorDamageLocation) return false; if (!this.isSideDoorDamageLocation) return false;
return this.sideDoorOptionsData.selectedDoorSides.some((selectedPassengerSide) => return this.sideDoorOptionsData.selectedDoorSides.some(
selectedPassengerSide.toUpperCase() (selectedPassengerSide) =>
=== damageLocationsCms.PASSENGERSIDE); selectedPassengerSide.toUpperCase() ===
damageLocationsCms.PASSENGERSIDE
);
}, },
hasRepairReplaceConflict() { hasRepairReplaceConflict() {
return ( return (
this.isWindshieldDamageLocation this.isWindshieldDamageLocation &&
&& this.selectedDamageLocations.length > 1 this.selectedDamageLocations.length > 1 &&
&& this.isWindshieldRepair this.isWindshieldRepair
); );
}, },
hasSplitSingleConflict() { hasSplitSingleConflict() {
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((selectedSingleWindshield) => this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some(
selectedSingleWindshield.toUpperCase() (selectedSingleWindshield) =>
=== damageLocationsSelected.SINGLE.toUpperCase()) selectedSingleWindshield.toUpperCase() ===
&& (this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some((selectedDriverWindshield) => damageLocationsSelected.SINGLE.toUpperCase()
selectedDriverWindshield.toUpperCase() ) &&
=== damageLocationsSelected.DRIVER.toUpperCase()) (this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some(
|| this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some((selectedPassengerWindshield) => (selectedDriverWindshield) =>
selectedPassengerWindshield.toUpperCase() selectedDriverWindshield.toUpperCase() ===
=== damageLocationsSelected.PASSENGER.toUpperCase())) damageLocationsSelected.DRIVER.toUpperCase()
) ||
this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some(
(selectedPassengerWindshield) =>
selectedPassengerWindshield.toUpperCase() ===
damageLocationsSelected.PASSENGER.toUpperCase()
))
); );
}, },
shouldDisplayVehicleChangeAlert() { shouldDisplayVehicleChangeAlert() {
return this.$route.params[this.routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]; return this.$route.params[
} this.routerParams.DISPLAY_VEHICLE_CHANGE_ALERT
];
},
}, },
methods: { methods: {
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
@ -270,21 +307,32 @@ export default {
const glassSelections = []; const glassSelections = [];
if ( if (
this.mainStore.order.damage.glassToReplace?.some((glass) => glass.glassLocation === damageLocationsSelected.WINDSHIELD) this.mainStore.order.damage.glassToReplace?.some(
|| this.mainStore.order.damage.isRepair (glass) =>
glass.glassLocation ===
damageLocationsSelected.WINDSHIELD
) ||
this.mainStore.order.damage.isRepair
) { ) {
glassSelections.push(damageLocationsSelected.WINDSHIELD); glassSelections.push(damageLocationsSelected.WINDSHIELD);
} }
if ( if (
this.mainStore.order.damage.glassToReplace?.some((glass) => this.mainStore.order.damage.glassToReplace?.some(
glass.glassLocation === damageLocationsSelected.DRIVER (glass) =>
|| glass.glassLocation === damageLocationsSelected.PASSENGER) glass.glassLocation ===
damageLocationsSelected.DRIVER ||
glass.glassLocation ===
damageLocationsSelected.PASSENGER
)
) { ) {
glassSelections.push(damageLocationsSelected.SIDEDOOR); glassSelections.push(damageLocationsSelected.SIDEDOOR);
} }
if ( if (
this.mainStore.order.damage.glassToReplace?.some((glass) => glass.glassLocation === damageLocationsSelected.REAR) this.mainStore.order.damage.glassToReplace?.some(
(glass) =>
glass.glassLocation === damageLocationsSelected.REAR
)
) { ) {
glassSelections.push(damageLocationsSelected.REARWINDOW); glassSelections.push(damageLocationsSelected.REARWINDOW);
} }
@ -295,45 +343,62 @@ export default {
const windShieldOptions = { const windShieldOptions = {
selectedWindshieldDamageType: '', selectedWindshieldDamageType: '',
selectedWindshieldChipCount: null, selectedWindshieldChipCount: null,
selectedWindshieldReplaceOptions: [] selectedWindshieldReplaceOptions: [],
}; };
if (this.mainStore.order.damage.isRepair === undefined) return windshieldOptions; if (this.mainStore.order.damage.isRepair === undefined)
return windshieldOptions;
if (this.mainStore.order.damage.isRepair) { if (this.mainStore.order.damage.isRepair) {
windShieldOptions.selectedWindshieldDamageType = windShieldOptions.selectedWindshieldDamageType =
damageLocationsSelected.REPAIR; damageLocationsSelected.REPAIR;
windShieldOptions.selectedWindshieldChipCount = windShieldOptions.selectedWindshieldChipCount =
this.mainStore.order.damage.numberOfChips; this.mainStore.order.damage.numberOfChips;
} else { } else {
if ( if (
this.mainStore.order.damage.glassToReplace?.some((glass) => this.mainStore.order.damage.glassToReplace?.some(
glass.glassLocation === damageLocationsSelected.WINDSHIELD (glass) =>
&& glass.glassName === damageLocationsSelected.SINGLE) glass.glassLocation ===
damageLocationsSelected.WINDSHIELD &&
glass.glassName === damageLocationsSelected.SINGLE
)
) { ) {
windShieldOptions.selectedWindshieldDamageType = windShieldOptions.selectedWindshieldDamageType =
damageLocationsSelected.REPLACE; damageLocationsSelected.REPLACE;
windShieldOptions.selectedWindshieldReplaceOptions.push(damageLocationsSelected.SINGLE); windShieldOptions.selectedWindshieldReplaceOptions.push(
damageLocationsSelected.SINGLE
);
} }
if ( if (
this.mainStore.order.damage.glassToReplace?.some((glass) => this.mainStore.order.damage.glassToReplace?.some(
glass.glassLocation === damageLocationsSelected.WINDSHIELD (glass) =>
&& glass.glassName === damageLocationsSelected.DRIVER) glass.glassLocation ===
damageLocationsSelected.WINDSHIELD &&
glass.glassName === damageLocationsSelected.DRIVER
)
) { ) {
windShieldOptions.selectedWindshieldDamageType = windShieldOptions.selectedWindshieldDamageType =
damageLocationsSelected.REPLACE; damageLocationsSelected.REPLACE;
windShieldOptions.selectedWindshieldReplaceOptions.push(damageLocationsSelected.DRIVER); windShieldOptions.selectedWindshieldReplaceOptions.push(
damageLocationsSelected.DRIVER
);
} }
if ( if (
this.mainStore.order.damage.glassToReplace?.some((glass) => this.mainStore.order.damage.glassToReplace?.some(
glass.glassLocation === damageLocationsSelected.WINDSHIELD (glass) =>
&& glass.glassName === damageLocationsSelected.PASSENGER) glass.glassLocation ===
damageLocationsSelected.WINDSHIELD &&
glass.glassName ===
damageLocationsSelected.PASSENGER
)
) { ) {
windShieldOptions.selectedWindshieldDamageType = windShieldOptions.selectedWindshieldDamageType =
damageLocationsSelected.REPLACE; damageLocationsSelected.REPLACE;
windShieldOptions.selectedWindshieldReplaceOptions.push(damageLocationsSelected.PASSENGER); windShieldOptions.selectedWindshieldReplaceOptions.push(
damageLocationsSelected.PASSENGER
);
} }
} }
@ -342,13 +407,20 @@ export default {
getDoorSidesFromStore() { getDoorSidesFromStore() {
const doorSides = []; const doorSides = [];
if ( if (
this.mainStore.order.damage.glassToReplace?.some((glass) => glass.glassLocation === damageLocationsSelected.DRIVER) this.mainStore.order.damage.glassToReplace?.some(
(glass) =>
glass.glassLocation === damageLocationsSelected.DRIVER
)
) { ) {
doorSides.push(damageLocationsSelected.DRIVERSIDE); doorSides.push(damageLocationsSelected.DRIVERSIDE);
} }
if ( if (
this.mainStore.order.damage.glassToReplace?.some((glass) => glass.glassLocation === damageLocationsSelected.PASSENGER) this.mainStore.order.damage.glassToReplace?.some(
(glass) =>
glass.glassLocation ===
damageLocationsSelected.PASSENGER
)
) { ) {
doorSides.push(damageLocationsSelected.PASSENGERSIDE); doorSides.push(damageLocationsSelected.PASSENGERSIDE);
} }
@ -379,7 +451,10 @@ export default {
}, },
getRearReplaceOptionsFromStore() { getRearReplaceOptionsFromStore() {
const rearReplaceOptions = const rearReplaceOptions =
this.mainStore.order.damage.glassToReplace?.filter((glass) => glass.glassLocation === damageLocationsSelected.REAR)[0]?.glassName; this.mainStore.order.damage.glassToReplace?.filter(
(glass) =>
glass.glassLocation === damageLocationsSelected.REAR
)[0]?.glassName;
return rearReplaceOptions; return rearReplaceOptions;
}, },
@ -391,7 +466,8 @@ export default {
); );
if (this.isWindshieldRepair) { if (this.isWindshieldRepair) {
const supportingItems = await useMainStore().getSupportingItems(); const supportingItems =
await useMainStore().getSupportingItems();
useMainStore().updateSupportingItems(supportingItems.data); useMainStore().updateSupportingItems(supportingItems.data);
} }
@ -403,10 +479,13 @@ export default {
} else if (this.mainStore.order.vehicle.vin) { } else if (this.mainStore.order.vehicle.vin) {
// If vin already exists, navigate directly to vin-lookup // If vin already exists, navigate directly to vin-lookup
const partsOrQuestionsResponse = await this.getPartsOrQuestions(); const partsOrQuestionsResponse =
await this.getPartsOrQuestions();
if (partsOrQuestionsResponse.error) { if (partsOrQuestionsResponse.error) {
// To Do: Need requirement on what to do here // To Do: Need requirement on what to do here
window.console.error('Error on retrieving PartsOrQuestions'); window.console.error(
'Error on retrieving PartsOrQuestions'
);
this.$refs.siteFooter.removeLoader(); this.$refs.siteFooter.removeLoader();
return null; return null;
} }
@ -428,42 +507,48 @@ export default {
selectedGlassToReplace() { selectedGlassToReplace() {
const selectedGlassToReplace = []; const selectedGlassToReplace = [];
if (this.isWindshieldDamageLocation && !this.isWindshieldRepair) { if (this.isWindshieldDamageLocation && !this.isWindshieldRepair) {
this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.forEach((wsItem) => { this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.forEach(
selectedGlassToReplace.push({ (wsItem) => {
glassLocation: damageLocationsSelected.WINDSHIELD, selectedGlassToReplace.push({
glassName: wsItem glassLocation: damageLocationsSelected.WINDSHIELD,
}); glassName: wsItem,
}); });
}
);
} }
if (this.isDriverSideReplace) { if (this.isDriverSideReplace) {
this.sideDoorOptionsData.selectedDriverSideReplaceOptions.forEach((driverItem) => { this.sideDoorOptionsData.selectedDriverSideReplaceOptions.forEach(
selectedGlassToReplace.push({ (driverItem) => {
glassLocation: damageLocationsSelected.DRIVER, selectedGlassToReplace.push({
glassName: driverItem glassLocation: damageLocationsSelected.DRIVER,
}); glassName: driverItem,
}); });
}
);
} }
if (this.isPassengerSideReplace) { if (this.isPassengerSideReplace) {
this.sideDoorOptionsData.selectedPassengerSideReplaceOptions.forEach((passengerItem) => { this.sideDoorOptionsData.selectedPassengerSideReplaceOptions.forEach(
selectedGlassToReplace.push({ (passengerItem) => {
glassLocation: damageLocationsSelected.PASSENGER, selectedGlassToReplace.push({
glassName: passengerItem glassLocation: damageLocationsSelected.PASSENGER,
}); glassName: passengerItem,
}); });
}
);
} }
if (this.isRearWindowDamageLocation) { if (this.isRearWindowDamageLocation) {
selectedGlassToReplace.push({ selectedGlassToReplace.push({
glassLocation: damageLocationsSelected.REAR, glassLocation: damageLocationsSelected.REAR,
glassName: this.selectedRearReplaceOptions glassName: this.selectedRearReplaceOptions,
}); });
} }
return selectedGlassToReplace; return selectedGlassToReplace;
} },
} },
}; };
</script> </script>

View file

@ -1,39 +1,37 @@
<template> <template>
<Form <Form @submit="onSubmit" @invalidSubmit="onInvalidSubmit">
@submit="onSubmit" <div class="container-fluid fade-on-route-transition">
@invalidSubmit="onInvalidSubmit"> <div class="row justify-content-center">
<div class="container-fluid fade-on-route-transition"> <div class="col-md-6 px-0 px-md-2">
<div class="row justify-content-center"> <siteHeader cmsWidgetName="SiteHeaderWidget" />
<div class="col-md-6 px-0 px-md-2"> </div>
<siteHeader cmsWidgetName="SiteHeaderWidget" /> </div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<div class="select-car-form rounded">
<VehicleBanner
class="mt-2 mb-4"
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="false" />
<SiteSubHeader
cmsWidgetName="SiteSubHeaderWidget"
class="mt-5 mb-2" />
<VinLookupMethods
ref="VinLookupMethods"
v-model="selectedVinLookupMethod"
cmsWidgetName="VINLookupMethod"
groupName="VinLookupMethods" />
<SiteFooter
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="isForwardActionDisabled"
class="mt-5"
@backClicked="navigateBack"
@forwardClicked="forwardButtonAction" />
</div>
</div>
</div>
</div> </div>
</div> </Form>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<div class="select-car-form rounded">
<VehicleBanner
class="mt-2 mb-4"
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="false" />
<SiteSubHeader
cmsWidgetName="SiteSubHeaderWidget"
class="mt-5 mb-2" />
<VinLookupMethods
ref="VinLookupMethods"
v-model="selectedVinLookupMethod"
cmsWidgetName="VINLookupMethod"
groupName="VinLookupMethods" />
<SiteFooter
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="isForwardActionDisabled"
class="mt-5"
@backClicked="navigateBack"
@forwardClicked="forwardButtonAction" />
</div>
</div>
</div>
</div>
</Form>
</template> </template>
<script> <script>
// Import Supporting Files // Import Supporting Files
@ -53,13 +51,13 @@ import VinLookupMethods from '@/layouts/vehicle-lookup/vin-lookup-methods/vin-lo
export default { export default {
name: 'vehicle-lookup', name: 'vehicle-lookup',
components: { components: {
// eslint-disable-next-line vue/no-reserved-component-names // eslint-disable-next-line vue/no-reserved-component-names
Form, Form,
SiteFooter, SiteFooter,
SiteHeader, SiteHeader,
SiteSubHeader, SiteSubHeader,
VehicleBanner, VehicleBanner,
VinLookupMethods VinLookupMethods,
}, },
mixins: [BaseFormMixin], mixins: [BaseFormMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
@ -69,8 +67,8 @@ export default {
const promiseResultMap = [ const promiseResultMap = [
{ {
resultKey: 'cmsContent', resultKey: 'cmsContent',
promise: cmsContentPromise promise: cmsContentPromise,
} },
]; ];
const resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
@ -82,13 +80,13 @@ export default {
}, },
data() { data() {
return { return {
selectedVinLookupMethod: null selectedVinLookupMethod: null,
}; };
}, },
computed: { computed: {
isForwardActionDisabled() { isForwardActionDisabled() {
return this.selectedVinLookupMethod === null; return this.selectedVinLookupMethod === null;
} },
}, },
methods: { methods: {
arePagePrerequisiteValid() { arePagePrerequisiteValid() {
@ -118,7 +116,7 @@ export default {
break; break;
} }
}, },
resetDependentState() {} resetDependentState() {},
} },
}; };
</script> </script>

View file

@ -1,60 +1,59 @@
<template> <template>
<Form <Form ref="theForm" @submit="onSubmit" @invalidSubmit="onInvalidSubmit">
ref="theForm" <div class="container-fluid fade-on-route-transition">
@submit="onSubmit" <div class="row justify-content-center">
@invalidSubmit="onInvalidSubmit"> <div class="col-md-6 px-0 px-md-2">
<div class="container-fluid fade-on-route-transition"> <siteHeader cmsWidgetName="SiteHeaderWidget" />
<div class="row justify-content-center"> </div>
<div class="col-md-6 px-0 px-md-2"> </div>
<siteHeader cmsWidgetName="SiteHeaderWidget" /> <div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<vehicleBanner
ref="vehicleBanner"
class="mt-2 mb-4"
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="false" />
<siteSubHeader
ref="siteSubHeader"
cmsWidgetName="SiteSubHeaderWidget" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<alert
id="vehicle-parts-alert"
class="mt-3 mb-3"
alertClass="alert-warning"
cmsWidgetName="AlertWidget"
:isDismissible="false" />
<div v-for="(item, i) in PartsOrQuestions" :key="i">
<!-- Render horizontal lines if there is multi-glass (aka if i > 0) -->
<hr v-if="i > 0" />
<glassPartQuestion
:ref="`${RefPrefix}-${item.glassLocation}-${item.glassName}`"
v-model="
selectedGlassParts[
item.glassLocation + '-' + item.glassName
]
"
:glassLocation="item.glassLocation"
:glassName="item.glassName"
:colorAnswers="item.colorAnswers"
:alreadyPopulatedPartsData="
alreadyPopulatedPartsData
" />
</div>
<siteFooter
ref="siteFooter"
class="mt-5"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="isForwardActionDisabled"
@backClicked="navigateBackByVehicleQuestions"
@ForwardClicked="forwardButtonAction" />
</div>
</div>
</div> </div>
</div> </Form>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<vehicleBanner
ref="vehicleBanner"
class="mt-2 mb-4"
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="false" />
<siteSubHeader
ref="siteSubHeader"
cmsWidgetName="SiteSubHeaderWidget" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<alert
id="vehicle-parts-alert"
class="mt-3 mb-3"
alertClass="alert-warning"
cmsWidgetName="AlertWidget"
:isDismissible="false" />
<div
v-for="(item, i) in PartsOrQuestions"
:key="i">
<!-- Render horizontal lines if there is multi-glass (aka if i > 0) -->
<hr v-if="i > 0" />
<glassPartQuestion
:ref="`${RefPrefix}-${item.glassLocation}-${item.glassName}`"
v-model="
selectedGlassParts[item.glassLocation + '-' + item.glassName]
"
:glassLocation="item.glassLocation"
:glassName="item.glassName"
:colorAnswers="item.colorAnswers"
:alreadyPopulatedPartsData="alreadyPopulatedPartsData" />
</div>
<siteFooter
ref="siteFooter"
class="mt-5"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="isForwardActionDisabled"
@backClicked="navigateBackByVehicleQuestions"
@ForwardClicked="forwardButtonAction" />
</div>
</div>
</div>
</Form>
</template> </template>
<script> <script>
@ -78,25 +77,25 @@ import { useMainStore } from '@/store';
export default { export default {
name: 'vehicle-parts', name: 'vehicle-parts',
components: { components: {
// eslint-disable-next-line vue/no-reserved-component-names // eslint-disable-next-line vue/no-reserved-component-names
Form, Form,
glassPartQuestion, glassPartQuestion,
siteHeader, siteHeader,
vehicleBanner, vehicleBanner,
siteSubHeader, siteSubHeader,
siteFooter, siteFooter,
alert alert,
}, },
mixins: [BaseFormMixin, vehicleQuestionsMixin], mixins: [BaseFormMixin, vehicleQuestionsMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
// Call APIs // Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage); const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
// Settle promises and get results // Settle promises and get results
const promiseResultMap = [ const promiseResultMap = [
{ {
resultKey: 'cmsContent', resultKey: 'cmsContent',
promise: cmsContentPromise promise: cmsContentPromise,
} },
]; ];
const resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
// Call the "next" function to complete the transition to this page. // Call the "next" function to complete the transition to this page.
@ -105,26 +104,32 @@ export default {
// Glass Part Question dynamic component // Glass Part Question dynamic component
Object.keys(vm.$refs) Object.keys(vm.$refs)
.filter((r) => r.includes(vm.RefPrefix) && vm.$refs[r][0] !== undefined) .filter(
(r) =>
r.includes(vm.RefPrefix) && vm.$refs[r][0] !== undefined
)
.forEach((c) => .forEach((c) =>
vm.$refs[c][0].initializeComponent({ vm.$refs[c][0].initializeComponent({
ColorQuestionWidget: resultMap.cmsContent.ColorQuestionWidget, ColorQuestionWidget:
FeatureQuestionWidget: resultMap.cmsContent.FeatureQuestionWidget resultMap.cmsContent.ColorQuestionWidget,
})); FeatureQuestionWidget:
resultMap.cmsContent.FeatureQuestionWidget,
})
);
}); });
}, },
data() { data() {
return { return {
selectedGlassParts: {}, selectedGlassParts: {},
alertWidgetData: Object, alertWidgetData: Object,
alreadyPopulatedPartsData: [] alreadyPopulatedPartsData: [],
}; };
}, },
computed: { computed: {
isForwardActionDisabled() { isForwardActionDisabled() {
return ( return (
this.selectedGlassPartNumbers?.length this.selectedGlassPartNumbers?.length !==
!== this.PartsFromApi.partsOrQuestions?.length this.PartsFromApi.partsOrQuestions?.length
); );
}, },
selectedGlassPartNumbers() { selectedGlassPartNumbers() {
@ -151,13 +156,15 @@ export default {
FeatureAnswers: [ FeatureAnswers: [
{ {
FeatureAnswerText: FeatureAnswerText:
p.description === '' ? p.color : p.description, p.description === ''
PartNumber: p.partNumber ? p.color
} : p.description,
] PartNumber: p.partNumber,
},
],
}); });
return arr; return arr;
}, []) }, []),
})); }));
return mappedData; return mappedData;
@ -169,7 +176,7 @@ export default {
RefPrefix() { RefPrefix() {
return 'partQuestion'; return 'partQuestion';
} },
}, },
mounted() { mounted() {
this.LoadInitialPartsData(); this.LoadInitialPartsData();
@ -178,10 +185,11 @@ export default {
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
// Check if isRepair is populated and if the pageData we need is here (Parts data) // Check if isRepair is populated and if the pageData we need is here (Parts data)
return ( return (
useMainStore().damage.isRepair != null useMainStore().damage.isRepair != null &&
&& useMainStore().pageData(issPageValues.VEHICLE_PARTS) useMainStore().pageData(issPageValues.VEHICLE_PARTS) &&
&& Object.keys(useMainStore().pageData(issPageValues.VEHICLE_PARTS)) Object.keys(
.length !== 0 useMainStore().pageData(issPageValues.VEHICLE_PARTS)
).length !== 0
); );
}, },
async forwardButtonAction() { async forwardButtonAction() {
@ -189,19 +197,25 @@ export default {
// Match them to the parts from the API. // Match them to the parts from the API.
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
for (const [key, value] of Object.entries(this.PartsFromApi.partsOrQuestions)) { for (const [key, value] of Object.entries(
this.PartsFromApi.partsOrQuestions
)) {
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
for (const [partKey, partValue] of Object.entries(value.parts)) { for (const [partKey, partValue] of Object.entries(
value.parts
)) {
const currentPart = const currentPart =
this.PartsFromApi.partsOrQuestions[key].parts[partKey]; this.PartsFromApi.partsOrQuestions[key].parts[partKey];
const isMatched = this.selectedGlassPartNumbers.some((p) => p === currentPart.partNumber); const isMatched = this.selectedGlassPartNumbers.some(
(p) => p === currentPart.partNumber
);
if (isMatched) { if (isMatched) {
matchedParts.push({ matchedParts.push({
glassLocation: value.glassLocation, glassLocation: value.glassLocation,
glassName: value.glassName, glassName: value.glassName,
parts: [currentPart] parts: [currentPart],
}); });
} }
} }
@ -209,7 +223,9 @@ 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.siteFooter.removeLoader(); this.$refs.siteFooter.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'
);
} }
this.navigateForward(matchedParts, null); this.navigateForward(matchedParts, null);
@ -218,9 +234,9 @@ export default {
LoadInitialPartsData() { LoadInitialPartsData() {
const partsData = this.PartsFromApi; const partsData = this.PartsFromApi;
this.alreadyPopulatedPartsData = this.alreadyPopulatedPartsData =
this.mainStore.lineItems.glassParts === null this.mainStore.lineItems.glassParts === null
? [] ? []
: this.mainStore.lineItems.glassParts; : this.mainStore.lineItems.glassParts;
partsData.partsOrQuestions.map((g) => { partsData.partsOrQuestions.map((g) => {
// If the part is already populated, use the value from the store and populate the v-model. // If the part is already populated, use the value from the store and populate the v-model.
@ -228,19 +244,21 @@ export default {
const { partNumber } = this.alreadyPopulatedPartsData[key]; const { partNumber } = this.alreadyPopulatedPartsData[key];
g.parts.forEach((p) => { g.parts.forEach((p) => {
if (p.partNumber === partNumber) { if (p.partNumber === partNumber) {
this.selectedGlassParts[`${g.glassLocation}-${g.glassName}`] = p; this.selectedGlassParts[
`${g.glassLocation}-${g.glassName}`
] = p;
} }
}); });
}); });
}); });
} },
} },
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
#vehicle-parts-alert { #vehicle-parts-alert {
.alert-heading { .alert-heading {
margin-top: 0.25rem !important; margin-top: 0.25rem !important;
} }
} }
</style> </style>

View file

@ -1,75 +1,75 @@
<template> <template>
<Form <Form
ref="theForm" ref="theForm"
v-slot="{ meta }" v-slot="{ meta }"
@submit="onSubmit" @submit="onSubmit"
@invalidSubmit="onInvalidSubmit"> @invalidSubmit="onInvalidSubmit">
<div class="container-fluid fade-on-route-transition"> <div class="container-fluid fade-on-route-transition">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6 px-0 px-md-2"> <div class="col-md-6 px-0 px-md-2">
<siteHeader <siteHeader
class="mb-2 header" class="mb-2 header"
cmsWidgetName="SiteHeaderWidget" /> cmsWidgetName="SiteHeaderWidget" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<siteSubHeader
cmsWidgetName="SiteSubHeaderWidget"
class="siteSubHeader"
justification="center"
darkGraySubText />
<vehicleQuestion
ref="vehicleYearQuestion"
v-model="selectedYear"
class="mb-2 mt-4"
cmsWidgetName="VehicleYearQuestion"
:updateValues="updateYearValues"
validationRules="year-required"
inputId="yearQuestionField" />
<vehicleQuestion
ref="vehicleMakeQuestion"
v-model="selectedMake"
class="mb-2 mt-4"
cmsWidgetName="VehicleMakeQuestion"
:updateValues="updateMakeValues"
validationRules="make-required"
inputId="makeQuestionField" />
<vehicleQuestion
ref="vehicleModelQuestion"
v-model="selectedModel"
class="mb-2 mt-4"
cmsWidgetName="VehicleModelQuestion"
:updateValues="updateModelValues"
validationRules="model-required"
inputId="modelQuestionField" />
<vehicleQuestion
ref="vehicleStyleQuestion"
v-model="selectedStyle"
class="mb-2 mt-4"
cmsWidgetName="VehicleStyleQuestion"
:updateValues="updateStyleValues"
validationRules="style-required"
inputId="styleQuestionField" />
<vehicleBanner
ref="banner"
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="displayGeneric"
class="mt-5 mb-3" />
<siteFooter
ref="siteFooter"
class="mt-5"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid"
@ForwardClicked="forwardButtonAction"
@backClicked="navigateBack" />
</div>
</div>
</div> </div>
</div> </Form>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<siteSubHeader
cmsWidgetName="SiteSubHeaderWidget"
class="siteSubHeader"
justification="center"
darkGraySubText />
<vehicleQuestion
ref="vehicleYearQuestion"
v-model="selectedYear"
class="mb-2 mt-4"
cmsWidgetName="VehicleYearQuestion"
:updateValues="updateYearValues"
validationRules="year-required"
inputId="yearQuestionField" />
<vehicleQuestion
ref="vehicleMakeQuestion"
v-model="selectedMake"
class="mb-2 mt-4"
cmsWidgetName="VehicleMakeQuestion"
:updateValues="updateMakeValues"
validationRules="make-required"
inputId="makeQuestionField" />
<vehicleQuestion
ref="vehicleModelQuestion"
v-model="selectedModel"
class="mb-2 mt-4"
cmsWidgetName="VehicleModelQuestion"
:updateValues="updateModelValues"
validationRules="model-required"
inputId="modelQuestionField" />
<vehicleQuestion
ref="vehicleStyleQuestion"
v-model="selectedStyle"
class="mb-2 mt-4"
cmsWidgetName="VehicleStyleQuestion"
:updateValues="updateStyleValues"
validationRules="style-required"
inputId="styleQuestionField" />
<vehicleBanner
ref="banner"
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="displayGeneric"
class="mt-5 mb-3" />
<siteFooter
ref="siteFooter"
class="mt-5"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid"
@ForwardClicked="forwardButtonAction"
@backClicked="navigateBack" />
</div>
</div>
</div>
</Form>
</template> </template>
<script> <script>
@ -104,20 +104,20 @@ export default {
siteFooter, siteFooter,
// eslint-disable-next-line vue/no-reserved-component-names // eslint-disable-next-line vue/no-reserved-component-names
Form, Form,
vehicleQuestion vehicleQuestion,
}, },
mixins: [baseFormMixin], mixins: [baseFormMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
// Call APIs // Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage); const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
// Settle promises and get results // Settle promises and get results
const promiseResultMap = [ const promiseResultMap = [
{ {
resultKey: 'cmsContent', resultKey: 'cmsContent',
promise: cmsContentPromise promise: cmsContentPromise,
} },
]; ];
const resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
@ -129,7 +129,7 @@ export default {
}, },
props: { props: {
cmsWidgetName: String, cmsWidgetName: String,
validationRules: String validationRules: String,
}, },
data() { data() {
const { year, make, model, style } = useMainStore().order.vehicle; const { year, make, model, style } = useMainStore().order.vehicle;
@ -137,13 +137,13 @@ export default {
selectedYear: year, selectedYear: year,
selectedMake: make, selectedMake: make,
selectedModel: model, selectedModel: model,
selectedStyle: style selectedStyle: style,
}; };
}, },
computed: { computed: {
displayGeneric() { displayGeneric() {
return !this.selectedStyle; return !this.selectedStyle;
} },
}, },
watch: { watch: {
@ -171,7 +171,7 @@ export default {
selectedStyle(value) { selectedStyle(value) {
this.mainStore.updateVehicleStyle(value); this.mainStore.updateVehicleStyle(value);
this.mainStore.setVehicle(); this.mainStore.setVehicle();
} },
}, },
mounted() { mounted() {
this.$refs.vehicleYearQuestion.getNewValues(); this.$refs.vehicleYearQuestion.getNewValues();
@ -220,22 +220,22 @@ export default {
}, },
async updateStyleValues() { async updateStyleValues() {
return this.mainStore.getVehicleStyles(); return this.mainStore.getVehicleStyles();
} },
} },
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.select-car-form { .select-car-form {
margin-left: 0.75rem; margin-left: 0.75rem;
margin-right: 0.75rem; margin-right: 0.75rem;
} }
.siteSubHeader { .siteSubHeader {
margin-top: 1.5rem; margin-top: 1.5rem;
} }
.subheader-secondary { .subheader-secondary {
margin-top: 0.5rem; margin-top: 0.5rem;
padding: 0px; padding: 0px;
} }
</style> </style>

View file

@ -1,44 +1,44 @@
<template> <template>
<Form <Form
ref="theForm" ref="theForm"
v-slot="{ meta }" v-slot="{ meta }"
@submit="onSubmit" @submit="onSubmit"
@invalidSubmit="onInvalidSubmit"> @invalidSubmit="onInvalidSubmit">
<div class="container-fluid fade-on-route-transition"> <div class="container-fluid fade-on-route-transition">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6 px-0 px-md-2"> <div class="col-md-6 px-0 px-md-2">
<siteHeader cmsWidgetName="SiteHeaderWidget" /> <siteHeader cmsWidgetName="SiteHeaderWidget" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<div class="select-car-form rounded">
<vehicleBanner
class="mt-2 mb-4"
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="!carIdIsValid" />
<siteSubHeader cmsWidgetName="SiteSubHeaderWidget" />
<vinLookupAlerts
class="mt-5"
:activeAlertType="activeVehicleLookupAlertType" />
<vinQuestion
v-model="vin"
:mask="vinMask"
:isDisabled="vinPopulatedOnPageLoad"
textPosition="left" />
<vinLocationInformation />
<siteFooter
ref="siteFooter"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid"
class="mt-5"
@backClicked="navigateBack"
@forwardClicked="forwardButtonAction" />
</div>
</div>
</div>
</div> </div>
</div> </Form>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<div class="select-car-form rounded">
<vehicleBanner
class="mt-2 mb-4"
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="!carIdIsValid" />
<siteSubHeader cmsWidgetName="SiteSubHeaderWidget" />
<vinLookupAlerts
class="mt-5"
:activeAlertType="activeVehicleLookupAlertType" />
<vinQuestion
v-model="vin"
:mask="vinMask"
:isDisabled="vinPopulatedOnPageLoad"
textPosition="left" />
<vinLocationInformation />
<siteFooter
ref="siteFooter"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid"
class="mt-5"
@backClicked="navigateBack"
@forwardClicked="forwardButtonAction" />
</div>
</div>
</div>
</div>
</Form>
</template> </template>
<script> <script>
// Import Supporting Files // Import Supporting Files
@ -75,12 +75,12 @@ export default {
vehicleBanner, vehicleBanner,
vinLocationInformation, vinLocationInformation,
vinLookupAlerts, vinLookupAlerts,
vinQuestion vinQuestion,
}, },
mixins: [baseFormMixin, vehicleQuestionsMixin], mixins: [baseFormMixin, vehicleQuestionsMixin],
provide() { provide() {
return { return {
vehicleFromLookup: computed(() => this.vehicleFromLookup) vehicleFromLookup: computed(() => this.vehicleFromLookup),
}; };
}, },
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
@ -90,8 +90,8 @@ export default {
const promiseResultMap = [ const promiseResultMap = [
{ {
resultKey: 'cmsContent', resultKey: 'cmsContent',
promise: cmsContentPromise promise: cmsContentPromise,
} },
]; ];
const resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
@ -109,23 +109,23 @@ export default {
const vin = this.getVinFromStore(); const vin = this.getVinFromStore();
return { return {
activeVehicleLookupAlertType: activeVehicleLookupAlertType:
vin?.length > 0 && !this.hasValidCarId() vin?.length > 0 && !this.hasValidCarId()
? vehicleLookupAlertTypes.NOT_FOUND ? vehicleLookupAlertTypes.NOT_FOUND
: null, : null,
needToLookupVehicle: true, needToLookupVehicle: true,
vehicleFromLookup: null, vehicleFromLookup: null,
vinWithNonMatchingCarId: vin?.length > 0 && !this.hasValidCarId(), vinWithNonMatchingCarId: vin?.length > 0 && !this.hasValidCarId(),
vin, vin,
forwardButtonCarStyle: '', forwardButtonCarStyle: '',
vinPopulatedOnPageLoad: vin?.length > 0 && this.hasValidCarId() vinPopulatedOnPageLoad: vin?.length > 0 && this.hasValidCarId(),
}; };
}, },
computed: { computed: {
isCarIdDifferentFromTheStore() { isCarIdDifferentFromTheStore() {
return ( return (
this.vehicleFromLookup !== null this.vehicleFromLookup !== null &&
&& this.hasValidCarId() this.hasValidCarId() &&
&& this.vehicleFromLookup.carId !== this.mainStore.vehicle.carId this.vehicleFromLookup.carId !== this.mainStore.vehicle.carId
); );
}, },
isTwoIdenticalYMMVehicleFound() { isTwoIdenticalYMMVehicleFound() {
@ -141,7 +141,7 @@ export default {
if (this.vinPopulatedOnPageLoad) { if (this.vinPopulatedOnPageLoad) {
// TODO: Modify to remove side effects in computed // TODO: Modify to remove side effects in computed
this.activeVehicleLookupAlertType = this.activeVehicleLookupAlertType =
vehicleLookupAlertTypes.PERFECT_MATCH; vehicleLookupAlertTypes.PERFECT_MATCH;
this.needToLookupVehicle = false; this.needToLookupVehicle = false;
const lastSixChars = this.vin.substring(11, this.vin.length); const lastSixChars = this.vin.substring(11, this.vin.length);
return `!X!X!X!X!X!X!X!X!X!X!X${lastSixChars}`; return `!X!X!X!X!X!X!X!X!X!X!X${lastSixChars}`;
@ -150,15 +150,17 @@ export default {
}, },
carIdIsValid() { carIdIsValid() {
return this.hasValidCarId(); return this.hasValidCarId();
} },
}, },
watch: { watch: {
vin() { vin() {
this.resetActiveAlert(); this.resetActiveAlert();
this.$refs.siteFooter.enableForwardAction(); this.$refs.siteFooter.enableForwardAction();
this.needToLookupVehicle = true; this.needToLookupVehicle = true;
this.$refs.siteFooter.updateButtonText(this.getCmsContent('SiteFooterWidget', 'ForwardButtonText')); this.$refs.siteFooter.updateButtonText(
} this.getCmsContent('SiteFooterWidget', 'ForwardButtonText')
);
},
}, },
methods: { methods: {
arePagePrerequisiteValid() { arePagePrerequisiteValid() {
@ -169,7 +171,8 @@ export default {
}, },
hasValidCarId() { hasValidCarId() {
return ( return (
this.mainStore.vehicle.carId && this.mainStore.vehicle.carId !== '0' this.mainStore.vehicle.carId &&
this.mainStore.vehicle.carId !== '0'
); );
}, },
// NOTE: If form is not valid, this method is not called when 'Continue' button is clicked // NOTE: If form is not valid, this method is not called when 'Continue' button is clicked
@ -179,11 +182,16 @@ export default {
this.$refs.siteFooter.enableForwardAction(); this.$refs.siteFooter.enableForwardAction();
if (this.needToLookupVehicle) { if (this.needToLookupVehicle) {
const vehicleLookupResponse = await this.lookupVehicleByVin(this.vin); const vehicleLookupResponse = await this.lookupVehicleByVin(
this.vin
);
if (vehicleLookupResponse.error) { if (vehicleLookupResponse.error) {
this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.NOT_FOUND; this.activeVehicleLookupAlertType =
this.mainStore.setBailout(bailoutMessage.vehicleNotFound(this.vin)); vehicleLookupAlertTypes.NOT_FOUND;
this.mainStore.setBailout(
bailoutMessage.vehicleNotFound(this.vin)
);
this.resetVehicleFromLookup(); this.resetVehicleFromLookup();
this.$refs.siteFooter.removeLoader(); this.$refs.siteFooter.removeLoader();
// Temp solution to turn on 'disabled' style on the Continue button // Temp solution to turn on 'disabled' style on the Continue button
@ -196,25 +204,30 @@ export default {
this.mainStore.resetBailout(); this.mainStore.resetBailout();
// Add vin bcs the response from the service doesn't contain vin // Add vin bcs the response from the service doesn't contain vin
this.vehicleFromLookup = Object.assign(vehicleLookupResponse.data, { this.vehicleFromLookup = Object.assign(
vin: this.vin vehicleLookupResponse.data,
}); {
vin: this.vin,
}
);
} }
if (this.needToLookupVehicle && this.isCarIdDifferentFromTheStore) { if (this.needToLookupVehicle && this.isCarIdDifferentFromTheStore) {
if (this.isTwoIdenticalYMMVehicleFound) { if (this.isTwoIdenticalYMMVehicleFound) {
this.activeVehicleLookupAlertType = this.activeVehicleLookupAlertType =
vehicleLookupAlertTypes.TWO_IDENTICAL_YMM_MATCHED; vehicleLookupAlertTypes.TWO_IDENTICAL_YMM_MATCHED;
this.forwardButtonCarStyle = this.vehicleFromLookup.style; this.forwardButtonCarStyle = this.vehicleFromLookup.style;
} else { } else {
this.activeVehicleLookupAlertType = this.activeVehicleLookupAlertType =
vehicleLookupAlertTypes.NOT_MATCHED; vehicleLookupAlertTypes.NOT_MATCHED;
} }
const vehicleYearMakeModelStyle = const vehicleYearMakeModelStyle =
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
`${this.vehicleFromLookup.year} ${this.vehicleFromLookup.make} ${this.vehicleFromLookup.model} ${this.forwardButtonCarStyle}`; `${this.vehicleFromLookup.year} ${this.vehicleFromLookup.make} ${this.vehicleFromLookup.model} ${this.forwardButtonCarStyle}`;
this.$refs.siteFooter.updateButtonText(`Continue with ${vehicleYearMakeModelStyle}`); this.$refs.siteFooter.updateButtonText(
`Continue with ${vehicleYearMakeModelStyle}`
);
this.$refs.siteFooter.removeLoader(); this.$refs.siteFooter.removeLoader();
this.needToLookupVehicle = false; this.needToLookupVehicle = false;
@ -224,13 +237,16 @@ export default {
let isSelectedGlassAvailableForVehicle = true; let isSelectedGlassAvailableForVehicle = true;
if (this.isCarIdDifferentFromTheStore) { if (this.isCarIdDifferentFromTheStore) {
isSelectedGlassAvailableForVehicle = await isGlassAvailableForCarId(this.vehicleFromLookup.carId); isSelectedGlassAvailableForVehicle =
await isGlassAvailableForCarId(
this.vehicleFromLookup.carId
);
} }
// navigate back to vehicle-damage // navigate back to vehicle-damage
if ( if (
this.isCarIdDifferentFromTheStore this.isCarIdDifferentFromTheStore &&
&& !isSelectedGlassAvailableForVehicle !isSelectedGlassAvailableForVehicle
) { ) {
this.mainStore.updateVehicle(this.vehicleFromLookup); this.mainStore.updateVehicle(this.vehicleFromLookup);
this.mainStore.resetDamageState(); this.mainStore.resetDamageState();
@ -280,8 +296,8 @@ export default {
} catch (responseError) { } catch (responseError) {
return { return {
error: { error: {
status: responseError.status status: responseError.status,
} },
}; };
} }
}, },
@ -291,7 +307,7 @@ export default {
resetVehicleFromLookup() { resetVehicleFromLookup() {
this.vehicleFromLookup = null; this.vehicleFromLookup = null;
}, },
resetDependentState() {} resetDependentState() {},
} },
}; };
</script> </script>

View file

@ -1,149 +1,147 @@
<template> <template>
<Form <Form
ref="theForm" ref="theForm"
v-slot="{ meta }" v-slot="{ meta }"
@submit="onSubmit" @submit="onSubmit"
@invalidSubmit="onInvalidSubmit"> @invalidSubmit="onInvalidSubmit">
<div class="container-fluid fade-on-route-transition"> <div class="container-fluid fade-on-route-transition">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6 px-0 px-md-2"> <div class="col-md-6 px-0 px-md-2">
<siteHeader cmsWidgetName="SiteHeaderWidget" /> <siteHeader cmsWidgetName="SiteHeaderWidget" />
</div> </div>
</div> </div>
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6 col-xl-4"> <div class="col-md-6 col-xl-4">
<siteSubHeader <siteSubHeader
cmsWidgetName="SiteSubHeaderWidget" cmsWidgetName="SiteSubHeaderWidget"
class="mt-4" /> class="mt-4" />
<textboxQuestion <textboxQuestion
ref="policyNumber" ref="policyNumber"
v-model="welcomePageModel.policyNumber" v-model="welcomePageModel.policyNumber"
inputId="policyNumberField" inputId="policyNumberField"
cmsWidgetName="PolicyNumberQuestion" cmsWidgetName="PolicyNumberQuestion"
isRequired isRequired
disableAutoFill disableAutoFill
:isDisabled="isPolicyHolderDisabled" :isDisabled="isPolicyHolderDisabled"
:validationRules="rules.policyNumber" :validationRules="rules.policyNumber"
class="mb-3" /> class="mb-3" />
<textboxQuestion <textboxQuestion
ref="policyZip" ref="policyZip"
v-model="welcomePageModel.policyZipCode" v-model="welcomePageModel.policyZipCode"
inputId="policyZipCode" inputId="policyZipCode"
cmsWidgetName="PolicyZipQuestion" cmsWidgetName="PolicyZipQuestion"
isRequired isRequired
mask="#####" mask="#####"
:isDisabled="isPolicyZipDisabled" :isDisabled="isPolicyZipDisabled"
:validationRules="rules.policyZip" :validationRules="rules.policyZip"
class="mb-3" /> class="mb-3" />
<textboxQuestion <textboxQuestion
ref="dateOfLoss" ref="dateOfLoss"
v-model="welcomePageModel.dateOfLoss" v-model="welcomePageModel.dateOfLoss"
type="date" type="date"
cmsWidgetName="DateOfLossQuestion" cmsWidgetName="DateOfLossQuestion"
inputId="dateOfLossField" inputId="dateOfLossField"
isRequired isRequired
:isDisabled="isDateOfLossDisabled" :isDisabled="isDateOfLossDisabled"
disableAutoFill disableAutoFill
:max="new Date().toJSON().slice(0, 10)" :max="new Date().toJSON().slice(0, 10)"
:min="'1972-12-01'" :min="'1972-12-01'"
:validationRules="rules.lossDate" :validationRules="rules.lossDate"
class="mb-3" /> class="mb-3" />
<textBlock <textBlock
cmsWidgetName="DamageDateEstimateWidget" cmsWidgetName="DamageDateEstimateWidget"
typeStyle="small" typeStyle="small"
class="mb-3" /> class="mb-3" />
<dropdownQuestion <dropdownQuestion
id="welcomeDropdown" id="welcomeDropdown"
ref="damageCause" ref="damageCause"
v-model="welcomePageModel.damageCause" v-model="welcomePageModel.damageCause"
cmsWidgetName="DamageCauseQuestion" cmsWidgetName="DamageCauseQuestion"
inputId="damageCauseQuestionField" inputId="damageCauseQuestionField"
:options="DamageCauseOptions" :options="DamageCauseOptions"
disableAutoFill disableAutoFill
:validationRules="rules.damageOption" :validationRules="rules.damageOption"
placeHolderText="Select an option" placeHolderText="Select an option"
class="mb-3" /> class="mb-3" />
<textboxQuestion <textboxQuestion
ref="phoneNumber" ref="phoneNumber"
v-model="welcomePageModel.phoneNumber" v-model="welcomePageModel.phoneNumber"
inputId="phoneNumberField" inputId="phoneNumberField"
cmsWidgetName="PhoneNumberQuestion" cmsWidgetName="PhoneNumberQuestion"
:validationRules="rules.phoneNumber" :validationRules="rules.phoneNumber"
isRequired isRequired
:mask="phoneMask" :mask="phoneMask"
disableAutoFill disableAutoFill
class="mb-3" /> class="mb-3" />
<textboxQuestion <textboxQuestion
ref="email" ref="email"
v-model="welcomePageModel.email" v-model="welcomePageModel.email"
inputId="emailField" inputId="emailField"
cmsWidgetName="EmailAddressQuestion" cmsWidgetName="EmailAddressQuestion"
:validationRules="rules.email" :validationRules="rules.email"
isRequired isRequired
disableAutoFill disableAutoFill
class="mb-3" /> class="mb-3" />
<textboxQuestion <textboxQuestion
v-if="displayDamageCityQuestion" v-if="displayDamageCityQuestion"
ref="damageCity" ref="damageCity"
v-model="welcomePageModel.damageCity" v-model="welcomePageModel.damageCity"
class="mb-3" class="mb-3"
inputId="damageCityField" inputId="damageCityField"
cmsWidgetName="DamageCityQuestion" cmsWidgetName="DamageCityQuestion"
isRequired isRequired
disableAutoFill disableAutoFill
:validationRules="rules.lossCity" /> :validationRules="rules.lossCity" />
<dropdownQuestion <dropdownQuestion
v-if="displayDamageStateQuestion" v-if="displayDamageStateQuestion"
id="welcomeDropdown" id="welcomeDropdown"
ref="state" ref="state"
v-model="welcomePageModel.damageState" v-model="welcomePageModel.damageState"
class="mb-3" class="mb-3"
cmsWidgetName="DamageStateQuestion" cmsWidgetName="DamageStateQuestion"
inputId="8fdf9dc2e13e430eb57529499dceb3eb" inputId="8fdf9dc2e13e430eb57529499dceb3eb"
:options="getStates" :options="getStates"
:validationRules="rules.lossState" :validationRules="rules.lossState"
isRequired isRequired
disableAutoFill disableAutoFill
placeHolderText="Select an option" /> placeHolderText="Select an option" />
<buttonQuestion <buttonQuestion
v-if="displayGlassOnlyQuestion" v-if="displayGlassOnlyQuestion"
ref="glassOnlyDamage" ref="glassOnlyDamage"
v-model="welcomePageModel.isDamageGlassOnly" v-model="welcomePageModel.isDamageGlassOnly"
class="px-0 mt-4" class="px-0 mt-4"
cmsWidgetName="GlassOnlyQuestion" cmsWidgetName="GlassOnlyQuestion"
inputId="isDamageGlassOnly" inputId="isDamageGlassOnly"
:answers="DamageGlassOnlyOptions" :answers="DamageGlassOnlyOptions"
:questionText="DamageGlassOnlyQuestion" :questionText="DamageGlassOnlyQuestion"
groupName="glassOnlyDamageOption" groupName="glassOnlyDamageOption"
buttonTypeString="listButtonHorizontal" buttonTypeString="listButtonHorizontal"
:validationRules="rules.damageOption" :validationRules="rules.damageOption"
isRequired isRequired
isSmallQuestionLabelText isSmallQuestionLabelText
disableAutoFill /> disableAutoFill />
<div <div id="welcomeFooter" class="row">
id="welcomeFooter" <alert
class="row"> v-if="displayInvalidZipAlert"
<alert ref="alertInvalidZip"
v-if="displayInvalidZipAlert" class="my-4"
ref="alertInvalidZip" cmsWidgetName="AlertInvalidZipWidget"
class="my-4" alertClass="alert-danger"
cmsWidgetName="AlertInvalidZipWidget" :isDismissible="false" />
alertClass="alert-danger" <siteFooter
:isDismissible="false" /> ref="siteFooter"
<siteFooter class="mt-3"
ref="siteFooter" cmsWidgetName="SiteFooterWidget"
class="mt-3" :isForwardActionDisabled="!meta.valid"
cmsWidgetName="SiteFooterWidget" @ForwardClicked="forwardButtonAction" />
:isForwardActionDisabled="!meta.valid" </div>
@ForwardClicked="forwardButtonAction" /> </div>
</div> </div>
</div> <!-- Footer image component must have parent (usually container-fluid)
</div>
<!-- Footer image component must have parent (usually container-fluid)
set to display: flex and height 100dvh or height 100% --> set to display: flex and height 100dvh or height 100% -->
<footerImage /> <footerImage />
</div> </div>
</Form> </Form>
</template> </template>
<script> <script>
@ -163,7 +161,7 @@ import footerImage from '@/iss-components/site-footer/footer-image/footer-image.
import { import {
fetchCmsContentForPage, fetchCmsContentForPage,
fetchGlobalCmsContent, fetchGlobalCmsContent,
updateCmsSiteHeader updateCmsSiteHeader,
} from '@/helpers/cms-content-helper'; } from '@/helpers/cms-content-helper';
import settleAllPromises from '@/helpers/layout-helper'; import settleAllPromises from '@/helpers/layout-helper';
import { required } from '@/helpers/validation-rules'; import { required } from '@/helpers/validation-rules';
@ -196,25 +194,25 @@ export default {
siteFooter, siteFooter,
textBlock, textBlock,
// eslint-disable-next-line vue/no-reserved-component-names // eslint-disable-next-line vue/no-reserved-component-names
Form Form,
}, },
mixins: [BaseFormMixin], mixins: [BaseFormMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
// Call APIs // Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage); const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
const cmsGlobalSiteHeaderContentPromise = const cmsGlobalSiteHeaderContentPromise =
fetchGlobalCmsContent('iss-siteheader'); fetchGlobalCmsContent('iss-siteheader');
// Settle promises and get results // Settle promises and get results
const promiseResultMap = [ const promiseResultMap = [
{ {
resultKey: 'cmsContent', resultKey: 'cmsContent',
promise: cmsContentPromise promise: cmsContentPromise,
}, },
{ {
resultKey: 'globalSiteHeaderCmsContent', resultKey: 'globalSiteHeaderCmsContent',
promise: cmsGlobalSiteHeaderContentPromise promise: cmsGlobalSiteHeaderContentPromise,
} },
]; ];
// use resultMap to populate layout content. // use resultMap to populate layout content.
@ -246,8 +244,8 @@ export default {
lossState: `${globalRules.DATE_OF_LOSS_STATE_REQUIRED}`, lossState: `${globalRules.DATE_OF_LOSS_STATE_REQUIRED}`,
policyNumber: `${globalRules.POLICY_NUMBER_REQUIRED}|${globalRules.POLICY_NUMBER_FORMAT}`, policyNumber: `${globalRules.POLICY_NUMBER_REQUIRED}|${globalRules.POLICY_NUMBER_FORMAT}`,
policyZip: `${globalRules.POLICY_ZIP_REQUIRED}|${globalRules.POLICY_ZIP_FORMAT}`, policyZip: `${globalRules.POLICY_ZIP_REQUIRED}|${globalRules.POLICY_ZIP_FORMAT}`,
phoneNumber: `${globalRules.PHONE_NUMBER_REQUIRED}|${globalRules.PHONE_NUMBER_FORMAT}` phoneNumber: `${globalRules.PHONE_NUMBER_REQUIRED}|${globalRules.PHONE_NUMBER_FORMAT}`,
} },
}; };
}, },
computed: { computed: {
@ -302,16 +300,18 @@ export default {
}, },
phoneMask() { phoneMask() {
return MaskaFormattedMasks.PHONE_NUMBER; return MaskaFormattedMasks.PHONE_NUMBER;
} },
}, },
methods: { methods: {
async forwardButtonAction() { async forwardButtonAction() {
await this.mainStore.validateZip({ zip: this.welcomePageModel.policyZipCode }) await this.mainStore
.validateZip({ zip: this.welcomePageModel.policyZipCode })
.then(async (zipInfo) => { .then(async (zipInfo) => {
if (zipInfo?.data?.isValid === true) { if (zipInfo?.data?.isValid === true) {
this.mainStore.updatePolicyData(this.welcomePageModel); this.mainStore.updatePolicyData(this.welcomePageModel);
this.mainStore.order.serviceLocation.zipCodeCtu = zipInfo.data.zipCodeCtu?.toString(); this.mainStore.order.serviceLocation.zipCodeCtu =
zipInfo.data.zipCodeCtu?.toString();
const billToInfo = await this.getBillToInfo( const billToInfo = await this.getBillToInfo(
this.mainStore.issConfig.parentAccountNumber, this.mainStore.issConfig.parentAccountNumber,
@ -319,16 +319,31 @@ export default {
); );
if (billToInfo !== null) { if (billToInfo !== null) {
this.mainStore.issConfig.billToAccountNumber = billToInfo.billToAccountNumber; this.mainStore.issConfig.billToAccountNumber =
this.mainStore.issConfig.itacCashBillToNumber = billToInfo.itacCashBillToNumber; billToInfo.billToAccountNumber;
this.mainStore.issConfig.itacFnrBillToNumber = billToInfo.itacFnrBillToNumber; this.mainStore.issConfig.itacCashBillToNumber =
billToInfo.itacCashBillToNumber;
this.mainStore.issConfig.itacFnrBillToNumber =
billToInfo.itacFnrBillToNumber;
} }
await this.mainStore.getDuplicateReferrals() await this.mainStore
.then(() => {}, () => {}) .getDuplicateReferrals()
.then(
() => {},
() => {}
)
.finally(async () => { .finally(async () => {
if (this.isCoverageEnabled && !this.maxCoverageLookupAttemptsReached) { if (
await this.mainStore.getCoveragePolicyInfo()?.then(() => {}, () => {}); this.isCoverageEnabled &&
!this.maxCoverageLookupAttemptsReached
) {
await this.mainStore
.getCoveragePolicyInfo()
?.then(
() => {},
() => {}
);
} else { } else {
this.mainStore.order.policy.policyLookupSuccessful = false; this.mainStore.order.policy.policyLookupSuccessful = false;
} }
@ -347,24 +362,29 @@ export default {
providerNumber: providerNumber.toString(), providerNumber: providerNumber.toString(),
billToSelectionCriteria: { billToSelectionCriteria: {
typeOfClaim: 'GLASS ONLY', typeOfClaim: 'GLASS ONLY',
lineOfBusiness: 'PERSONAL' lineOfBusiness: 'PERSONAL',
} },
}; };
const response = await globalMethods.callHttpClient({ const response = await globalMethods.callHttpClient({
method: endpoints.GetBillToInfo.method, method: endpoints.GetBillToInfo.method,
endpoint: endpoints.GetBillToInfo.url, endpoint: endpoints.GetBillToInfo.url,
payload payload,
}); });
return response.data; return response.data;
} catch (err) { } catch (err) {
console.error(`Error Status Code: ${err.data?.status}: ${err.data?.title}`); console.error(
`Error Status Code: ${err.data?.status}: ${err.data?.title}`
);
return null; return null;
} }
}, },
navigateForward() { navigateForward() {
if (this.mainStore.applicationUser.duplicateOrders?.length > 0 ?? false) { if (
this.mainStore.applicationUser.duplicateOrders?.length > 0 ??
false
) {
this.$router.navigate( this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_WITH_DUPLICATES, this.navigationScenarios.CLICKED_FORWARD_WITH_DUPLICATES,
this.$route, this.$route,
@ -409,36 +429,37 @@ export default {
damageCause: this.mainStore.order.policy.damageCause, damageCause: this.mainStore.order.policy.damageCause,
damageState: this.mainStore.order.policy.damageState, damageState: this.mainStore.order.policy.damageState,
damageCity: this.mainStore.order.policy.damageCity, damageCity: this.mainStore.order.policy.damageCity,
isDamageGlassOnly: this.mainStore.order.policy.isDamageGlassOnly, isDamageGlassOnly:
this.mainStore.order.policy.isDamageGlassOnly,
phoneNumber: this.mainStore.order.customer.phoneNumber, phoneNumber: this.mainStore.order.customer.phoneNumber,
email: this.mainStore.order.customer.emailAddress, email: this.mainStore.order.customer.emailAddress,
isPolicyNumberDisabled: isPolicyNumberDisabled:
this.mainStore.order.policy.isPolicyNumberDisabled this.mainStore.order.policy.isPolicyNumberDisabled,
}; };
} },
} },
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
form { form {
height: 100dvh; height: 100dvh;
.container-fluid { .container-fluid {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
} }
} }
@-moz-document url-prefix() { @-moz-document url-prefix() {
// Temporary solution that prevents the Continue button from being hidden in Firefox // Temporary solution that prevents the Continue button from being hidden in Firefox
#welcomeFooter { #welcomeFooter {
position: static !important; position: static !important;
} }
// Fixes Firefox styling defect for placeholder text in dropdown fields // Fixes Firefox styling defect for placeholder text in dropdown fields
#welcomeDropdown { #welcomeDropdown {
.form-select { .form-select {
padding: 0.75rem 1rem; padding: 0.75rem 1rem;
}
} }
}
} }
</style> </style>