Merge branch 'develop' of https://github.com/Safelite/DigitalConsumer.ISS into feature/digital/SSR-1135.4

This commit is contained in:
Michaela Brydon 2024-03-07 14:47:51 -05:00
commit be022b8feb
13 changed files with 477 additions and 66 deletions

View file

@ -1,5 +1,21 @@
const queryStrings = Object.freeze({ const queryStrings = Object.freeze({
ISS_PAGE: 'issPage' ISS_PAGE: 'issPage',
AUTH_CODE: 'auth_code',
BILL_TO_FIRST_NAME: 'billto_firstname',
BILL_TO_LAST_NAME: 'billto_lastname',
BILL_TO_POSTAL_CODE: 'billto_postalcode',
CARD_EXPIRATION_MONTH: 'card_expirationmonth',
CARD_EXPIRATION_YEAR: 'card_expirationyear',
CARD_TYPE: 'sgcardtype',
DISPLAY_PAY_IN_ADVANCE_ALERT: 'displayPayInAdvanceAlert',
ERROR: 'error',
LAST_FOUR: 'last_four',
REFERENCE_NUMBER: 'req_reference_number',
REFERRAL_SEQ_NUM: 'referralseqnum',
SUBSCRIPTIONID: 'subscriptionid',
TRANS_REFERENCE_NUMBER: 'auth_trans_ref_no',
TRANSACTION_ID: 'transaction_id'
}); });
export default queryStrings; export default queryStrings;

View file

@ -0,0 +1,5 @@
const webStorageConstants = Object.freeze({
SUBMITTED_ORDER: 'submittedOrder'
});
export default webStorageConstants;

View file

