DigitalConsumer.FixMyGlass/src/layouts/confirmation/confirmation.vue

646 lines
24 KiB
Vue

<template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm">
<loadingModal notFullScreen ref="loadingModal" />
<funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
<div class="container page-container-grouped-styles">
<div class="row">
<div class="col-12 col-md-10 col-lg-8 col-xl-7">
<div class="header-container">
<img :src="ScheduleConfirmationImage" />
<span v-html="ScheduleConfirmationText"></span>
</div>
<div class="emailtext" v-html="ConfirmationEmailText"></div>
<div class="main">
<div class="scheduleText">
<p>{{ ScheduleDateFormatted }}</p>
<p>{{ ScheduleTimeFormatted }}</p>
</div>
<addToCalendar
mobileWidgetName="AddToCalendar_Mobile"
inShopWidgetName="AddToCalendar_InShop"
dropOffWidgetName="AddToCalendar_DropOff"
overnightDropOffWidgetName="AddToCalendar_OvernightDropOff"
allDayDropOffWidgetName="AddToCalendar_AllDayDropOff"
sameDayDropOffWidgetName="AddToCalendar_SameDayDropOff"
:serviceLocationFullAddress="ServiceLocationFullAddress"
:providerFullAddress="ProviderFullAddress"
:appointmentType="AppointmentType"
:scheduleDate="ScheduleDate"
:scheduleStartTime="ScheduleStartTime"
:scheduleEndTime="ScheduleEndTime" />
<div class="appointment-text" v-html="AppointmentWordingText"></div>
<textBlock
:customText="AppointmentDuration"
justifyText="center"
typeStyle="medium"
class="duration-text-block" />
</div>
<div class="mt-3" v-if="shouldDisplayFosterLove">
<donationBlock
cmsWidgetName="DonationWidget"
v-model="donationAmount"
:donationValues="donationValues"
:showDonationSuccess="showDonationSuccess"
:showDonationError="showDonationError"
@DonationAddedEvent="addDonationToOrder"
ref="donationBlock" />
</div>
<hr class="mt-4 mb-0" />
<cart
v-if="ShowCart"
:damage="damageInfo"
:availableVaps="vaps"
:allowItemRemoval="false"
v-model="lineItemsWithoutDonation"
:donationCartItem="donationLineItem"
:showAsPaid="isPia"
servicePackageOptionsCmsName="ServicePackageTitle"
recyclingModalCmsWidgetName="RecycleModal"
:isInsurance="isInsurance"
:insuranceDeductible="currentDeductible"
:insuranceCompanyName="insuranceCompanyName"
:showInsuranceCoverageAs="showInsuranceCoverageAs"
:shouldHideRecalibration="shouldHideRecalibration"
:isExpandedOnLoad="false"
:isItac="isItac"
:isNoComp="isNoComp"
:isMSRFeeApplicable="isMSRFeeApplicable" />
<hr class="mb-5" />
</div>
</div>
</div>
</Form>
</template>
<script>
// Components
import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
import addToCalendar from "@/layouts/confirmation/add-to-calendar/add-to-calendar";
import cart from "@/fmg-components/cart/cart";
import textBlock from "@/digital-components/text-block/text-block";
//Supporting files
import baseMixin from "@/mixins/base-mixin.js";
import store from "@/store";
import { storeActions } from "@/constants/store-actions";
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { AppointmentTypeStrings, RouteCodeFlags } from "@/constants/schedule-constants";
import { settleAllPromises } from "@/helpers/layout-helper";
import { applicationConfig } from "@/constants/application-config.js";
import { convertDateStringToDate } from "@/layouts/schedule/helpers/schedule-helper";
import { deepClone } from "@/helpers/object-helper";
import { Form } from "vee-validate";
import { get12HourTimeFormat, get12HourTimeMobileFormat } from "@/helpers/date-helper";
import { coverageStatus } from "@/constants/insurance";
import { getDisplayTextForDurationLength } from "@/helpers/duration-length-helper";
import { experimentSettings } from "@/constants/experiments";
import experimentMixin from "@/mixins/experiment-mixin.js";
import { containsRecalParts } from "@/helpers/recal-helper.js";
import donationBlock from "@/experiment-components/donation-block.vue";
import { partNumberStrings } from "@/constants/part-number-strings";
import { partTypeStrings } from "@/constants/part-type-strings";
import analyticsMixin from "@/mixins/analytics-mixin";
export default {
name: "confirmation",
async beforeRouteEnter(to, from, next) {
// Experiments
const hasRecalPriceRemoveExperiment = await store.getters.shouldHideRecalibration;
let applicationUser = baseMixin.methods.hasSubmittedApplicationUser()
? baseMixin.methods.getSubmittedApplicationUser()
: store.getters.applicationUser;
let hasFosterLoveExperiment = experimentMixin.methods.hasSettingEqualTo(
experimentSettings.FOSTER_LOVE,
"true",
applicationUser.experiments
);
// send part question data to SPS API for logging
const orderFromStore = await deepClone(store.getters.order);
const ctu = orderFromStore.serviceLocation?.zipCodeCtu;
const partsOrQuestions = orderFromStore.damage?.partQuestionAnswers;
if (partsOrQuestions) {
partsOrQuestions.forEach((pqa) => {
const payloadPartQuestions = [];
pqa.answeredQuestions.forEach((answer) => {
payloadPartQuestions.push({
questionSeq: answer?.questionNum,
questionText: answer?.questionText,
answerText: answer?.selectedAnswerText,
basePart: pqa?.result,
});
});
const payload = {
eon: orderFromStore.eon,
ctu: ctu,
workOrderId: orderFromStore.workOrderId,
workOrderNumber: orderFromStore.workOrderNumber,
carId: orderFromStore.vehicle.carId,
glassLocation: pqa?.glassLocation,
partQuestions: payloadPartQuestions,
};
baseMixin.methods.dispatchStoreAction(
storeActions.LOG_PART_QUESTIONS,
payload,
false
);
});
}
// Create order
await baseMixin.methods.dispatchStoreAction(storeActions.CREATE_SUBMITTED_STATE); // This nulls the Store order
// Call APIs
const submittedOrder = baseMixin.methods.getSubmittedOrder();
const cmsContentPromise = fetchCmsContentForPage(to.name);
const wipersPromise = baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.GET_WIPERS,
{
serviceZipCode: submittedOrder.serviceLocation.zipCode,
carId: submittedOrder.vehicle.carId,
},
"confirmation"
);
const rainDefensePromise = baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.GET_RAIN_DEFENSE,
null,
"confirmation"
);
// Settle promises and get results
const promiseResultMap = [
{
resultKey: "cmsContent",
promise: cmsContentPromise,
},
{
resultKey: "wipers",
promise: wipersPromise,
},
{
resultKey: "rainDefense",
promise: rainDefensePromise,
},
];
const resultMap = await settleAllPromises(promiseResultMap);
const availableVaps = [resultMap.rainDefense, ...resultMap.wipers];
const lineItemsFromSubmittedOrder = deepClone(submittedOrder.lineItems);
const isNoComp = submittedOrder?.policy?.isNoComp;
const isItac = submittedOrder?.policy?.isItac;
const isRecalibrationOnOrder = containsRecalParts(lineItemsFromSubmittedOrder);
const shouldHideRecalibration = () => {
if (isNoComp || isItac) {
return false;
}
return hasRecalPriceRemoveExperiment && isRecalibrationOnOrder;
};
const donationItems = lineItemsFromSubmittedOrder.supportingItems?.filter(
(item) => item.partType === partTypeStrings.DONATION
);
const donationAmount = donationItems?.length > 0 ? donationItems[0].sellingPrice : 0;
// Call the "next" function to complete the transition to this page.
next((vm) => {
vm.setCmsContent(resultMap.cmsContent);
vm.lineItems = lineItemsFromSubmittedOrder;
vm.vaps = availableVaps;
vm.submittedOrder = submittedOrder;
vm.shouldHideRecalibration = shouldHideRecalibration();
vm.shouldDisplayFosterLove = hasFosterLoveExperiment;
vm.donationAmount = donationAmount;
vm.showDonationSuccess = lineItemsFromSubmittedOrder.supportingItems?.some(
(item) => item.partType === partTypeStrings.DONATION
);
vm.pushAnalytics();
});
},
data() {
return {
lineItems: [],
vaps: [],
submittedOrder: null,
shouldHideRecalibration: null,
shouldDisplayFosterLove: null,
donationAmount: 0,
showDonationSuccess: null,
showDonationError: null,
};
},
computed: {
isRecalibrationOnOrder() {
return containsRecalParts(this?.lineItems);
},
ShowCart() {
if (this.isPia && this.submittedOrder?.settledTenderAmount == 0) {
return false;
}
return true;
},
ScheduleConfirmationText() {
return this.getCmsContent("ScheduleConfirmationWidget", "BodyText");
},
ScheduleConfirmationImage() {
return this.getCmsContent("ScheduleConfirmationWidget", "Image");
},
CustomerPortalLoginToken() {
return this.submittedOrder?.customerPortalLoginToken;
},
ConfirmationEmailText() {
return this.getCmsContent("ConfirmationEmailWidget", "BodyText")
?.replaceAll(
"{custom:submittedOrder.customer.emailAddress}",
this.submittedOrder?.customer?.emailAddress
)
?.replaceAll("{custom:MY_ACCOUNT_URL}", applicationConfig.MY_ACCOUNT)
?.replaceAll("{custom:CUSTOMER_PORTAL_LOGIN_TOKEN}", this.CustomerPortalLoginToken)
?.replaceAll("&lt;", "<")
?.replaceAll("&gt;", ">");
},
ScheduleDate() {
return this.submittedOrder?.schedule?.date;
},
AppointmentType() {
return this.submittedOrder?.serviceLocation?.appointmentType;
},
ScheduleStartTime() {
return this.submittedOrder?.schedule?.startTime;
},
ScheduleEndTime() {
return this.submittedOrder?.schedule?.endTime;
},
InShopWordingText() {
return this.getCmsContent("InShopWordingWidget", "BodyText")
?.replaceAll(
"{custom:submittedOrder.vehicle.year}",
this.submittedOrder?.vehicle?.year
)
?.replaceAll(
"{custom:submittedOrder.vehicle.make}",
this.submittedOrder?.vehicle?.make
)
?.replaceAll(
"{custom:submittedOrder.vehicle.model}",
this.submittedOrder?.vehicle?.model
);
},
MobileWordingText() {
return this.getCmsContent("MobileWordingWidget", "BodyText")
?.replaceAll(
"{custom:submittedOrder.vehicle.year}",
this.submittedOrder?.vehicle?.year
)
?.replaceAll(
"{custom:submittedOrder.vehicle.make}",
this.submittedOrder?.vehicle?.make
)
?.replaceAll(
"{custom:submittedOrder.vehicle.model}",
this.submittedOrder?.vehicle?.model
);
},
DropOffWordingText() {
return this.getCmsContent("DropOffWordingWidget", "BodyText")
?.replaceAll(
"{custom:submittedOrder.vehicle.year}",
this.submittedOrder?.vehicle?.year
)
?.replaceAll(
"{custom:submittedOrder.vehicle.make}",
this.submittedOrder?.vehicle?.make
)
?.replaceAll(
"{custom:submittedOrder.vehicle.model}",
this.submittedOrder?.vehicle?.model
);
},
ServiceLocationAddress() {
return this.submittedOrder?.serviceLocation?.address;
},
ServiceLocationAddress2() {
return this.submittedOrder?.serviceLocation?.address2;
},
ServiceLocationCity() {
return this.submittedOrder?.serviceLocation?.city;
},
ServiceLocationState() {
return this.submittedOrder?.serviceLocation?.state;
},
ServiceLocationZipCode() {
return this.submittedOrder?.serviceLocation?.zipCode;
},
ProviderAddress() {
return this.submittedOrder?.serviceLocation?.provider?.address?.streetAddress;
},
ProviderCity() {
return this.submittedOrder?.serviceLocation?.provider?.address?.city;
},
ProviderState() {
return this.submittedOrder?.serviceLocation?.provider?.address?.state;
},
ProviderZipCode() {
return this.submittedOrder?.serviceLocation?.provider?.address?.zipCode;
},
AppointmentWordingText() {
if (this.AppointmentType == AppointmentTypeStrings.MOBILE) {
return this.MobileWordingText?.replaceAll(
"{custom:ADDRESS}",
this.ServiceLocationFullAddress
);
} else if (this.AppointmentType == AppointmentTypeStrings.DROP_OFF) {
return this.DropOffWordingText?.replaceAll(
"{custom:ADDRESS}",
this.ProviderFullAddress
);
} else {
return this.InShopWordingText?.replaceAll(
"{custom:ADDRESS}",
this.ProviderFullAddress
);
}
},
ServiceLocationFullAddress() {
return `${this.ServiceLocationAddress}${this.ServiceLocationAddress2 ? ", " + this.ServiceLocationAddress2 : ""},<br/> ${this.ServiceLocationCity}, ${this.ServiceLocationState} ${this.ServiceLocationZipCode}`;
},
ProviderFullAddress() {
return `${this.ProviderAddress},<br/> ${this.ProviderCity}, ${this.ProviderState} ${this.ProviderZipCode}`;
},
ScheduleDateFormatted() {
// This conversion ensures we don't get get GMT induced date changes
const dateObject = convertDateStringToDate(this.ScheduleDate);
// Ex: Tuesday, April 22
return dateObject?.toLocaleDateString("en-us", {
weekday: "long",
month: "long",
day: "numeric",
});
},
ScheduleTimeFormatted() {
if (this.AppointmentType == AppointmentTypeStrings.MOBILE) {
return `Between ${get12HourTimeMobileFormat(
this.ScheduleStartTime
)} - ${get12HourTimeMobileFormat(this.ScheduleEndTime)}`;
}
if (this.AppointmentType == AppointmentTypeStrings.DROP_OFF) {
if (
this.submittedOrder?.schedule?.routeCode.includes(
RouteCodeFlags.OVERNIGHT_DROP_OFF
)
) {
return `Drop off before 5:30 PM`;
} else {
return `Drop off before 9:30 AM`;
}
}
return `at ${get12HourTimeFormat(this.ScheduleStartTime)}`;
},
vehicleBannerImageUrl() {
return this.submittedOrder?.vehicle.imageUrl;
},
damageInfo() {
return this.submittedOrder?.damage;
},
isMSRFeeApplicable() {
return this.submittedOrder?.isMSRFeeApplicable;
},
isInsurance() {
return this.submittedOrder?.payment?.isInsurance;
},
isNoComp() {
return this.submittedOrder?.policy?.isNoComp;
},
isItac() {
return this.submittedOrder?.policy?.isItac;
},
hasVapsInCart() {
if (this.lineItems?.vaps?.length > 0) {
return true;
}
return false;
},
showInsuranceCoverageAs() {
if (!this.isInsurance) return null;
if (this.isNoComp || this.isItac) {
return coverageStatus.NOCOMP;
} else {
return this.submittedOrder?.payment?.insuranceCoverage?.coverageStatus;
}
},
currentDeductible() {
return this.submittedOrder?.policy?.currentDeductible;
},
insuranceCompanyName() {
return this.submittedOrder?.policy?.insuranceCompanyName;
},
isPia() {
return this.submittedOrder?.payment?.isPia;
},
AppointmentDuration() {
const durationMaximum = this.submittedOrder?.schedule?.jobMaxMinutes;
const durationMinimum = this.submittedOrder?.schedule?.jobMinMinutes;
const durationLengthString = "Duration: ";
if (
this.submittedOrder?.schedule?.routeCode.includes(RouteCodeFlags.ALL_DAY_DROP_OFF)
) {
return durationLengthString.concat("All Day");
} else if (
this.submittedOrder?.schedule?.routeCode.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)
) {
return durationLengthString.concat("Overnight");
} else {
return durationLengthString.concat(
getDisplayTextForDurationLength(durationMinimum, durationMaximum)
);
}
},
donationValues() {
return [1, 3, 5];
},
lineItemsWithoutDonation() {
const lineItemsClone = deepClone(this.lineItems);
lineItemsClone.supportingItems = lineItemsClone.supportingItems?.filter(
(item) => item.partType !== partTypeStrings.DONATION
);
return lineItemsClone;
},
donationLineItem() {
if (!this.lineItems) return null;
const donationLineItems = this.lineItems.supportingItems?.filter(
(item) => item.partType === partTypeStrings.DONATION
);
let output =
donationLineItems && donationLineItems.length > 0 ? donationLineItems[0] : null;
return output;
},
},
methods: {
arePagePrerequisitesValid() {
if (baseMixin.methods.hasSubmittedOrder()) {
return true;
}
// Service Location
const serviceLocation = store.getters.order.serviceLocation;
const mobileReqs = !!(
serviceLocation.address &&
serviceLocation.city &&
serviceLocation.state &&
serviceLocation.zipCode
);
const providerLocation = serviceLocation.provider.address;
const dropOffInshopReqs = !!(
providerLocation.streetAddress &&
providerLocation.city &&
providerLocation.state &&
providerLocation.zipCode
);
const isMobile = serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE;
const serviceLocationReqs =
(isMobile && mobileReqs) || (!isMobile && dropOffInshopReqs);
// Schedule
const schedule = store.getters.order.schedule;
const scheduleReqs = !!(schedule.date && schedule.startTime && schedule.endTime);
// Customer
const customer = store.getters.order.customer;
const customerReqs = !!customer.emailAddress;
return serviceLocationReqs && scheduleReqs && customerReqs;
},
forwardButtonAction() {
window.location.assign(location.protocol + "//" + location.host);
},
async addDonationToOrder() {
const donationResponse = await baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.SUBMIT_DONATION_PART_TO_SV2,
{
donationAmount: parseInt(this.donationAmount),
submittedOrder: this.submittedOrder,
},
false
);
if (donationResponse?.wasSuccessful) {
this.showDonationSuccess = true;
this.showDonationError = false;
await baseMixin.methods.dispatchStoreAction(
storeActions.ADD_DONATION_TO_SUBMITTED_STATE,
parseInt(this.donationAmount),
false
);
this.lineItems = deepClone(this.getSubmittedOrder().lineItems);
this.$refs.donationBlock.removeLoader();
} else {
this.showDonationError = true;
this.showDonationSuccess = false;
this.donationAmount = 0;
this.$refs.donationBlock.removeLoader();
}
},
pushAnalytics() {
//Push Ecommerce Cart to Data Layer
analyticsMixin.methods.pushECommerceCartToDataLayer();
//Push Product Array to Data Layer
analyticsMixin.methods.pushProductArrayToDataLayer();
//Push Commission Junction Gtm Data To Data Layer
analyticsMixin.methods.pushCommissionJunctionGtmDataToDataLayer();
},
},
components: {
funnelHeader,
Form,
addToCalendar,
cart,
textBlock,
donationBlock,
},
};
</script>
<style lang="scss">
.main {
margin-bottom: 1.5rem;
padding: 1rem 1.5rem 1.5rem;
box-shadow: 0 3px 10px rgb(0 0 0 / 0.2);
border-radius: 5px;
}
.scheduleText {
text-align: center;
}
.scheduleText P {
margin: 0;
font-weight: 400;
font-size: 1.25rem;
color: $black;
+ p {
font-size: 1rem;
margin-top: 0.5rem;
font-weight: 500;
}
}
.header-container {
display: flex;
align-items: center;
justify-content: flex-start;
padding: 1.5rem 0 0.5rem 0;
p {
display: inline-block;
font-weight: 400;
font-size: 1.25rem;
color: $black;
margin: 0;
}
img {
margin-right: 0.5rem;
}
}
.appointment-text {
p {
margin: 0;
}
strong {
color: $black;
}
}
.emailtext {
padding: 0 0 1.5em;
p {
font-size: 0.875rem;
margin: 0;
strong {
color: $black;
}
}
}
</style>