@ -1,6 +1,17 @@
import { useMainStore } from '@/store'; import { useMainStore } from '@/store';
import { updateOrCreateISSCookie } from '@/helpers/cookie-helper'; import { updateOrCreateISSCookie } from '@/helpers/cookie-helper';
/*
Encapsulates asynchronous Save Session logic inside a promise to allow for Save Session queuing
*/
async function saveSessionHelper(store) {
const savedSessionInfo = await store.saveSession();
if (savedSessionInfo) {
store.setSaveSessionInfo(savedSessionInfo.data);
}
updateOrCreateISSCookie();
}
/* /*
Will call API to save existing order, or create new one depending where it's called from. Will call API to save existing order, or create new one depending where it's called from.
This will also set Referral information in the store after saving, and then This will also set Referral information in the store after saving, and then
@ -8,8 +19,8 @@ import { updateOrCreateISSCookie } from '@/helpers/cookie-helper';
*/ */
export async function saveSession({ shouldAwaitSaveSessionQueue = false }) { export async function saveSession({ shouldAwaitSaveSessionQueue = false }) {
const store = useMainStore(); const store = useMainStore();
var saveSessionPromise = store.applicationUser.saveSessionPromise const saveSessionPromise = store.applicationUser.saveSessionPromise
? store.applicationUser.saveSessionPromise.then(() => { return saveSessionHelper(store); }) ? store.applicationUser.saveSessionPromise.then(() => saveSessionHelper(store))
: saveSessionHelper(store); : saveSessionHelper(store);
store.setSaveSessionPromise(saveSessionPromise); store.setSaveSessionPromise(saveSessionPromise);
@ -20,12 +31,18 @@ export async function saveSession({ shouldAwaitSaveSessionQueue = false }) {
} }
/* /*
Encapsulates asynchronous Save Session logic inside a promise to allow for Save Session queuing Will determine if to submitWorkOrder.
TODO: Add more description to this
*/ */
async function saveSessionHelper(store) { export async function submitWorkOrder({
const savedSessionInfo = await store.saveSession(); pageNameToLog,
if (savedSessionInfo) { submitAfterSave = false,
store.setSaveSessionInfo(savedSessionInfo.data); createDeleteStatusWorkOrderForPia = false
} }) {
updateOrCreateISSCookie(); await saveSession({
} pageNameToLog,
shouldAwaitSaveSessionQueue: true,
submitAfterSave,
createDeleteStatusWorkOrderForPia
});
}

View file

@ -0,0 +1,11 @@
export default function getQueryStringParameter(key) {
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const lowerCaseParams = new URLSearchParams();
urlParams.forEach((value, name) => {
lowerCaseParams.append(name.toLowerCase(), value);
});
return lowerCaseParams.get(key.toLowerCase());
}

View file

@ -12,7 +12,8 @@ import { createTestingPinia } from '@pinia/testing';
jest.mock('@/helpers/layout-helper.js', () => jest.fn()); jest.mock('@/helpers/layout-helper.js', () => jest.fn());
jest.mock('@/helpers/cms-content-helper', () => ({ jest.mock('@/helpers/cms-content-helper', () => ({
fetchCmsContentForPage: jest.fn() fetchCmsContentForPage: jest.fn(),
processIfStatements: jest.fn()
})); }));
const wordingText = 'wording Text {custom:address}'; const wordingText = 'wording Text {custom:address}';
@ -34,12 +35,18 @@ const headerStub = {
render: () => {} render: () => {}
}; };
const vehicleBannerStub = {
render: () => {}
};
const initialStore = { const initialStore = {
order: { order: {
schedule: { schedule: {
date: '2024-03-01', date: '2024-03-01',
startTime: '09:00', startTime: '09:00',
endTime: '10:00' endTime: '10:00',
jobMinMinutes: 60,
jobMaxMinutes: 90
}, },
serviceLocation: { serviceLocation: {
address: '123 Test Way', address: '123 Test Way',
@ -70,7 +77,8 @@ function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRu
mountOptions.global.stubs = { mountOptions.global.stubs = {
siteFooter: footerStub, siteFooter: footerStub,
siteHeader: headerStub siteHeader: headerStub,
vehicleBanner: vehicleBannerStub
}; };
const testingPinia = createTestingPinia({ const testingPinia = createTestingPinia({
@ -110,6 +118,16 @@ describe('OrderConfirmation.vue', () => {
// Assert // Assert
expect(siteHeader.exists()).toBe(true); expect(siteHeader.exists()).toBe(true);
}); });
test('Should render Vehicle Banner', () => {
// Arrange
const { wrapper } = getMountedComponent(initialStore);
// Act
const vehicleBanner = wrapper.findComponent(vehicleBannerStub);
// Assert
expect(vehicleBanner.exists()).toBe(true);
});
test('If Advanced flow, should display Site Footer', () => { test('If Advanced flow, should display Site Footer', () => {
// Arrange // Arrange
const testStore = { const testStore = {
@ -215,7 +233,7 @@ describe('OrderConfirmation.vue', () => {
endTime: '10:00' endTime: '10:00'
}, },
serviceLocation: { serviceLocation: {
appointmentType: 'Drop Off' appointmentType: 'Dropoff'
} }
} }
}; };
@ -282,7 +300,7 @@ describe('OrderConfirmation.vue', () => {
zipCode: '12345' zipCode: '12345'
} }
}, },
appointmentType: 'Drop Off' appointmentType: 'Dropoff'
} }
} }
}; };
@ -324,5 +342,74 @@ describe('OrderConfirmation.vue', () => {
// Assert // Assert
expect(testValue).toEqual('<br/>123 Safelite Street,<br/> Mesa, AZ 12345<br/>'); expect(testValue).toEqual('<br/>123 Safelite Street,<br/> Mesa, AZ 12345<br/>');
}); });
test('appointmentWordingText2 should return Mobile text in expected format', () => {
// Arrange
const testStore = {
order: {
schedule: {
date: '2019-01-01',
startTime: '09:00',
endTime: '10:00'
},
serviceLocation: {
address: '123 Test Way',
address2: '#1',
city: 'Mesa',
state: 'AZ',
zipCode: '12345',
appointmentType: 'Mobile'
}
}
};
const { wrapper } = getMountedComponent(testStore);
// Act
const testValue = wrapper.vm.appointmentWordingText2;
// Assert
expect(testValue).toEqual(wordingText);
});
test('appointmentWordingText2 should return Drop Off and text in expected format', () => {
// Arrange
const testStore = {
order: {
schedule: {
date: '2019-01-01',
startTime: '09:00',
endTime: '10:00'
},
serviceLocation: {
provider: {
address: {
streetAddress: '123 Safelite Street',
city: 'Mesa',
state: 'AZ',
zipCode: '12345'
}
},
appointmentType: 'Dropoff'
}
}
};
const { wrapper } = getMountedComponent(testStore);
// Act
const testValue = wrapper.vm.appointmentWordingText2;
const expected = wrapper.vm.getBodyText2FromCms('Test Widget');
// Assert
expect(testValue).toEqual(expected);
});
test('appointmentWordingText2 should return In Shop text in expected format', () => {
// Arrange
const { wrapper } = getMountedComponent(initialStore);
// Act
const testValue = wrapper.vm.appointmentWordingText2;
const expected = wrapper.vm.getBodyText2FromCms('Test Widget');
// Assert
expect(testValue).toEqual(expected);
});
}); });
}); });

View file

@ -26,9 +26,13 @@
<p>{{ appointmentTimeFormatted }}</p> <p>{{ appointmentTimeFormatted }}</p>
</div> </div>
<div <div
class="appointment-text text-center text-color--black lh-base" class="appointment-text text-center lh-base"
v-html="appointmentWordingText"> v-html="appointmentWordingText">
</div> </div>
<div
class="appointment-text text-center lh-base mt-2"
v-html="appointmentWordingText2">
</div>
</div> </div>
<siteFooter <siteFooter
v-if="carrierUrl" v-if="carrierUrl"
@ -49,13 +53,15 @@ import siteHeader from '@/iss-components/site-header/site-header.vue';
import vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner.vue'; import vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner.vue';
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 } from '@/helpers/cms-content-helper'; import { fetchCmsContentForPage, processIfStatements } from '@/helpers/cms-content-helper';
import settleAllPromises from '@/helpers/layout-helper'; import settleAllPromises from '@/helpers/layout-helper';
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 { useMainStore } from '@/store'; import { useMainStore } from '@/store';
import { get12HourTimeFormat, get12HourTimeMobileFormat, convertDateStringToDate } from '@/helpers/date-helper.js'; import { get12HourTimeFormat, get12HourTimeMobileFormat, convertDateStringToDate,
getDisplayTextForDurationLength } from '@/helpers/date-helper.js';
import { toTitleCase } from '@/helpers/text-helper.js'; import { toTitleCase } from '@/helpers/text-helper.js';
import { AppointmentTypeStrings } from '@/constants/schedule-constants';
export default { export default {
name: 'order-confirmation', name: 'order-confirmation',
@ -100,7 +106,7 @@ export default {
return this.getCmsContent('OrderConfirmationContent', 'Image'); return this.getCmsContent('OrderConfirmationContent', 'Image');
}, },
appointmentType() { appointmentType() {
return this.mainStore.order.serviceLocation.appointmentType.toUpperCase(); return this.mainStore.order.serviceLocation.appointmentType;
}, },
appointmentDate() { appointmentDate() {
return this.mainStore.order.schedule.date; return this.mainStore.order.schedule.date;
@ -128,9 +134,15 @@ export default {
mobileWordingText() { mobileWordingText() {
return this.getCmsContent('MobileWordingWidget', 'BodyText'); return this.getCmsContent('MobileWordingWidget', 'BodyText');
}, },
mobileWordingText2() {
return this.getCmsContent('MobileWordingWidget', 'BodyText2');
},
dropOffAndInShopWordingText() { dropOffAndInShopWordingText() {
return this.getCmsContent('DropOffAndInShopWordingWidget', 'BodyText'); return this.getCmsContent('DropOffAndInShopWordingWidget', 'BodyText');
}, },
dropOffAndInShopWordingText2() {
return this.getBodyText2FromCms('DropOffAndInShopWordingWidget');
},
serviceLocationAddress() { serviceLocationAddress() {
return this.mainStore.order.serviceLocation.address; return this.mainStore.order.serviceLocation.address;
}, },
@ -167,7 +179,58 @@ export default {
return `<br/>${this.providerAddress},<br/> ${this.providerCity}, ${this.providerState} ${this.providerZipCode}<br/>`; return `<br/>${this.providerAddress},<br/> ${this.providerCity}, ${this.providerState} ${this.providerZipCode}<br/>`;
}, },
appointmentWordingText() { appointmentWordingText() {
return this.formatWordingText(this.appointmentType); switch (this.appointmentType) {
case AppointmentTypeStrings.MOBILE:
case AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP:
return this.mobileWordingText?.replaceAll(
'{custom:address}',
this.serviceLocationFullAddress
);
case AppointmentTypeStrings.DROP_OFF:
return this.dropOffAndInShopWordingText?.replaceAll(
'{custom:address}',
this.providerFullAddress
);
case AppointmentTypeStrings.IN_SHOP:
return this.dropOffAndInShopWordingText?.replaceAll(
'{custom:address}',
this.providerFullAddress
);
default:
return null;
}
},
appointmentWordingText2() {
switch (this.appointmentType) {
case AppointmentTypeStrings.MOBILE:
case AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP:
return this.mobileWordingText2;
case AppointmentTypeStrings.DROP_OFF:
return this.dropOffAndInShopWordingText2;
case AppointmentTypeStrings.IN_SHOP:
return this.dropOffAndInShopWordingText2?.replaceAll(
'{custom:inShopDuration}',
this.inShopAppointmentDuration
);
default:
return null;
}
},
mobileAppointment() {
return this.mainStore.isMobileAppointment;
},
inShopAppointment() {
return this.mainStore.isInShopAppointment;
},
dropOffAppointment() {
return this.mainStore.isDropOffAppointment;
},
inShopAppointmentDuration() {
const inshopDurationTime = getDisplayTextForDurationLength(
this.mainStore.order.schedule.jobMinMinutes,
this.mainStore.order.schedule.jobMaxMinutes
);
return inshopDurationTime;
} }
}, },
mounted() { mounted() {
@ -181,34 +244,29 @@ export default {
}, },
formatAppointmentTime(appointmentType) { formatAppointmentTime(appointmentType) {
switch (appointmentType) { switch (appointmentType) {
case 'MOBILE': case AppointmentTypeStrings.MOBILE:
case AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP:
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
return `Between ${get12HourTimeMobileFormat(this.appointmentStartTime)} - ${get12HourTimeMobileFormat(this.appointmentEndTime)}`; return `Between ${get12HourTimeMobileFormat(this.appointmentStartTime)} - ${get12HourTimeMobileFormat(this.appointmentEndTime)}`;
case 'DROP OFF': case AppointmentTypeStrings.DROP_OFF:
return 'Drop off before 9:30 AM'; return 'Drop off before 9:30 AM';
case 'INSHOP': case AppointmentTypeStrings.IN_SHOP:
return `at ${get12HourTimeFormat(this.appointmentStartTime)}`; return `at ${get12HourTimeFormat(this.appointmentStartTime)}`;
default: default:
return null; return null;
} }
}, },
formatWordingText(appointmentType) { processIfStatements,
switch (appointmentType) { getBodyText2FromCms(cmsWidgetName) {
case 'MOBILE': const body2Text = this.getCmsContent(cmsWidgetName, 'BodyText2');
return this.mobileWordingText?.replaceAll( return this.processIfStatements(body2Text, 'custom', this.getCustomValueFromString);
'{custom:address}', },
this.serviceLocationFullAddress getCustomValueFromString(str) {
); switch (str) {
case 'DROP OFF': case 'inShopAppointment':
return this.dropOffAndInShopWordingText?.replaceAll( return this.inShopAppointment;
'{custom:address}', case 'dropOffAppointment':
this.providerFullAddress return this.dropOffAppointment;
);
case 'INSHOP':
return this.dropOffAndInShopWordingText?.replaceAll(
'{custom:address}',
this.providerFullAddress
);
default: default:
return null; return null;
} }
@ -254,6 +312,7 @@ $page-side-padding: 1.5rem;
.appointment-text { .appointment-text {
:deep(strong) { :deep(strong) {
font-weight: $font-weight-bold; font-weight: $font-weight-bold;
color: $black;
} }
} }
</style> </style>

View file

@ -178,7 +178,8 @@ export default {
&& providerLocation.zipCode && providerLocation.zipCode
); );
const isMobile = serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE; const isMobile = serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE
|| serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP;
const serviceLocationReqs = const serviceLocationReqs =
(isMobile && mobileReqs) || (!isMobile && dropOffInshopReqs); (isMobile && mobileReqs) || (!isMobile && dropOffInshopReqs);

View file

@ -460,15 +460,11 @@ export default {
computed: { computed: {
payInAdvanceResponseUrl() { payInAdvanceResponseUrl() {
const { protocol, host } = window.location; const { protocol, host } = window.location;
return `${protocol}//${host}/?issPage=${ return `${protocol}//${host}/?issPage=${issPageValues.PAYMENT_RETURN}&src=iss-nextgen`;
issPageValues.PAYMENT_RETURN
}&src=iss-nextgen`;
}, },
payInAdvanceCancelUrl() { payInAdvanceCancelUrl() {
const { protocol, host } = window.location; const { protocol, host } = window.location;
return `${protocol}//${host}/?issPage=${ return `${protocol}//${host}/?issPage=${issPageValues.PAYMENT_METHOD}&src=iss-nextgen`;
issPageValues.PAYMENT_METHOD
}&src=iss-nextgen`;
}, },
dynamicCSSUrl() { dynamicCSSUrl() {
const { protocol, hostname, port } = window.location; const { protocol, hostname, port } = window.location;

View file

@ -0,0 +1,143 @@
<template>
<Form
ref="theForm"
@submit="onSubmit"
@invalidSubmit="onInvalidSubmit">
</Form>
</template>
<script>
import { Form } from 'vee-validate';
import BaseFormMixin from '@/mixins/base-form-mixin.js';
import { useMainStore } from '@/store/index.js';
import queryStrings from '@/constants/query-strings';
import getQueryStringParameter from '@/helpers/querystring-helper.js';
import { paymentMethods } from '@/constants/payment-method-constants.js';
import { submitWorkOrder } from '@/helpers/order-helper.js';
import showIssLoadingModal from '@/helpers/loading-modal-helper';
export default {
name: 'payment-return',
components: {
// eslint-disable-next-line vue/no-reserved-component-names
Form
},
mixins: [BaseFormMixin],
computed: {
creditCardToken() {
return {
subscriptionId: getQueryStringParameter(queryStrings.SUBSCRIPTIONID),
expMonth: getQueryStringParameter(queryStrings.CARD_EXPIRATION_MONTH),
expYear: getQueryStringParameter(queryStrings.CARD_EXPIRATION_YEAR),
cardType: getQueryStringParameter(queryStrings.CARD_TYPE),
billToPostalCode: getQueryStringParameter(queryStrings.BILL_TO_POSTAL_CODE),
billToFirstName: getQueryStringParameter(queryStrings.BILL_TO_FIRST_NAME),
billToLastName: getQueryStringParameter(queryStrings.BILL_TO_LAST_NAME),
referenceNumber: getQueryStringParameter(queryStrings.REFERENCE_NUMBER),
authCode: getQueryStringParameter(queryStrings.AUTH_CODE),
transactionId: getQueryStringParameter(queryStrings.TRANSACTION_ID),
transReferenceNumber: getQueryStringParameter(queryStrings.TRANS_REFERENCE_NUMBER),
lastFour: getQueryStringParameter(queryStrings.LAST_FOUR)
};
}
},
async mounted() {
showIssLoadingModal(true);
const payInAdvanceError = getQueryStringParameter(queryStrings.ERROR);
const { payInAdvanceType } = useMainStore().order.payment;
if (payInAdvanceError) {
console.error(`Error during payment: ${payInAdvanceError}`);
const paymentPageNavScenario =
payInAdvanceType === paymentMethods.CREDIT_CARD || payInAdvanceType === paymentMethods.AFTERPAY;
if (paymentPageNavScenario) {
this.$router.navigate(
this.navigationScenarios.PAY_IN_ADVANCE_CREDIT_CARD_ERROR,
this.$route,
{
[queryStrings.DISPLAY_PAY_IN_ADVANCE_ALERT]: payInAdvanceType
}
);
} else {
this.$router.navigate(
this.navigationScenarios.PAY_IN_ADVANCE_ERROR,
this.$route,
{
[queryStrings.DISPLAY_PAY_IN_ADVANCE_ALERT]: true
}
);
}
} else {
switch (payInAdvanceType) {
case paymentMethods.CREDIT_CARD:
case paymentMethods.AFTERPAY:
await this.processCreditCardResponse();
break;
case paymentMethods.PAYPAL:
break;
default:
console.log(`Unknown pay in advance type: ${payInAdvanceType}`);
this.$router.navigate(
this.navigationScenarios.PAY_IN_ADVANCE_ERROR,
this.$route,
{
[queryStrings.DISPLAY_PAY_IN_ADVANCE_ALERT]: true
}
);
}
}
},
methods: {
arePagePrerequisitesValid() {
return true;
},
navigateOnPayInAdvanceError() {
this.$router.navigate(
this.navigationScenarios.PAY_IN_ADVANCE_ERROR,
this.$route,
{
[queryStrings.DISPLAY_PAY_IN_ADVANCE_ALERT]: true
}
);
},
async processPaypalResponse() {
const token = getQueryStringParameter(queryStrings.TOKEN);
this.mainStore.updatePaypalToken(token);
await this.saveAndSubmitWorkOrder();
},
async processCreditCardResponse() {
const referralSeqNum = getQueryStringParameter(queryStrings.REFERRAL_SEQ_NUM);
if (referralSeqNum !== useMainStore().order.referralSequenceNumber) {
console.error(`error: unknown ref:${referralSeqNum} ${useMainStore().order.referralSequenceNumber}`);
this.navigateOnPayInAdvanceError();
} else {
useMainStore().updateCreditCardToken(this.creditCardToken);
await this.saveAndSubmitWorkOrder();
}
},
async saveAndSubmitWorkOrder() {
// Final work order submit after returning from pay in advance.
useMainStore().resetSubmittedOrder();
try {
await submitWorkOrder({
pageNameToLog: 'payment-return',
submitAfterSave: true
});
} catch (error) {
console.error(`error: response from submit work order:${error.message}`);
this.navigateOnPayInAdvanceError();
showIssLoadingModal(false);
return;
}
showIssLoadingModal(false);
useMainStore().createSubmittedOrder();
this.$router.navigate(
this.navigationScenarios.PAY_IN_ADVANCE_SUCCESS,
this.$route
);
}
}
};
</script>

View file

@ -2,7 +2,7 @@
import { createWebHistory, createRouter } from 'vue-router'; import { createWebHistory, createRouter } from 'vue-router';
import lazyLoadComponent from '@/router/dynamic-routing/component-loader'; import lazyLoadComponent from '@/router/dynamic-routing/component-loader';
import issPageValues from '@/router/router-constants/issPage-values'; import issPageValues from '@/router/router-constants/issPage-values';
import { routingTable } from '@/router/router-constants/routing-table'; import routingTable from '@/router/router-constants/routing-table';
import { useMainStore } from '@/store'; import { useMainStore } from '@/store';
import eventBus from '@/helpers/event-bus/event-bus'; import eventBus from '@/helpers/event-bus/event-bus';
import { globalEvents, globalEventTypes } from '@/constants/events'; import { globalEvents, globalEventTypes } from '@/constants/events';
@ -16,11 +16,9 @@ import showIssLoadingModal from '@/helpers/loading-modal-helper';
import analyticsMixin from '@/mixins/analytics-mixin'; import analyticsMixin from '@/mixins/analytics-mixin';
import { saveSession } from '@/helpers/order-helper.js'; import { saveSession } from '@/helpers/order-helper.js';
import routerParams from '@/router/router-constants/router-params'; import routerParams from '@/router/router-constants/router-params';
import bailoutCode from '@/constants/bailoutCode'; import canBailoutNavigateBack from '@/helpers/bailout-helper';
import IssPageValues from '@/router/router-constants/issPage-values'; import bailoutMessage from '@/constants/bailoutMessage';
import navigationScenarios from './router-constants/navigation-scenarios'; import navigationScenarios from './router-constants/navigation-scenarios';
import canBailoutNavigateBack from "@/helpers/bailout-helper";
import bailoutMessage from "@/constants/bailoutMessage";
const routes = [ const routes = [
{ {
@ -129,7 +127,7 @@ router.beforeEach(async (to, from) => {
showIssLoadingModal(true); showIssLoadingModal(true);
} }
const isInIframe = fromQueryPage === IssPageValues.PAYMENT_PAGE; const isInIframe = fromQueryPage === issPageValues.PAYMENT_PAGE;
if (isInIframe) { if (isInIframe) {
// need to set window.top.location.href directly when navigating out of an iframe // need to set window.top.location.href directly when navigating out of an iframe
// especially when navigating with browser buttons // especially when navigating with browser buttons
@ -139,7 +137,7 @@ router.beforeEach(async (to, from) => {
const store = useMainStore(); const store = useMainStore();
// Prevent navigating backwards if we enter a bailout that we are not allowed to go back on // Prevent navigating backwards if we enter a bailout that we are not allowed to go back on
if (store.isBailout && from.name === IssPageValues.BAILOUT_PAGE && to.name !== 'root' && to.name !== IssPageValues.CONTACT_CONFIRMATION if (store.isBailout && from.name === issPageValues.BAILOUT_PAGE && to.name !== 'root' && to.name !== issPageValues.CONTACT_CONFIRMATION
&& !canBailoutNavigateBack()) { && !canBailoutNavigateBack()) {
return false; return false;
} }

View file

@ -96,6 +96,9 @@ const navigationScenarios = Object.freeze({
// Payment // Payment
CLICKED_PAY_NOW: 'CLICKED_PAY_NOW', CLICKED_PAY_NOW: 'CLICKED_PAY_NOW',
PAY_IN_ADVANCE_ERROR: 'PAY_IN_ADVANCE_ERROR',
PAY_IN_ADVANCE_CREDIT_CARD_ERROR: 'PAY_IN_ADVANCE_CREDIT_CARD_ERROR',
PAY_IN_ADVANCE_SUCCESS: 'PAY_IN_ADVANCE_SUCCESS',
// Bailout // Bailout
CLICKED_FORWARD_WITH_BAILOUT: 'CLICKED_FORWARD_WITH_BAILOUT' CLICKED_FORWARD_WITH_BAILOUT: 'CLICKED_FORWARD_WITH_BAILOUT'

View file

@ -641,6 +641,23 @@ const routingTable = () => [
} }
] ]
}, },
{
issPageValue: issPageValues.PAYMENT_RETURN,
maps: [
{
scenario: navigationScenarios.PAY_IN_ADVANCE_ERROR,
destinationIssPageValue: issPageValues.PAYMENT_METHOD
},
{
scenario: navigationScenarios.PAY_IN_ADVANCE_CREDIT_CARD_ERROR,
destinationIssPageValue: issPageValues.PAYMENT_PAGE
},
{
scenario: navigationScenarios.PAY_IN_ADVANCE_SUCCESS,
destinationIssPageValue: issPageValues.CONFIRMATION
}
]
},
{ {
issPageValue: issPageValues.TPA_CONFIRMATION, issPageValue: issPageValues.TPA_CONFIRMATION,
maps: [ maps: [
@ -746,4 +763,4 @@ const routingTable = () => [
]; ];
export { routingTable }; export default routingTable;

View file

@ -14,6 +14,7 @@ import coverageStatuses from '@/constants/coverage-statuses';
import { AppointmentTypeStrings } from '@/constants/schedule-constants'; import { AppointmentTypeStrings } from '@/constants/schedule-constants';
import { convertDateStringToDate, getDateDifferenceInDays, militaryToTwelveHourTime } from '@/helpers/date-helper'; import { convertDateStringToDate, getDateDifferenceInDays, militaryToTwelveHourTime } from '@/helpers/date-helper';
import { paymentMethods } from '@/constants/payment-method-constants'; import { paymentMethods } from '@/constants/payment-method-constants';
import webStorageConstants from '@/constants/web-storage-constants';
import { import {
deductibleForSelectedVehicle, endorsementsForSelectedVehicle, deductibleForSelectedVehicle, endorsementsForSelectedVehicle,
noCoverageForSelectedVehicle, noCoverageForSelectedVehicle,
@ -155,7 +156,22 @@ const getDefaultState = () => ({
}, },
parentAccountNumber: 0, parentAccountNumber: 0,
isPayInAdvance: null, isPayInAdvance: null,
payInAdvanceType: null payInAdvanceType: null,
paypalToken: null,
creditCardToken: {
subscriptionId: null,
expMonth: null,
expYear: null,
cardType: null,
billToPostalCode: null,
billToFirstName: null,
billToLastName: null,
referenceNumber: null,
authCode: null,
transactionId: null,
transReferenceNumber: null,
lastFour: null
}
}, },
contactInfo: { contactInfo: {
firstName: null, firstName: null,
@ -237,6 +253,7 @@ export const useMainStore = defineStore({
isMobileAppointment: (state) => state.order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE isMobileAppointment: (state) => state.order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE
|| state.order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP, || state.order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP,
isDropOffAppointment: (state) => state.order.serviceLocation.appointmentType === AppointmentTypeStrings.DROP_OFF, isDropOffAppointment: (state) => state.order.serviceLocation.appointmentType === AppointmentTypeStrings.DROP_OFF,
isInShopAppointment: (state) => state.order.serviceLocation.appointmentType === AppointmentTypeStrings.IN_SHOP,
isClaimRegistrationRequired: (state) => state.issConfig.isClaimRegistrationRequired, isClaimRegistrationRequired: (state) => state.issConfig.isClaimRegistrationRequired,
isClaimAlreadyRegistered: (state) => state.order.payment.insuranceCoverage.claimNumber !== null, isClaimAlreadyRegistered: (state) => state.order.payment.insuranceCoverage.claimNumber !== null,
isBailout: (state) => state.applicationUser.pageData[issPageValues.BAILOUT_PAGE] != null, isBailout: (state) => state.applicationUser.pageData[issPageValues.BAILOUT_PAGE] != null,
@ -933,7 +950,6 @@ export const useMainStore = defineStore({
}).then((response) => resolve(response), (error) => reject(error)); }).then((response) => resolve(response), (error) => reject(error));
}); });
}, },
getCarrierAccountInfo() { getCarrierAccountInfo() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
globalMethods.callHttpClient({ globalMethods.callHttpClient({
@ -945,12 +961,10 @@ export const useMainStore = defineStore({
}).catch((error) => reject(error)); }).catch((error) => reject(error));
}); });
}, },
async getSupportingItems() { async getSupportingItems() {
const { glassParts } = this.lineItems; const { glassParts } = this.lineItems;
const { carId } = this.vehicle; const { carId } = this.vehicle;
const { isRepair, numberOfChips } = this.damage; const { isRepair, numberOfChips } = this.damage;
const { parentAccountNumber } = this.issConfig;
return globalMethods return globalMethods
.callHttpClient({ .callHttpClient({
@ -959,7 +973,7 @@ export const useMainStore = defineStore({
payload: { payload: {
carId, carId,
damageType: isRepair ? 'Repair' : 'Replace', damageType: isRepair ? 'Repair' : 'Replace',
parentAccountNumber, parentAccountNumber: applicationConfig.CASH_PARENT_ACCOUNT_NUMBER,
parts: glassParts ?? [], parts: glassParts ?? [],
numberOfRepairChips: isRepair ? numberOfChips : 0 numberOfRepairChips: isRepair ? numberOfChips : 0
} }
@ -1199,7 +1213,7 @@ export const useMainStore = defineStore({
submitToMainframe: !!this.order.referralNumber, submitToMainframe: !!this.order.referralNumber,
loadedFromDupeCheck loadedFromDupeCheck
}, },
additionalSuccessEventDataHandler: (response) => additionalSuccessEventDataHandler: () =>
`Email provided: ${customer.emailAddress ? 'true' : 'false'}` `Email provided: ${customer.emailAddress ? 'true' : 'false'}`
}).then((response) => { }).then((response) => {
if (loadedFromDupeCheck) { if (loadedFromDupeCheck) {
@ -1301,7 +1315,23 @@ export const useMainStore = defineStore({
throw ex; throw ex;
} }
}, },
updateCreditCardToken(token) {
this.order.payment.creditCardToken.subscriptionId = token.subscriptionId;
this.order.payment.creditCardToken.expMonth = token.expMonth;
this.order.payment.creditCardToken.expYear = token.expYear;
this.order.payment.creditCardToken.cardType = token.cardType;
this.order.payment.creditCardToken.billToPostalCode = token.billToPostalCode;
this.order.payment.creditCardToken.billToFirstName = token.billToFirstName;
this.order.payment.creditCardToken.billToLastName = token.billToLastName;
this.order.payment.creditCardToken.referenceNumber = token.referenceNumber;
this.order.payment.creditCardToken.authCode = token.authCode;
this.order.payment.creditCardToken.transactionId = token.transactionId;
this.order.payment.creditCardToken.transReferenceNumber = token.transReferenceNumber;
this.order.payment.creditCardToken.lastFour = token.lastFour;
},
updatePaypalToken(token) {
this.order.payment.paypalToken = token;
},
setSaveSessionPromise(promise) { setSaveSessionPromise(promise) {
this.applicationUser.saveSessionPromise = promise; this.applicationUser.saveSessionPromise = promise;
}, },
@ -1383,6 +1413,10 @@ export const useMainStore = defineStore({
this.order.serviceLocation.searchFilter = serviceLocationInfo.searchFilter; this.order.serviceLocation.searchFilter = serviceLocationInfo.searchFilter;
}, },
resetState() {
Object.assign(this, getDefaultState());
},
resetRegistrationState() { resetRegistrationState() {
this.order.vehicle.registration.licensePlate = null; this.order.vehicle.registration.licensePlate = null;
this.order.vehicle.registration.address = null; this.order.vehicle.registration.address = null;
@ -1392,7 +1426,7 @@ export const useMainStore = defineStore({
this.order.vehicle.registration.firstName = null; this.order.vehicle.registration.firstName = null;
this.order.vehicle.registration.lastName = null; this.order.vehicle.registration.lastName = null;
}, },
resetServiceLocationAndDependencies(context) { resetServiceLocationAndDependencies() {
this.resetServiceLocationAppointmentType(); this.resetServiceLocationAppointmentType();
this.resetServiceLocationProvider(); this.resetServiceLocationProvider();
this.resetSchedule(); this.resetSchedule();
@ -2123,7 +2157,6 @@ export const useMainStore = defineStore({
this.resetInsurance(); this.resetInsurance();
this.resetBailout(); this.resetBailout();
}, },
savePaymentMethodChoice(paymentMethod) { savePaymentMethodChoice(paymentMethod) {
const isPayInAdvance = paymentMethod !== paymentMethods.PAY_AT_TIME_OF_SERVICE; const isPayInAdvance = paymentMethod !== paymentMethods.PAY_AT_TIME_OF_SERVICE;
this.order.payment.isPayInAdvance = isPayInAdvance; this.order.payment.isPayInAdvance = isPayInAdvance;
@ -2135,8 +2168,33 @@ export const useMainStore = defineStore({
method: endpoints.GetPaymentSignature.method, method: endpoints.GetPaymentSignature.method,
endpoint: endpoints.GetPaymentSignature.url endpoint: endpoints.GetPaymentSignature.url
}); });
} },
hasSubmittedOrder() {
return window.sessionStorage.getItem(webStorageConstants.SUBMITTED_ORDER) !== null;
},
createSubmittedOrder() {
if (this.hasSubmittedOrder()) {
return;
}
const submittedOrder = this.order;
const { experiments } = this.applicationUser;
// set to local storage
window.sessionStorage.setItem(webStorageConstants.SUBMITTED_ORDER, JSON.stringify(submittedOrder));
// clear vuex
this.resetState();
// restore user's experiments
this.applicationUser.experiments = experiments;
},
resetSubmittedOrder() {
// clear from local storage
window.sessionStorage.removeItem(webStorageConstants.SUBMITTED_ORDER);
}
}, },
persist: true persist: true
}); });