Merge branch 'develop' into feature/kroell/INSR-8094
This commit is contained in:
commit
51c7a68fc9
81 changed files with 1922 additions and 3335 deletions
62
package-lock.json
generated
62
package-lock.json
generated
|
|
@ -6057,7 +6057,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"version": "6.14.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
|
||||
"integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -6088,14 +6090,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/ajv-formats/node_modules/ajv": {
|
||||
"version": "8.11.0",
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
|
||||
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2",
|
||||
"uri-js": "^4.2.2"
|
||||
"require-from-string": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
|
|
@ -8088,14 +8092,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/css-minimizer-webpack-plugin/node_modules/ajv": {
|
||||
"version": "8.11.0",
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
|
||||
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2",
|
||||
"uri-js": "^4.2.2"
|
||||
"require-from-string": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
|
|
@ -15565,14 +15571,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/mini-css-extract-plugin/node_modules/ajv": {
|
||||
"version": "8.11.0",
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
|
||||
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2",
|
||||
"uri-js": "^4.2.2"
|
||||
"require-from-string": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
|
|
@ -19434,7 +19442,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/terser-webpack-plugin/node_modules/ajv": {
|
||||
"version": "8.17.1",
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
|
||||
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -20952,14 +20962,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/webpack-dev-middleware/node_modules/ajv": {
|
||||
"version": "8.11.0",
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
|
||||
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2",
|
||||
"uri-js": "^4.2.2"
|
||||
"require-from-string": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
|
|
@ -21055,14 +21067,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/webpack-dev-server/node_modules/ajv": {
|
||||
"version": "8.11.0",
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
|
||||
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2",
|
||||
"uri-js": "^4.2.2"
|
||||
"require-from-string": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
|
|
@ -21129,7 +21143,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/webpack/node_modules/ajv": {
|
||||
"version": "8.17.1",
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
|
||||
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
|
|||
BIN
src/assets/img/icons/chevron-circle_review_dropdown.png
Normal file
BIN
src/assets/img/icons/chevron-circle_review_dropdown.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
|
|
@ -1,3 +1,4 @@
|
|||
// When adding a new bailout, the Digital Consumer Order Service must also be updated to map the new bailout code to the SV2BailoutCode
|
||||
const bailoutCode = Object.freeze({
|
||||
Unknown: 0,
|
||||
SaveSessionError: 1,
|
||||
|
|
@ -15,7 +16,8 @@ const bailoutCode = Object.freeze({
|
|||
VehicleYMMSLookupError: 13,
|
||||
ApplicationError: 14,
|
||||
ApiError: 15,
|
||||
RouterError: 16
|
||||
RouterError: 16,
|
||||
CoverageCancelledByUser: 17
|
||||
});
|
||||
|
||||
export default bailoutCode;
|
||||
|
|
|
|||
|
|
@ -93,7 +93,11 @@ const bailoutMessage = Object.freeze({
|
|||
code: bailoutCode.VehicleYMMSLookupError,
|
||||
message: getMessage(year, make, model, style)
|
||||
};
|
||||
}
|
||||
},
|
||||
CoverageCancelledByUser: () => ({
|
||||
code: bailoutCode.CoverageCancelledByUser,
|
||||
message: 'User canceled claim on ITAC/No Comp coverage statement.'
|
||||
})
|
||||
});
|
||||
|
||||
export default bailoutMessage;
|
||||
|
|
|
|||
|
|
@ -60,10 +60,11 @@ export const pageProgressMapper = {
|
|||
percent: 85
|
||||
},
|
||||
'payment-method': {
|
||||
percent: 90
|
||||
percent: 95
|
||||
},
|
||||
'payment-page': {
|
||||
percent: 95
|
||||
// No progress bar on this page
|
||||
percent: 0
|
||||
},
|
||||
'tpa-search': {
|
||||
percent: 80
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@
|
|||
:data-bs-toggle="includeSelectIcon ? 'modal' : ''"
|
||||
:data-bs-target="'#' + cmsWidgetName"
|
||||
@change="handleChange"
|
||||
@blur="handleChange"
|
||||
@blur="blurPlus"
|
||||
@focus="$emit('focus', $event.target.value)"
|
||||
@paste="trimOnPaste"
|
||||
@drop="trimOnPaste" />
|
||||
|
|
@ -92,7 +92,7 @@ export default {
|
|||
buttonMain
|
||||
},
|
||||
props: {
|
||||
alwaysEmitFieldErrorOnClick: {
|
||||
alwaysEmitFieldErrorOnEvent: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
|
|
@ -222,6 +222,12 @@ export default {
|
|||
this.$emit('textboxQuestionEvent.inputIdAssigned', this.inputId);
|
||||
},
|
||||
methods: {
|
||||
blurPlus() {
|
||||
this.handleChange(this.value);
|
||||
if (this.alwaysEmitFieldErrorOnEvent) {
|
||||
this.$emit('field-has-error', !this.meta.valid);
|
||||
}
|
||||
},
|
||||
trimOnPaste(evt) {
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
|
|
@ -258,7 +264,7 @@ export default {
|
|||
if (result.valid) {
|
||||
this.handleChange(this.value);
|
||||
this.$emit('click-event');
|
||||
} else if (this.alwaysEmitFieldErrorOnClick) {
|
||||
} else if (this.alwaysEmitFieldErrorOnEvent) {
|
||||
this.$emit('field-has-error', true);
|
||||
}
|
||||
}
|
||||
|
|
@ -297,6 +303,18 @@ input::-webkit-date-and-time-value {
|
|||
}
|
||||
|
||||
.textbox-question {
|
||||
&.has-error {
|
||||
.input-wrapper.has-search-icon {
|
||||
input.form-control[type='text'] {
|
||||
border-color: #db0020;
|
||||
}
|
||||
button[type='submit'] {
|
||||
border-color: #db0020;
|
||||
background-color: #fbe5e9;
|
||||
background-image: url($svg-error-search-icon);
|
||||
}
|
||||
}
|
||||
}
|
||||
label {
|
||||
color: $black;
|
||||
font-weight: 600;
|
||||
|
|
|
|||
|
|
@ -94,10 +94,11 @@ export default {
|
|||
}
|
||||
|
||||
if (error.response.status !== 404) {
|
||||
global.$logger.logError(`${method}: ${endpoint}: ${error.message}`, error.response);
|
||||
const errorData = { method, url, payload, error };
|
||||
global.$logger.logError(`${method}: ${endpoint}`, errorData);
|
||||
if (bailoutOnError && global.bailoutOnAxiosError !== undefined)
|
||||
{
|
||||
global.bailoutOnAxiosError({ url, error });
|
||||
global.bailoutOnAxiosError(errorData);
|
||||
}
|
||||
}
|
||||
return reject(error.response);
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ global.$logger = {
|
|||
function setupMocksForHttpClient({
|
||||
endpoint = null,
|
||||
isError = false,
|
||||
additionalData = null
|
||||
additionalData = null,
|
||||
bailoutOnError = false
|
||||
}) {
|
||||
// Clear node module
|
||||
axios.mockClear();
|
||||
|
|
@ -57,7 +58,8 @@ function setupMocksForHttpClient({
|
|||
|
||||
return {
|
||||
endpoint,
|
||||
logApiCall: true
|
||||
logApiCall: true,
|
||||
bailoutOnError
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -92,3 +94,21 @@ it('Global Methods - Call Http Client - Should Reject Promise', () => {
|
|||
expect(err.status).toEqual(500);
|
||||
});
|
||||
});
|
||||
|
||||
it('Global Methods - Call Http Client - Rejected Promised - bailoutOnError: true - Calls global.bailoutOnAxiosError', () => {
|
||||
// Arrange
|
||||
const endpoint = 'https://mock.safelite.com';
|
||||
const httpArgs = setupMocksForHttpClient({
|
||||
endpoint,
|
||||
isError: true,
|
||||
bailoutOnError: true
|
||||
});
|
||||
analyticsMixIn.methods.pushEventToGA = jest.fn();
|
||||
global.bailoutOnAxiosError = jest.fn();
|
||||
|
||||
// Act
|
||||
globalMethods.callHttpClient(httpArgs).catch((err) => {
|
||||
// Assert
|
||||
expect(global.bailoutOnAxiosError).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
|
@ -19,11 +19,12 @@ export function getLineItems(order) {
|
|||
* @returns {[]} array of service line items
|
||||
*/
|
||||
export function getServiceLineItems(order) {
|
||||
const { supportingItems, glassParts, otherParts } = getLineItems(order);
|
||||
const { supportingItems, glassParts, otherParts, feeItems } = getLineItems(order);
|
||||
return [
|
||||
...(supportingItems ?? []),
|
||||
...(glassParts ?? []),
|
||||
...(otherParts ?? [])
|
||||
...(otherParts ?? []),
|
||||
...(feeItems ?? [])
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -33,10 +34,9 @@ export function getServiceLineItems(order) {
|
|||
* @returns {[]} array of non-service line items
|
||||
*/
|
||||
export function getNonServiceLineItems(order) {
|
||||
const { vaps, feeItems } = getLineItems(order);
|
||||
const { vaps } = getLineItems(order);
|
||||
return [
|
||||
...(vaps ?? []),
|
||||
...(feeItems ?? [])
|
||||
...(vaps ?? [])
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -142,3 +142,18 @@ export function getSalesTax(order) {
|
|||
export function getCartTotal(order) {
|
||||
return Math.round((getSubtotal(order) + getSalesTax(order)) * 100) / 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total price of all recal child parts on the order
|
||||
* @param {object} order order object
|
||||
* @returns {number} total price of all recal child parts on the order
|
||||
*/
|
||||
export function getRecalibrationTotal(order) {
|
||||
const itemsWithRecalibration = getServiceLineItems(order)
|
||||
.filter((item) => item.requiresRecalibration);
|
||||
const recalibrationLineItems = itemsWithRecalibration.map((item) => {
|
||||
return item.childParts?.find((child) => child.partType === partTypeStrings.RECALIBRATION || child.partType === partTypeStrings.ADAS_RECALIBRATION) ?? {};
|
||||
});
|
||||
|
||||
return getPriceOfLineItems(recalibrationLineItems);
|
||||
}
|
||||
|
|
@ -93,10 +93,12 @@ describe('cart-helper', () => {
|
|||
const result = getServiceLineItems(order);
|
||||
|
||||
// Assert
|
||||
expect(result).toHaveLength(3);
|
||||
expect(result).toHaveLength(5);
|
||||
expect(result).toContain(supportingLineItem);
|
||||
expect(result).toContain(glassLineItem);
|
||||
expect(result).toContain(otherLineItem);
|
||||
expect(result).toContain(mobileFeeLineItem);
|
||||
expect(result).toContain(recycleFeeLineItem);
|
||||
});
|
||||
|
||||
test('Returns empty array when line items are null', () => {
|
||||
|
|
@ -124,10 +126,8 @@ describe('cart-helper', () => {
|
|||
const result = getNonServiceLineItems(order);
|
||||
|
||||
// Assert
|
||||
expect(result).toHaveLength(3);
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result).toContain(vapsLineItem);
|
||||
expect(result).toContain(mobileFeeLineItem);
|
||||
expect(result).toContain(recycleFeeLineItem);
|
||||
});
|
||||
|
||||
test('Returns empty array when line items are null', () => {
|
||||
|
|
@ -403,7 +403,7 @@ describe('cart-helper', () => {
|
|||
describe.each([
|
||||
[330, coverageType.ITAC],
|
||||
[330, coverageType.NO_COMP],
|
||||
[310, coverageType.Deductible]])('getSubtotal with line items', (expected, type) => {
|
||||
[160, coverageType.Deductible]])('getSubtotal with line items', (expected, type) => {
|
||||
test(`Returns ${expected} cart subtotal when verified ${getEnumName(coverageType, type)}`, () => {
|
||||
// Arrange
|
||||
const order = {
|
||||
|
|
@ -480,7 +480,7 @@ describe('cart-helper', () => {
|
|||
describe.each([
|
||||
[21, coverageType.ITAC],
|
||||
[21, coverageType.NO_COMP],
|
||||
[15, coverageType.Deductible]])('getSalesTax with line items', (expected, type) => {
|
||||
[4, coverageType.Deductible]])('getSalesTax with line items', (expected, type) => {
|
||||
test(`Returns ${expected} when coverageType is ${getEnumName(coverageType, type)}`, () => {
|
||||
// Arrange
|
||||
const order = {
|
||||
|
|
@ -565,7 +565,7 @@ describe('cart-helper', () => {
|
|||
describe.each([
|
||||
[351, coverageType.ITAC],
|
||||
[351, coverageType.NO_COMP],
|
||||
[325, coverageType.Deductible]
|
||||
[164, coverageType.Deductible]
|
||||
])('getCartTotal items', (expected, type) => {
|
||||
test(`Returns ${expected} cart total when verified ${getEnumName(coverageType, type)}`, () => {
|
||||
// Arrange
|
||||
|
|
|
|||
|
|
@ -32,9 +32,16 @@ export class Logger {
|
|||
}
|
||||
|
||||
formatLogEntry(message, details) {
|
||||
return `Application: ${applicationConfig.APPLICATION_NAME}\n${message}\n${
|
||||
details ? JSON.stringify(details, undefined, 2) : ''
|
||||
}`;
|
||||
let json = '';
|
||||
if (details) {
|
||||
if (details instanceof Error) {
|
||||
json = JSON.stringify(details, Object.getOwnPropertyNames(details), 2);
|
||||
} else {
|
||||
json = JSON.stringify(details, undefined, 2)
|
||||
}
|
||||
}
|
||||
|
||||
return `Application: ${applicationConfig.APPLICATION_NAME} \n${message} \n${json}`;
|
||||
}
|
||||
|
||||
writeLogEntry(endpoint, logEntry) {
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import submitType from '@/constants/submit-type';
|
|||
/*
|
||||
Encapsulates asynchronous Save Session logic inside a promise to allow for Save Session queuing
|
||||
*/
|
||||
async function saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA }) {
|
||||
const savedSessionInfo = await store.saveSession({ submitAfterSave, createWorkOrderNumberForPIA });
|
||||
async function saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA, bailoutOnError }) {
|
||||
const savedSessionInfo = await store.saveSession({ submitAfterSave, createWorkOrderNumberForPIA, bailoutOnError });
|
||||
if (savedSessionInfo) {
|
||||
store.setSaveSessionInfo(savedSessionInfo.data);
|
||||
}
|
||||
|
|
@ -16,11 +16,11 @@ async function saveSessionHelper(store, { submitAfterSave, createWorkOrderNumber
|
|||
This will also set Referral information in the store after saving, and then
|
||||
update the cookie. To force synchronous behavior pass in 'true' for shouldAwaitSaveSessionQueue
|
||||
*/
|
||||
export async function saveSession({ shouldAwaitSaveSessionQueue = false, submitAfterSave = false, createWorkOrderNumberForPIA = false }) {
|
||||
export async function saveSession({ shouldAwaitSaveSessionQueue = false, submitAfterSave = false, createWorkOrderNumberForPIA = false, bailoutOnError = false }) {
|
||||
const store = useMainStore();
|
||||
const saveSessionPromise = store.applicationUser.saveSessionPromise
|
||||
? store.applicationUser.saveSessionPromise.then(() => saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA }))
|
||||
: saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA });
|
||||
? store.applicationUser.saveSessionPromise.then(() => saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA, bailoutOnError }))
|
||||
: saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA, bailoutOnError });
|
||||
|
||||
store.setSaveSessionPromise(saveSessionPromise);
|
||||
|
||||
|
|
@ -38,7 +38,8 @@ export async function submitWorkOrder({ submitType }) {
|
|||
store.resetSubmittedOrder();
|
||||
await saveSession({
|
||||
shouldAwaitSaveSessionQueue: true,
|
||||
submitAfterSave: true
|
||||
submitAfterSave: true,
|
||||
bailoutOnError: true
|
||||
});
|
||||
await store.createSubmittedOrder(submitType);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import { useMainStore } from '@/store';
|
||||
import { AppointmentTypeStrings } from '@/constants/schedule-constants';
|
||||
import settleAllPromises from '@/helpers/layout-helper';
|
||||
|
||||
function processZipCodeResults(request) {
|
||||
return request.then((serviceZipValidationResponse) => ({
|
||||
|
|
@ -29,7 +31,7 @@ export async function getPricedMobileFeePart(serviceZipCode) {
|
|||
// Get the Mobile Fee Part
|
||||
const mobileFeePart = await useMainStore().getMobileFeePart();
|
||||
|
||||
if (mobileFeePart.data == null || mobileFeePart.data === '') {
|
||||
if (mobileFeePart?.data == null || mobileFeePart.data === '') {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
|
|
@ -40,17 +42,6 @@ export async function getPricedMobileFeePart(serviceZipCode) {
|
|||
return Promise.resolve(pricingResults[0]);
|
||||
}
|
||||
|
||||
export async function getServiceabilityDetails(serviceZipCode, lineItems) {
|
||||
const serviceabilityDetails = await useMainStore().getServiceabilityDetails(
|
||||
{
|
||||
serviceZipCode,
|
||||
lineItems
|
||||
},
|
||||
false
|
||||
);
|
||||
return Promise.resolve(serviceabilityDetails);
|
||||
}
|
||||
|
||||
export async function getAvailabilityRating(
|
||||
startDate,
|
||||
endDate,
|
||||
|
|
@ -69,3 +60,45 @@ export async function getAvailabilityRating(
|
|||
|
||||
return Promise.resolve(shopStatus);
|
||||
}
|
||||
|
||||
export function getProviderData() {
|
||||
const mainStore = useMainStore();
|
||||
const storeServiceLocation = mainStore.order.serviceLocation;
|
||||
const storeSelectedAppointmentType = storeServiceLocation?.appointmentType;
|
||||
const isMobileAppointment = storeSelectedAppointmentType === AppointmentTypeStrings.MOBILE
|
||||
|| storeSelectedAppointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP;
|
||||
const storeSelectedProvider = storeServiceLocation?.provider;
|
||||
const serviceZipCode = storeServiceLocation?.zipCode || mainStore.order.customer.address.zipCode;
|
||||
|
||||
return { storeServiceLocation, isMobileAppointment, storeSelectedProvider, serviceZipCode }
|
||||
}
|
||||
|
||||
export async function onProviderChanged() {
|
||||
const { isMobileAppointment, storeSelectedProvider, serviceZipCode } = getProviderData();
|
||||
if (!storeSelectedProvider) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mainStore = useMainStore();
|
||||
|
||||
await mainStore.getBillToInfo(storeSelectedProvider.providerNumber);
|
||||
const promiseResultMap = [
|
||||
{
|
||||
resultKey: 'glassFees',
|
||||
promise: mainStore.getGlassFees()
|
||||
.then(async (glassFees) => await mainStore.getCombinedQuote(glassFees.data))
|
||||
.then(async (quotedGlassFees) => mainStore.updateGlassFees(quotedGlassFees))
|
||||
},
|
||||
];
|
||||
|
||||
if (isMobileAppointment && (mainStore.isNoComp || mainStore.isITAC)) {
|
||||
promiseResultMap.push({
|
||||
resultKey: 'priceMobileFee',
|
||||
promise: getPricedMobileFeePart(serviceZipCode).then(mobileFee => mainStore.updateMobileFee(mobileFee))
|
||||
});
|
||||
} else {
|
||||
mainStore.updateMobileFee(null);
|
||||
}
|
||||
|
||||
await settleAllPromises(promiseResultMap);
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
exports[`cart-dropdown component initial data rendered as expected 1`] = `
|
||||
Object {
|
||||
"RECYCLING_MODAL_REF_NAME": "RecycleModal",
|
||||
"cartItemType": Object {
|
||||
"MOBILE_FEE": "MOBILE FEE",
|
||||
"RECYCLE_FEE": "RECYCLE FEE",
|
||||
|
|
@ -16,6 +15,7 @@ Object {
|
|||
"deductible": "DeductibleWidget",
|
||||
"guaranteeText": "GuaranteeCartItemTextWidget",
|
||||
"mobileFee": "MobileServiceWidget",
|
||||
"recalibration": "RecalibrationWidget",
|
||||
"recycleFee": "RecycleFeeWidget",
|
||||
"salesTax": "SalesTaxWidget",
|
||||
"servicePackage": "ServicePackageTitle",
|
||||
|
|
|
|||
|
|
@ -73,10 +73,22 @@ describe('cart-dropdown component', () => {
|
|||
expect(wrapper.vm.$data).toMatchSnapshot();
|
||||
});
|
||||
describe('displays', () => {
|
||||
test('cart table', () => {
|
||||
test('cart table when not deductible only', () => {
|
||||
// Arrange
|
||||
const reference = '.cart-table';
|
||||
const { wrapper } = getMountedComponent({}, {}, {});
|
||||
const part1 = { partType: partTypeStrings.FRONT_WIPER };
|
||||
const storeData = {
|
||||
order: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.VERIFIED,
|
||||
coverageType: coverageType.Deductible
|
||||
},
|
||||
lineItems: {
|
||||
vaps: [part1],
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData, {}, {});
|
||||
|
||||
// Act
|
||||
const cartTable = wrapper.find(reference);
|
||||
|
|
@ -84,9 +96,9 @@ describe('cart-dropdown component', () => {
|
|||
// Assert
|
||||
expect(cartTable.exists()).toBeTruthy();
|
||||
});
|
||||
test('cart deductible when showDeductibleCartItem is true', () => {
|
||||
test('deductible-box when deductible only', () => {
|
||||
// Arrange
|
||||
const reference = '#deductible-label';
|
||||
const reference = '.deductible-box';
|
||||
const storeData = {
|
||||
order: {
|
||||
insuranceCoverage: {
|
||||
|
|
@ -127,9 +139,9 @@ describe('cart-dropdown component', () => {
|
|||
// Assert
|
||||
expect(cartBasePrice.exists()).toBeTruthy();
|
||||
});
|
||||
test('non service package cart items', () => {
|
||||
test('additional cart items', () => {
|
||||
// Arrange
|
||||
const reference = '.non-packaged.cart-item';
|
||||
const reference = '.cart-item';
|
||||
const part1 = { partType: 'apple' };
|
||||
const part2 = { partType: 'banana' };
|
||||
const part3 = { partType: 'orange' };
|
||||
|
|
@ -155,38 +167,27 @@ describe('cart-dropdown component', () => {
|
|||
const { wrapper } = getMountedComponent(storeData, {}, {});
|
||||
|
||||
// Act
|
||||
const nonPackagedCartItems = wrapper.find(reference);
|
||||
const cartItems = wrapper.findAll(reference);
|
||||
|
||||
// Assert
|
||||
expect(nonPackagedCartItems.exists()).toBeTruthy();
|
||||
expect(cartItems.length).toBeGreaterThan(1);
|
||||
});
|
||||
test('recycle cart item label when isRecycle true for some cart item', () => {
|
||||
test('cart footer when not deductible only', () => {
|
||||
// Arrange
|
||||
const reference = '#recycle-fee-label';
|
||||
const isExpanded = true;
|
||||
const initialPropsData = { isExpanded };
|
||||
const reference = '.cart-footer';
|
||||
const part1 = { partType: partTypeStrings.FRONT_WIPER };
|
||||
const storeData = {
|
||||
order: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.VERIFIED,
|
||||
coverageType: coverageType.Deductible
|
||||
},
|
||||
lineItems: {
|
||||
feeItems: [
|
||||
{ partNumber: partNumberStrings.RECYCLE_FEE }
|
||||
]
|
||||
vaps: [part1],
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData, {}, initialPropsData);
|
||||
|
||||
// Act
|
||||
const recycleFeeLabel = wrapper.find(reference);
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.recycleFeeCartItem).not.toBeNull();
|
||||
expect(recycleFeeLabel.exists()).toBeTruthy();
|
||||
});
|
||||
test('cart footer', () => {
|
||||
// Arrange
|
||||
const reference = '#cart-footer';
|
||||
const { wrapper } = getMountedComponent({}, {}, {});
|
||||
const { wrapper } = getMountedComponent(storeData, {}, {});
|
||||
|
||||
// Act
|
||||
const cartFooter = wrapper.find(reference);
|
||||
|
|
@ -197,7 +198,19 @@ describe('cart-dropdown component', () => {
|
|||
test('subtotal', () => {
|
||||
// Arrange
|
||||
const reference = '#cart-subtotal';
|
||||
const { wrapper } = getMountedComponent({}, {}, {});
|
||||
const part1 = { partType: partTypeStrings.FRONT_WIPER };
|
||||
const storeData = {
|
||||
order: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.VERIFIED,
|
||||
coverageType: coverageType.Deductible
|
||||
},
|
||||
lineItems: {
|
||||
vaps: [part1],
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData, {}, {});
|
||||
|
||||
// Act
|
||||
const subtotal = wrapper.find(reference);
|
||||
|
|
@ -208,7 +221,19 @@ describe('cart-dropdown component', () => {
|
|||
test('tax', () => {
|
||||
// Arrange
|
||||
const reference = '#cart-sales-tax';
|
||||
const { wrapper } = getMountedComponent({}, {}, {});
|
||||
const part1 = { partType: partTypeStrings.FRONT_WIPER };
|
||||
const storeData = {
|
||||
order: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.VERIFIED,
|
||||
coverageType: coverageType.Deductible
|
||||
},
|
||||
lineItems: {
|
||||
vaps: [part1],
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData, {}, {});
|
||||
|
||||
// Act
|
||||
const salesTax = wrapper.find(reference);
|
||||
|
|
@ -222,8 +247,16 @@ describe('cart-dropdown component', () => {
|
|||
const isExpanded = true;
|
||||
const showAsPaid = true;
|
||||
const initialPropsData = { isExpanded, showAsPaid };
|
||||
const part1 = { partType: partTypeStrings.FRONT_WIPER };
|
||||
const storeData = {
|
||||
order: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.VERIFIED,
|
||||
coverageType: coverageType.Deductible
|
||||
},
|
||||
lineItems: {
|
||||
vaps: [part1],
|
||||
},
|
||||
payment: {
|
||||
isPayInAdvance: true
|
||||
}
|
||||
|
|
@ -240,7 +273,19 @@ describe('cart-dropdown component', () => {
|
|||
test('bottom amount due', () => {
|
||||
// Arrange
|
||||
const reference = '#bottom-amount-due';
|
||||
const { wrapper } = getMountedComponent({}, {}, {});
|
||||
const part1 = { partType: partTypeStrings.FRONT_WIPER };
|
||||
const storeData = {
|
||||
order: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.VERIFIED,
|
||||
coverageType: coverageType.Deductible
|
||||
},
|
||||
lineItems: {
|
||||
vaps: [part1],
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData, {}, {});
|
||||
|
||||
// Act
|
||||
const bottomAmountDue = wrapper.find(reference);
|
||||
|
|
@ -293,27 +338,6 @@ describe('cart-dropdown component', () => {
|
|||
// Assert
|
||||
expect(cartBasePrice.exists()).toBeFalsy();
|
||||
});
|
||||
test('recycle cart item label when isRecycle false for all cart items', () => {
|
||||
// Arrange
|
||||
const reference = '#recycle-fee-label';
|
||||
const isExpanded = true;
|
||||
const initialPropsData = { isExpanded };
|
||||
const storeData = {
|
||||
order: {
|
||||
lineItems: {
|
||||
supportingItems: []
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData, {}, initialPropsData);
|
||||
|
||||
// Act
|
||||
const recycleFeeLabel = wrapper.find(reference);
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.recycleFeeCartItem).toBeNull();
|
||||
expect(recycleFeeLabel.exists()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
describe('computed', () => {
|
||||
describe('amountDue', () => {
|
||||
|
|
@ -475,7 +499,7 @@ describe('cart-dropdown component', () => {
|
|||
expect(result.sort(sortByPartType)).toStrictEqual(expected.sort(sortByPartType));
|
||||
});
|
||||
});
|
||||
describe('servicePackageCartItems', () => {
|
||||
describe('cartItems', () => {
|
||||
test.each([
|
||||
[undefined], [null], [[]]
|
||||
])('returns empty list when package contents %p', (packageContents) => {
|
||||
|
|
@ -485,7 +509,7 @@ describe('cart-dropdown component', () => {
|
|||
const expected = [];
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.servicePackageCartItems;
|
||||
const result = wrapper.vm.cartItems;
|
||||
|
||||
// Assert
|
||||
expect(result).toStrictEqual(expected);
|
||||
|
|
@ -531,7 +555,7 @@ describe('cart-dropdown component', () => {
|
|||
const wrapper = shallowMount(cartDropdown, mountOptions);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.servicePackageCartItems;
|
||||
const result = wrapper.vm.cartItems;
|
||||
|
||||
// Assert
|
||||
expect(result.length).toBe(1);
|
||||
|
|
@ -577,7 +601,7 @@ describe('cart-dropdown component', () => {
|
|||
const wrapper = shallowMount(cartDropdown, mountOptions);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.servicePackageCartItems;
|
||||
const result = wrapper.vm.cartItems;
|
||||
|
||||
// Assert
|
||||
expect(result.length).toBe(1);
|
||||
|
|
@ -623,7 +647,7 @@ describe('cart-dropdown component', () => {
|
|||
const wrapper = shallowMount(cartDropdown, mountOptions);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.servicePackageCartItems;
|
||||
const result = wrapper.vm.cartItems;
|
||||
|
||||
// Assert
|
||||
expect(result.length).toBe(1);
|
||||
|
|
@ -685,7 +709,7 @@ describe('cart-dropdown component', () => {
|
|||
const wrapper = shallowMount(cartDropdown, mountOptions);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.servicePackageCartItems;
|
||||
const result = wrapper.vm.cartItems;
|
||||
|
||||
// Assert
|
||||
expect(result.length).toBe(3);
|
||||
|
|
@ -699,8 +723,6 @@ describe('cart-dropdown component', () => {
|
|||
name: expectedRainDefenseName
|
||||
}));
|
||||
});
|
||||
});
|
||||
describe('nonServicePackageCartItems', () => {
|
||||
test('when recycleFee, includes recycle fee', () => {
|
||||
// Arrange
|
||||
const storeData = {
|
||||
|
|
@ -717,7 +739,9 @@ describe('cart-dropdown component', () => {
|
|||
const { wrapper } = getMountedComponent(storeData);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.nonServicePackageCartItems;
|
||||
const result = wrapper.vm.cartItems;
|
||||
|
||||
console.log(result);
|
||||
|
||||
// Assert
|
||||
expect(result).toContainEqual(expect.objectContaining({
|
||||
|
|
@ -736,163 +760,13 @@ describe('cart-dropdown component', () => {
|
|||
const { wrapper } = getMountedComponent(storeData);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.nonServicePackageCartItems;
|
||||
const result = wrapper.vm.cartItems;
|
||||
|
||||
// Assert
|
||||
expect(result).toContainEqual(expect.objectContaining({
|
||||
partType: partTypeStrings.MOBILE_FEE
|
||||
}));
|
||||
});
|
||||
test('front wiper not included when front wiper in service package', () => {
|
||||
// Arrange
|
||||
getPriceOfLineItems.mockImplementation(() => 123);
|
||||
getPackageContents.mockImplementationOnce(() => [partTypeStrings.FRONT_WIPER]);
|
||||
const storeData = {
|
||||
order: {
|
||||
lineItems: {
|
||||
vaps: [
|
||||
{
|
||||
partType: partTypeStrings.FRONT_WIPER
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.nonServicePackageCartItems;
|
||||
|
||||
// Assert
|
||||
expect(result).not.toContainEqual(expect.objectContaining({
|
||||
partType: partTypeStrings.FRONT_WIPER
|
||||
}));
|
||||
});
|
||||
test('rear wiper not included when rear wiper in service package', () => {
|
||||
// Arrange
|
||||
getPriceOfLineItems.mockImplementation(() => 123);
|
||||
getPackageContents.mockImplementationOnce(() => [partTypeStrings.REAR_WIPER]);
|
||||
const storeData = {
|
||||
order: {
|
||||
lineItems: {
|
||||
vaps: [
|
||||
{
|
||||
partType: partTypeStrings.REAR_WIPER
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.nonServicePackageCartItems;
|
||||
|
||||
// Assert
|
||||
expect(result).not.toContainEqual(expect.objectContaining({
|
||||
partType: partTypeStrings.REAR_WIPER
|
||||
}));
|
||||
});
|
||||
test('rain defense not included when rain defense in service package', () => {
|
||||
// Arrange
|
||||
getPriceOfLineItems.mockImplementation(() => 123);
|
||||
getPackageContents.mockImplementationOnce(() => [partTypeStrings.RAIN_DEFENSE]);
|
||||
const storeData = {
|
||||
order: {
|
||||
lineItems: {
|
||||
vaps: [
|
||||
{
|
||||
partType: partTypeStrings.RAIN_DEFENSE
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.nonServicePackageCartItems;
|
||||
|
||||
// Assert
|
||||
expect(result).not.toContainEqual(expect.objectContaining({
|
||||
partType: partTypeStrings.RAIN_DEFENSE
|
||||
}));
|
||||
});
|
||||
test('front wiper included when not in service package and in vaps', () => {
|
||||
// Arrange
|
||||
getPriceOfLineItems.mockImplementation(() => 123);
|
||||
getPackageContents.mockImplementationOnce(() => []);
|
||||
const storeData = {
|
||||
order: {
|
||||
lineItems: {
|
||||
vaps: [
|
||||
{
|
||||
partType: partTypeStrings.FRONT_WIPER
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.nonServicePackageCartItems;
|
||||
|
||||
// Assert
|
||||
expect(result).toContainEqual(expect.objectContaining({
|
||||
partType: partTypeStrings.FRONT_WIPER
|
||||
}));
|
||||
});
|
||||
test('rear wiper included when not in service package and in vaps', () => {
|
||||
// Arrange
|
||||
getPriceOfLineItems.mockImplementation(() => 123);
|
||||
getPackageContents.mockImplementationOnce(() => []);
|
||||
const storeData = {
|
||||
order: {
|
||||
lineItems: {
|
||||
vaps: [
|
||||
{
|
||||
partType: partTypeStrings.REAR_WIPER
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.nonServicePackageCartItems;
|
||||
|
||||
// Assert
|
||||
expect(result).toContainEqual(expect.objectContaining({
|
||||
partType: partTypeStrings.REAR_WIPER
|
||||
}));
|
||||
});
|
||||
test('rain defense included when not in service package and in vaps', () => {
|
||||
// Arrange
|
||||
getPriceOfLineItems.mockImplementation(() => 123);
|
||||
getPackageContents.mockImplementationOnce(() => []);
|
||||
const storeData = {
|
||||
order: {
|
||||
lineItems: {
|
||||
vaps: [
|
||||
{
|
||||
partType: partTypeStrings.RAIN_DEFENSE
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.nonServicePackageCartItems;
|
||||
|
||||
// Assert
|
||||
expect(result).toContainEqual(expect.objectContaining({
|
||||
partType: partTypeStrings.RAIN_DEFENSE
|
||||
}));
|
||||
});
|
||||
test('item in vaps outside front wiper, rear wiper, and rain defense never included', () => {
|
||||
// Arrange
|
||||
getPriceOfLineItems.mockImplementation(() => 123);
|
||||
|
|
@ -911,143 +785,13 @@ describe('cart-dropdown component', () => {
|
|||
const { wrapper } = getMountedComponent(storeData);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.nonServicePackageCartItems;
|
||||
const result = wrapper.vm.cartItems;
|
||||
|
||||
// Assert
|
||||
expect(result).not.toContainEqual(expect.objectContaining({
|
||||
partType: partTypeStrings.RECALIBRATION
|
||||
}));
|
||||
});
|
||||
test('returns expected when multiple variables apply', () => {
|
||||
// Arrange
|
||||
getPriceOfLineItems.mockImplementation(() => 123);
|
||||
getPackageContents.mockImplementationOnce(() => [partTypeStrings.FRONT_WIPER]);
|
||||
const storeData = {
|
||||
order: {
|
||||
lineItems: {
|
||||
feeItems: [
|
||||
{ partType: partTypeStrings.MOBILE_FEE },
|
||||
{ partNumber: partNumberStrings.RECYCLE_FEE }
|
||||
],
|
||||
vaps: [
|
||||
{ partType: partTypeStrings.RECALIBRATION },
|
||||
{ partType: partTypeStrings.FRONT_WIPER },
|
||||
{ partType: partTypeStrings.REAR_WIPER }
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.nonServicePackageCartItems;
|
||||
|
||||
// Assert
|
||||
expect(result.length).toBe(3);
|
||||
expect(result).toContainEqual(expect.objectContaining({
|
||||
partType: partTypeStrings.REPLACE_FEE
|
||||
}));
|
||||
expect(result).toContainEqual(expect.objectContaining({
|
||||
partType: partTypeStrings.MOBILE_FEE
|
||||
}));
|
||||
expect(result).toContainEqual(expect.objectContaining({
|
||||
partType: partTypeStrings.REAR_WIPER
|
||||
}));
|
||||
});
|
||||
});
|
||||
describe('servicePackageLabelWidget', () => {
|
||||
test.each([
|
||||
[null],
|
||||
[undefined]
|
||||
])('returns "" when servicePackageNames %p', (servicePackageNames) => {
|
||||
// Arrange
|
||||
const mountOptions = getMountOptions({
|
||||
router: { navigate: jest.fn() }
|
||||
});
|
||||
const mockMixin = {
|
||||
methods: {
|
||||
getCmsContent: jest.fn(() => servicePackageNames)
|
||||
}
|
||||
};
|
||||
mountOptions.mixins = [mockMixin];
|
||||
|
||||
const wrapper = shallowMount(cartDropdown, mountOptions);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.servicePackageLabelWidget;
|
||||
|
||||
// Assert
|
||||
expect(result).toBe('');
|
||||
});
|
||||
test('returns "" when servicePackageTier not found in servicePackageNames', () => {
|
||||
// Arrange
|
||||
const mountOptions = getMountOptions({
|
||||
router: { navigate: jest.fn() }
|
||||
});
|
||||
getHighestFullySatisfiedTier.mockImplementationOnce(() => 'some other tier');
|
||||
const servicePackageNames = [{ Name: 'some tier' }];
|
||||
const mockMixin = {
|
||||
methods: {
|
||||
getCmsContent: jest.fn(() => servicePackageNames)
|
||||
}
|
||||
};
|
||||
mountOptions.mixins = [mockMixin];
|
||||
const wrapper = shallowMount(cartDropdown, mountOptions);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.servicePackageLabelWidget;
|
||||
|
||||
// Assert
|
||||
expect(result).toBe('');
|
||||
});
|
||||
test('returns "" when servicePackageTier has no SubWidgetName', () => {
|
||||
// Arrange
|
||||
const mountOptions = getMountOptions({
|
||||
router: { navigate: jest.fn() }
|
||||
});
|
||||
const tier = 'tier1';
|
||||
getHighestFullySatisfiedTier.mockImplementationOnce(() => tier);
|
||||
const servicePackageNames = [{ Name: tier }];
|
||||
const mockMixin = {
|
||||
methods: {
|
||||
getCmsContent: jest.fn(() => servicePackageNames)
|
||||
}
|
||||
};
|
||||
mountOptions.mixins = [mockMixin];
|
||||
const wrapper = shallowMount(cartDropdown, mountOptions);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.servicePackageLabelWidget;
|
||||
|
||||
// Assert
|
||||
expect(result).toBe('');
|
||||
});
|
||||
test('returns expected when servicePackageTier has SubWidgetName', () => {
|
||||
// Arrange
|
||||
const mountOptions = getMountOptions({
|
||||
router: { navigate: jest.fn() }
|
||||
});
|
||||
const tier = 'tier1';
|
||||
getHighestFullySatisfiedTier.mockImplementationOnce(() => tier);
|
||||
const expected = 'name of sub widget';
|
||||
const servicePackageNames = [{
|
||||
Name: tier,
|
||||
SubWidgetName: expected
|
||||
}];
|
||||
const mockMixin = {
|
||||
methods: {
|
||||
getCmsContent: jest.fn(() => servicePackageNames)
|
||||
}
|
||||
};
|
||||
mountOptions.mixins = [mockMixin];
|
||||
const wrapper = shallowMount(cartDropdown, mountOptions);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.servicePackageLabelWidget;
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(expected);
|
||||
});
|
||||
});
|
||||
describe('recycleFeeCartItem', () => {
|
||||
test.each([
|
||||
|
|
|
|||
|
|
@ -1,39 +1,39 @@
|
|||
<template>
|
||||
<div class="cart-table">
|
||||
<div
|
||||
v-if="!isDeductibleOnly"
|
||||
class="cart-table">
|
||||
<div class="cart-item-list">
|
||||
<div
|
||||
id="cart-deductible-or-base-price"
|
||||
v-if="showDeductibleCartItem"
|
||||
class="cart-item">
|
||||
<div class="price-row">
|
||||
<template
|
||||
v-if="showDeductibleCartItem">
|
||||
<span id="deductible-label">{{ deductibleLabel }}</span>
|
||||
<span id="deductible-value">{{ getDisplayed(deductible) }}</span>
|
||||
</template>
|
||||
<template
|
||||
v-else>
|
||||
<span id="base-price-label">{{ basePriceLabel }}</span>
|
||||
<span id="base-price-value">{{ getDisplayed(servicePrice) }}</span>
|
||||
</template>
|
||||
<span id="deductible-label">{{ deductibleLabel }}</span>
|
||||
<span id="deductible-value">{{ getDisplayed(deductible) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="cart-item">
|
||||
<div class="price-row">
|
||||
<span id="base-price-label">{{ basePriceLabel }}</span>
|
||||
<span id="base-price-value">{{ getDisplayed(servicePrice) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="showRecalibrationCartItem"
|
||||
class="cart-item">
|
||||
<div class="price-row">
|
||||
<span id="recalibration-label">{{ recalibrationLabel }}</span>
|
||||
<span id="recalibration-value">{{ formatAmountInDollars(recalibrationPrice) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- NonPackaged Cart Items -->
|
||||
<div
|
||||
v-for="(item, i) in nonServicePackageCartItems"
|
||||
v-for="(item, i) in cartItems"
|
||||
:key="i"
|
||||
class="cart-item non-packaged">
|
||||
class="cart-item packaged">
|
||||
<div class="price-row">
|
||||
<span
|
||||
v-if="item.cartItemType === cartItemType.RECYCLE_FEE"
|
||||
id="recycle-fee-label">
|
||||
<textLink
|
||||
linkType="text"
|
||||
:text="item?.name ?? ''"
|
||||
href="javascript:void(0)"
|
||||
@clickEvent="openModal(RECYCLING_MODAL_REF_NAME)" />
|
||||
</span>
|
||||
<span v-else>{{ item?.name ?? '' }}</span>
|
||||
<span>{{ item?.name ?? '' }}</span>
|
||||
<span>{{ formatAmountInDollars(item?.subTotal ?? 0) }}</span>
|
||||
</div>
|
||||
<textLink
|
||||
|
|
@ -48,32 +48,12 @@
|
|||
</template>
|
||||
</textLink>
|
||||
</div>
|
||||
|
||||
<!-- Packaged Cart Items -->
|
||||
<div
|
||||
v-for="(item, i) in servicePackageCartItems"
|
||||
:key="i"
|
||||
class="cart-item packaged">
|
||||
<div class="price-row">
|
||||
<span>{{ item?.name ?? '' }}</span>
|
||||
<span>{{ formatAmountInDollars(item?.subTotal ?? 0) }}</span>
|
||||
</div>
|
||||
<textLink
|
||||
v-if="!readOnly"
|
||||
linkType="text"
|
||||
text="Remove"
|
||||
href="javascript:void(0)"
|
||||
@clickEvent="removeVap(item.partType)">
|
||||
<template #after-text>
|
||||
<span class="sr-only">{{ item?.name ?? '' }}</span>
|
||||
</template>
|
||||
</textLink>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
id="cart-footer"
|
||||
class="cart-footer pb-5">
|
||||
class="cart-footer">
|
||||
<div
|
||||
v-if="!isUnverified"
|
||||
id="cart-subtotal-and-tax"
|
||||
class="subtotal-and-tax">
|
||||
<div
|
||||
|
|
@ -103,11 +83,10 @@
|
|||
<span id="bottom-amount-due-value">{{ getDisplayed(amountDue) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<contentGroupModal
|
||||
:ref="RECYCLING_MODAL_REF_NAME"
|
||||
:cmsWidgetName="recyclingModalCmsWidgetName">
|
||||
<textBlock cmsWidgetName="RecycleTextBlock" />
|
||||
</contentGroupModal>
|
||||
</div>
|
||||
<div v-else class="deductible-box">
|
||||
<span class="deductible-label">{{ deductibleLabel }}:</span>
|
||||
<span class="deductible-value">{{ getDisplayed(deductible) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -117,7 +96,6 @@ import contentGroupModal from '@/iss-components/content-group-modal/content-grou
|
|||
import textBlock from '@/digital-components/text-block/text-block.vue';
|
||||
import textLink from '@/ux-components/text-link/text-link.vue';
|
||||
import { formatAmountInDollars } from '@/helpers/text-helper.js';
|
||||
import { getHighestFullySatisfiedTier, getPackageContents } from '@/helpers/service-package-helper.js';
|
||||
|
||||
// Constants
|
||||
import { experimentSettings } from '@/constants/experiments';
|
||||
|
|
@ -128,6 +106,7 @@ import {
|
|||
getCartTotal,
|
||||
getDeductible,
|
||||
getLineItems, getMobileFeeLineItem,
|
||||
getRecalibrationTotal,
|
||||
getRecycleFeeLineItem, getSalesTax,
|
||||
getServiceLineItems, getSubtotal, isOrderITAC, isOrderNoComp,
|
||||
isOrderUnverified
|
||||
|
|
@ -136,19 +115,15 @@ import { getPriceOfLineItems, getTaxOfLineItems } from '@/helpers/price-calculat
|
|||
import { processIfStatements } from '@/helpers/cms-content-helper';
|
||||
|
||||
const VERIFYING_COVERAGE = 'Verifying coverage';
|
||||
const RECYCLING_MODAL_REF_NAME = 'RecycleModal';
|
||||
|
||||
export default {
|
||||
name: 'confirmation-cart',
|
||||
components: {
|
||||
contentGroupModal,
|
||||
textBlock,
|
||||
textLink
|
||||
},
|
||||
props: {
|
||||
readOnly: Boolean,
|
||||
showAsPaid: Boolean,
|
||||
recyclingModalCmsWidgetName: String,
|
||||
submittedOrder: Object
|
||||
},
|
||||
data() {
|
||||
|
|
@ -158,6 +133,7 @@ export default {
|
|||
amountPaid: 'AmountPaidTextWidget',
|
||||
deductible: 'DeductibleWidget',
|
||||
basePrice: 'BasePriceWidget',
|
||||
recalibration: 'RecalibrationWidget',
|
||||
subtotal: 'SubtotalWidget',
|
||||
salesTax: 'SalesTaxWidget',
|
||||
recycleFee: 'RecycleFeeWidget',
|
||||
|
|
@ -167,8 +143,7 @@ export default {
|
|||
warrantyText: 'WarrantyCartItemTextWidget',
|
||||
guaranteeText: 'GuaranteeCartItemTextWidget'
|
||||
},
|
||||
cartItemType,
|
||||
RECYCLING_MODAL_REF_NAME
|
||||
cartItemType
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -181,9 +156,18 @@ export default {
|
|||
deductible() {
|
||||
return getDeductible(this.cartOrder);
|
||||
},
|
||||
isDeductibleOnly() {
|
||||
return this.cartItems.length === 0;
|
||||
},
|
||||
showDeductibleCartItem() {
|
||||
return this.isUnverified || (!this.isNoComp && !this.isITAC);
|
||||
},
|
||||
showRecalibrationCartItem() {
|
||||
return this.recalibrationPrice > 0;
|
||||
},
|
||||
recalibrationPrice() {
|
||||
return getRecalibrationTotal(this.cartOrder);
|
||||
},
|
||||
lineItems() {
|
||||
return getLineItems(this.cartOrder);
|
||||
},
|
||||
|
|
@ -191,7 +175,8 @@ export default {
|
|||
return getRecycleFeeLineItem(this.cartOrder);
|
||||
},
|
||||
servicePrice() {
|
||||
return getPriceOfLineItems(getServiceLineItems(this.cartOrder)) ?? 0;
|
||||
const price = getPriceOfLineItems(getServiceLineItems(this.cartOrder)) ?? 0;
|
||||
return price - this.recalibrationPrice;
|
||||
},
|
||||
isUnverified() {
|
||||
return isOrderUnverified(this.cartOrder);
|
||||
|
|
@ -229,47 +214,21 @@ export default {
|
|||
];
|
||||
return result;
|
||||
},
|
||||
vehicleDamage() {
|
||||
return this.cartOrder.damage;
|
||||
},
|
||||
servicePackageTier() {
|
||||
const { glassToReplace, isRepair } = this.vehicleDamage;
|
||||
const { vaps } = this.lineItems;
|
||||
return getHighestFullySatisfiedTier(
|
||||
glassToReplace ?? [],
|
||||
this.availableLineItems,
|
||||
isRepair,
|
||||
vaps ?? []
|
||||
);
|
||||
},
|
||||
partTypesInServicePackage() {
|
||||
const { glassToReplace, isRepair } = this.vehicleDamage;
|
||||
return getPackageContents(
|
||||
glassToReplace ?? [],
|
||||
this.availableLineItems,
|
||||
isRepair,
|
||||
this.servicePackageTier
|
||||
) ?? [];
|
||||
},
|
||||
servicePackageCartItems() {
|
||||
cartItems() {
|
||||
const items = [];
|
||||
if (this.partTypesInServicePackage.includes(partTypeStrings.FRONT_WIPER) ?? this.frontWipersCartItem) {
|
||||
items.push(this.frontWipersCartItem);
|
||||
const isRecycleFeeHidden = this.getSettingValue(experimentSettings.ISS_FEATURE_TOGGLE_IS_RECYCLE_FEE_HIDDEN) === 'true';
|
||||
const isMobileFeeHidden = this.getSettingValue(experimentSettings.ISS_FEATURE_TOGGLE_IS_MOBILE_FEE_HIDDEN) === 'true';
|
||||
if (this.isITAC || this.isNoComp) {
|
||||
items.push(this.warrantyCartItem);
|
||||
}
|
||||
if (this.partTypesInServicePackage.includes(partTypeStrings.REAR_WIPER) ?? this.rearWipersCartItem) {
|
||||
items.push(this.rearWipersCartItem);
|
||||
if (this.recycleFeeCartItem && !isRecycleFeeHidden) {
|
||||
items.push(this.recycleFeeCartItem);
|
||||
}
|
||||
if (this.partTypesInServicePackage.includes(partTypeStrings.RAIN_DEFENSE) ?? this.rainDefenseCartItem) {
|
||||
items.push(this.rainDefenseCartItem);
|
||||
if (this.mobileFeeCartItem && !isMobileFeeHidden) {
|
||||
items.push(this.mobileFeeCartItem);
|
||||
}
|
||||
return items;
|
||||
},
|
||||
nonServicePackageCartItems() {
|
||||
const vapPartTypesInOrder = Array.from(new Set(this.lineItems.vaps?.map((vap) => vap.partType) ?? []));
|
||||
const vapPartTypesInOrderButNotPackage = vapPartTypesInOrder
|
||||
.filter((partType) => !this.partTypesInServicePackage.includes(partType))
|
||||
?? [];
|
||||
const vapsCartItemsNotInPackage = vapPartTypesInOrderButNotPackage.map((partType) => {
|
||||
const vapsCartItems = vapPartTypesInOrder.map((partType) => {
|
||||
switch (partType) {
|
||||
case partTypeStrings.FRONT_WIPER:
|
||||
return this.frontWipersCartItem;
|
||||
|
|
@ -281,18 +240,7 @@ export default {
|
|||
return null;
|
||||
}
|
||||
});
|
||||
const items = vapsCartItemsNotInPackage.filter((item) => item != null);
|
||||
const isRecycleFeeHidden = this.getSettingValue(experimentSettings.ISS_FEATURE_TOGGLE_IS_RECYCLE_FEE_HIDDEN) === 'true';
|
||||
const isMobileFeeHidden = this.getSettingValue(experimentSettings.ISS_FEATURE_TOGGLE_IS_MOBILE_FEE_HIDDEN) === 'true';
|
||||
if (this.recycleFeeCartItem && !isRecycleFeeHidden) {
|
||||
items.push(this.recycleFeeCartItem);
|
||||
}
|
||||
if (this.mobileFeeCartItem && !isMobileFeeHidden) {
|
||||
items.push(this.mobileFeeCartItem);
|
||||
}
|
||||
if (this.isITAC || this.isNoComp) {
|
||||
items.push(this.warrantyCartItem);
|
||||
}
|
||||
items.push(...vapsCartItems.filter((item) => item != null));
|
||||
return items;
|
||||
},
|
||||
frontWipersCartItem() {
|
||||
|
|
@ -304,12 +252,6 @@ export default {
|
|||
rainDefenseCartItem() {
|
||||
return this.getCartItemForVapsPart(partTypeStrings.RAIN_DEFENSE);
|
||||
},
|
||||
allCartItems() {
|
||||
return [
|
||||
...this.servicePackageCartItems,
|
||||
...this.nonServicePackageCartItems
|
||||
];
|
||||
},
|
||||
amountDueLabel() {
|
||||
return this.getCmsContent(this.widget.amountDue, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
||||
},
|
||||
|
|
@ -327,27 +269,15 @@ export default {
|
|||
this.getCustomValueFromString
|
||||
);
|
||||
},
|
||||
recalibrationLabel() {
|
||||
return this.getCmsContent(this.widget.recalibration, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
||||
},
|
||||
subtotalLabel() {
|
||||
return this.getCmsContent(this.widget.subtotal, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
||||
},
|
||||
salesTaxLabel() {
|
||||
return this.getCmsContent(this.widget.salesTax, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
||||
},
|
||||
servicePackageNames() {
|
||||
return this.getCmsContent(
|
||||
this.widget.servicePackage,
|
||||
widgetFields.INPUT_QUESTION_WIDGET.ANSWERS
|
||||
);
|
||||
},
|
||||
servicePackageLabelWidget() {
|
||||
if (!this.servicePackageNames) {
|
||||
return '';
|
||||
}
|
||||
const currentPackage = this.servicePackageNames
|
||||
?.find((entry) => entry?.Name === this.servicePackageTier);
|
||||
|
||||
return currentPackage?.SubWidgetName ?? '';
|
||||
},
|
||||
recycleFeeCartItem() {
|
||||
return this.recycleFeeLineItem
|
||||
? this.getCartItem(
|
||||
|
|
@ -452,8 +382,6 @@ export default {
|
|||
<style lang="scss" scoped>
|
||||
@import "@/styles/ux-variables-svg-strings.scss";
|
||||
|
||||
$RECYCLING_MODAL_REF_NAME: 'RecycleModal';
|
||||
|
||||
.cart-table {
|
||||
max-height: 50rem;
|
||||
transition: all 150ms ease-in;
|
||||
|
|
@ -508,22 +436,18 @@ $RECYCLING_MODAL_REF_NAME: 'RecycleModal';
|
|||
padding: .5rem 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.modal-content a.external-text) {
|
||||
text-underline-offset: 0.25rem;
|
||||
line-height: 2;
|
||||
padding: 0 0 0.25rem 0;
|
||||
font-weight: 500;
|
||||
max-width: -webkit-fit-content;
|
||||
max-width: -moz-fit-content;
|
||||
max-width: fit-content;
|
||||
.deductible-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: .25rem;
|
||||
}
|
||||
|
||||
:deep(##{$RECYCLING_MODAL_REF_NAME} h5) {
|
||||
text-align: center;
|
||||
.deductible-label {
|
||||
color: $black;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
:deep(#recycle-fee-label a){
|
||||
line-height: initial;
|
||||
.deductible-value {
|
||||
color: $green;
|
||||
font-weight: $font-weight-bold;
|
||||
font-size: 1.625rem;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
exports[`contact-details-drawer snapshot matches returns the initial data 1`] = `
|
||||
Object {
|
||||
"detailsSaved": false,
|
||||
"emailAddress": "fred.tay@gmail.com",
|
||||
"extension": null,
|
||||
"firstName": "Frederick",
|
||||
|
|
@ -15,8 +16,8 @@ Object {
|
|||
"rules": Object {
|
||||
"emailAddress": "email-required|email-address-format",
|
||||
"extension": "extension-format",
|
||||
"firstName": "first-name-required",
|
||||
"lastName": "last-name-required",
|
||||
"firstName": "policyholder-first-name-required",
|
||||
"lastName": "policyholder-last-name-required",
|
||||
"phoneNumber": "phone-number-required|phone-number-format",
|
||||
},
|
||||
"widget": Object {
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
// Components
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import contactDetailsDrawer from '@/layouts/tpa-submit/contact-details-drawer/contact-details-drawer.vue';
|
||||
import contactDetailsDrawer from '@/iss-components/contact-details-drawer/contact-details-drawer.vue';
|
||||
|
||||
// Supporting Files
|
||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
||||
|
|
@ -9,25 +9,29 @@
|
|||
@isModalOpened="setModalStatus"
|
||||
@footerButtonEvent="clickFooterButtonEvent">
|
||||
<template v-if="isModalOpened">
|
||||
<div class="row">
|
||||
<span class="name-section-label">{{ nameSectionLabel }}</span>
|
||||
<div class="row form-group">
|
||||
<textboxQuestion
|
||||
class="col"
|
||||
class="col-lg-6"
|
||||
ref="firstNameQuestion"
|
||||
v-model="firstName"
|
||||
inputId="firstName"
|
||||
:displayQuestionText="false"
|
||||
:cmsWidgetName="widget.firstNameQuestion"
|
||||
isRequired
|
||||
:validationRules="rules.firstName" />
|
||||
<textboxQuestion
|
||||
class="col"
|
||||
class="col-lg-6 last-name-question"
|
||||
ref="lastNameQuestion"
|
||||
v-model="lastName"
|
||||
inputId="lastName"
|
||||
:displayQuestionText="false"
|
||||
:cmsWidgetName="widget.lastNameQuestion"
|
||||
isRequired
|
||||
:validationRules="rules.lastName" />
|
||||
</div>
|
||||
<textboxQuestion
|
||||
class="form-group"
|
||||
ref="emailQuestion"
|
||||
v-model="emailAddress"
|
||||
inputId="emailAddress"
|
||||
|
|
@ -35,6 +39,7 @@
|
|||
isRequired
|
||||
:validationRules="rules.emailAddress" />
|
||||
<textboxQuestion
|
||||
class="form-group"
|
||||
ref="phoneNumberQuestion"
|
||||
v-model="phoneNumber"
|
||||
inputId="phoneNumber"
|
||||
|
|
@ -44,6 +49,7 @@
|
|||
disableAutoFill
|
||||
:validationRules="rules.phoneNumber" />
|
||||
<textboxQuestion
|
||||
class="form-group"
|
||||
ref="extensionQuestion"
|
||||
v-model="extension"
|
||||
inputId="extension"
|
||||
|
|
@ -73,7 +79,7 @@ export default {
|
|||
modal,
|
||||
textboxQuestion
|
||||
},
|
||||
emits: ['update-contact-details'],
|
||||
emits: ['update-contact-details', 'close-without-saving'],
|
||||
data() {
|
||||
const { firstName,
|
||||
lastName,
|
||||
|
|
@ -97,13 +103,14 @@ export default {
|
|||
drawerFooter: 'ContactDetailsDrawerFooterWidget'
|
||||
},
|
||||
rules: {
|
||||
firstName: globalRules.FIRST_NAME_REQUIRED,
|
||||
lastName: globalRules.LAST_NAME_REQUIRED,
|
||||
firstName: globalRules.POLICYHOLDER_FIRST_NAME_REQUIRED,
|
||||
lastName: globalRules.POLICYHOLDER_LAST_NAME_REQUIRED,
|
||||
emailAddress: `${globalRules.EMAIL_ADDRESS_REQUIRED}|${globalRules.EMAIL_ADDRESS_FORMAT}`,
|
||||
phoneNumber: `${globalRules.PHONE_NUMBER_REQUIRED}|${globalRules.PHONE_NUMBER_FORMAT}`,
|
||||
extension: `${globalRules.EXTENSION_FORMAT}`
|
||||
},
|
||||
modalPositions
|
||||
modalPositions,
|
||||
detailsSaved: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -121,13 +128,17 @@ export default {
|
|||
},
|
||||
phoneMask() {
|
||||
return MaskaFormattedMasks.PHONE_NUMBER;
|
||||
}
|
||||
},
|
||||
nameSectionLabel() {
|
||||
return this.getCmsContent(this.widget.firstNameQuestion, widgetFields.INPUT_QUESTION_WIDGET.QUESTION_TEXT);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
setModalStatus(isOpened) {
|
||||
this.isModalOpened = isOpened;
|
||||
},
|
||||
clickFooterButtonEvent() {
|
||||
this.detailsSaved = true;
|
||||
this.saveContactDetails();
|
||||
this.closeModal();
|
||||
},
|
||||
|
|
@ -146,6 +157,9 @@ export default {
|
|||
this.$emit('update-contact-details');
|
||||
},
|
||||
resetFormValues() {
|
||||
if (!this.detailsSaved) {
|
||||
this.$emit('close-without-saving');
|
||||
}
|
||||
const { firstName,
|
||||
lastName,
|
||||
emailAddress,
|
||||
|
|
@ -156,6 +170,7 @@ export default {
|
|||
this.emailAddress = emailAddress;
|
||||
this.phoneNumber = servicePhone;
|
||||
this.extension = extension;
|
||||
this.detailsSaved = false;
|
||||
},
|
||||
openModal() {
|
||||
this.modal.openModal();
|
||||
|
|
@ -174,13 +189,18 @@ export default {
|
|||
font-size: $h5-font-size;
|
||||
font-weight: $font-weight-normal !important;
|
||||
}
|
||||
.textbox-question {
|
||||
.form-group {
|
||||
margin-bottom: 1.25rem;
|
||||
:deep(label:has(b)) {
|
||||
font-weight: $font-weight-normal;
|
||||
b {
|
||||
font-weight: 600;
|
||||
}
|
||||
align-items: start;
|
||||
}
|
||||
.name-section-label {
|
||||
color: $black;
|
||||
font-weight: 600;
|
||||
margin-bottom: .25rem;
|
||||
}
|
||||
.last-name-question {
|
||||
@include media-breakpoint-down(lg) {
|
||||
margin-top: .625rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -157,8 +157,7 @@ export default {
|
|||
this.navigateForward(this.partsOrQuestionsData, null);
|
||||
},
|
||||
requestCallbackBailout() {
|
||||
this.mainStore.setBailout(bailoutMessage.RequestCallback());
|
||||
this.$router.navigate(navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT, this.$route);
|
||||
this.$router.navigateBailout(bailoutMessage.RequestCallback());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,116 @@
|
|||
<template>
|
||||
<div class="afterpay-modal-banner" role="alert">
|
||||
<component
|
||||
src="https://js.squarecdn.com/square-marketplace.js"
|
||||
async
|
||||
:is="'script'">
|
||||
</component>
|
||||
<div class="afterpay-content">
|
||||
<span class="afterpay-copy" v-html="afterpayCopy"></span>
|
||||
<span class="afterpay-copy">{{ afterpayPaymentAmount }}</span>
|
||||
<span class="afterpay-copy" v-html="afterpayCopyTwo"></span>
|
||||
<img :src="imageUrl" class="image" alt="Afterpay logo"/>
|
||||
<span class="afterpay-copy">. </span>
|
||||
<a
|
||||
id="afterpay-learnmore"
|
||||
href="#"
|
||||
data-afterpay-modal="en_US"
|
||||
data-bind="click:afterpayLearnMore"
|
||||
class="afterpay-learn-more"
|
||||
@click.prevent>
|
||||
<span>Learn more</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { setupModalLinks } from "@/helpers/cms-content-helper";
|
||||
import { formatAmountInDollars } from '@/helpers/text-helper.js';
|
||||
import widgetFields from '@/constants/cms-widget-fields.js';
|
||||
import { useMainStore } from '@/store/index.js';
|
||||
|
||||
export default {
|
||||
name: "afterpay-banner",
|
||||
components: {
|
||||
},
|
||||
props: {
|
||||
cmsWidgetName: String,
|
||||
totalServicePrice: Number
|
||||
},
|
||||
setup() {
|
||||
const mainStore = useMainStore();
|
||||
return { mainStore };
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
computed: {
|
||||
imageUrl() {
|
||||
return this.getCmsContent(this.cmsWidgetName, widgetFields.CONTENT_GROUP_WIDGET.IMAGE);
|
||||
},
|
||||
afterpayCopy() {
|
||||
return this.getCmsContent(this.cmsWidgetName, widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT);
|
||||
},
|
||||
afterpayCopyTwo() {
|
||||
return this.getCmsContent(this.cmsWidgetName, widgetFields.CONTENT_GROUP_WIDGET.SUBHEADER_TEXT);
|
||||
},
|
||||
deductibleValue() {
|
||||
return this.mainStore.currentDeductible;
|
||||
},
|
||||
afterpayPaymentAmount() {
|
||||
return this.calculateAfterpayPaymentAmount;
|
||||
},
|
||||
isDeductibleScenario() {
|
||||
return this.mainStore.isVerified && this.mainStore.isDeductible;
|
||||
},
|
||||
calculateAfterpayPaymentAmount() {
|
||||
const totalAmount = this.isDeductibleScenario ? this.deductibleValue : this.totalServicePrice;
|
||||
const afterpayAmount = (totalAmount / 4).toFixed(2);
|
||||
return formatAmountInDollars(afterpayAmount);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.afterpay-modal-banner {
|
||||
background-color: $blue-100;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0.5rem 1rem 0.5rem 1rem;
|
||||
border-radius: 0.25rem;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.afterpay-content {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.afterpay-copy {
|
||||
font-size: 0.875rem;
|
||||
color: $blue-700;
|
||||
font-weight: $font-weight-bold;
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
|
||||
.image-and-link {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.image {
|
||||
border: 0;
|
||||
height: 20px;
|
||||
width: 83px;
|
||||
}
|
||||
|
||||
:deep(a.afterpay-learn-more) {
|
||||
color: $blue-700;
|
||||
fill: $blue-700;
|
||||
text-decoration: underline;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -63,7 +63,8 @@ const loadingModalStub = {
|
|||
function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRun = () => {}) {
|
||||
const mountOptions = getMountOptions({
|
||||
router: {
|
||||
navigate: jest.fn()
|
||||
navigate: jest.fn(),
|
||||
navigateBailout: jest.fn()
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -439,6 +440,109 @@ describe('coverageStatement.vue', () => {
|
|||
expect(result).toBe(expected);
|
||||
});
|
||||
});
|
||||
describe('displayAfterpayBanner', () => {
|
||||
test('returns true if ITAC', () => {
|
||||
// Arrange
|
||||
const mainInitialState = {
|
||||
order: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.VERIFIED,
|
||||
coverageType: coverageType.ITAC
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(mainInitialState);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.displayAfterpayBanner;
|
||||
|
||||
// Assert
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
test('returns true if No Comp', () => {
|
||||
// Arrange
|
||||
const mainInitialState = {
|
||||
order: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.VERIFIED,
|
||||
coverageType: coverageType.NO_COMP
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(mainInitialState);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.displayAfterpayBanner;
|
||||
|
||||
// Assert
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
test('returns true if deductible is > $0', () => {
|
||||
// Arrange
|
||||
const mainInitialState = {
|
||||
order: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.VERIFIED,
|
||||
coverageType: coverageType.Deductible
|
||||
},
|
||||
damage: {
|
||||
isRepair: false
|
||||
},
|
||||
currentDeductible: {
|
||||
replace: 500
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(mainInitialState);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.displayAfterpayBanner;
|
||||
|
||||
// Assert
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
test('returns false if deductible is <= $0', () => {
|
||||
// Arrange
|
||||
const mainInitialState = {
|
||||
order: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.VERIFIED,
|
||||
coverageType: coverageType.Deductible
|
||||
},
|
||||
damage: {
|
||||
isRepair: false
|
||||
},
|
||||
currentDeductible: {
|
||||
replace: 0
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(mainInitialState);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.displayAfterpayBanner;
|
||||
|
||||
// Assert
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
test('returns false if Unverified', () => {
|
||||
// Arrange
|
||||
const mainInitialState = {
|
||||
order: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.UNVERIFIED,
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(mainInitialState);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.displayAfterpayBanner;
|
||||
|
||||
// Assert
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('methods', () => {
|
||||
describe('arePagePrerequisitesValid', () => {
|
||||
|
|
@ -587,7 +691,7 @@ describe('coverageStatement.vue', () => {
|
|||
undefined
|
||||
);
|
||||
});
|
||||
test('If Verified ITAC, selected Cancel, navigate forward w/ CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP', () => {
|
||||
test('If Verified ITAC, selected Cancel, navigateBailout', () => {
|
||||
// Arrange
|
||||
const deductible = servicePrice + 1;
|
||||
const mainInitialState = {
|
||||
|
|
@ -613,11 +717,7 @@ describe('coverageStatement.vue', () => {
|
|||
wrapper.vm.cancelClaim();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.$router.navigate)
|
||||
.toHaveBeenCalledWith(
|
||||
navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP,
|
||||
undefined
|
||||
);
|
||||
expect(wrapper.vm.$router.navigateBailout).toHaveBeenCalled();
|
||||
});
|
||||
test('If No Comp and selected Safelite, navigate forward with CLICKED_FORWARD_WITH_SAFELITE scenario', () => {
|
||||
// Arrange
|
||||
|
|
@ -650,7 +750,7 @@ describe('coverageStatement.vue', () => {
|
|||
undefined
|
||||
);
|
||||
});
|
||||
test('If No comp and selected other shop, navigate forward with CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP ', () => {
|
||||
test('If No comp and selected other shop, navigateBailout ', () => {
|
||||
// Arrange
|
||||
const mainInitialState = {
|
||||
order: {
|
||||
|
|
@ -675,11 +775,7 @@ describe('coverageStatement.vue', () => {
|
|||
wrapper.vm.cancelClaim();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.$router.navigate)
|
||||
.toHaveBeenCalledWith(
|
||||
navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP,
|
||||
undefined
|
||||
);
|
||||
expect(wrapper.vm.$router.navigateBailout).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
describe('openCancelClaimModal', () => {
|
||||
|
|
|
|||
|
|
@ -70,6 +70,10 @@
|
|||
class="d-flex justify-content-center cost mb-0 cost-underline">
|
||||
{{ formatAmountInDollars(totalServicePrice) }}
|
||||
</div>
|
||||
<afterpayModalBanner
|
||||
v-if="displayAfterpayBanner"
|
||||
cmsWidgetName="AfterpayModalWidget"
|
||||
:totalServicePrice="totalServicePrice" />
|
||||
<buttonMain
|
||||
ref="buttonMain"
|
||||
class="full-width-button mt-5 mb-5"
|
||||
|
|
@ -118,6 +122,7 @@ import textBlock from '@/digital-components/text-block/text-block.vue';
|
|||
import textLink from '@/ux-components/text-link/text-link.vue';
|
||||
import buttonMain from '@/ux-components/button-main/button-main.vue';
|
||||
import siteFooter from '@/iss-components/site-footer/site-footer.vue';
|
||||
import afterpayModalBanner from '@/layouts/coverage-statement/afterpay-modal-banner/afterpay-modal-banner.vue';
|
||||
// Import Supporting Files
|
||||
import {
|
||||
fetchCmsContentForPage,
|
||||
|
|
@ -152,7 +157,8 @@ export default {
|
|||
textBlock,
|
||||
textLink,
|
||||
siteFooter,
|
||||
cancelClaimModal
|
||||
cancelClaimModal,
|
||||
afterpayModalBanner
|
||||
},
|
||||
mixins: [baseFormMixin, vehicleQuestionsMixin],
|
||||
async beforeRouteEnter(to, from, next) {
|
||||
|
|
@ -339,6 +345,11 @@ export default {
|
|||
return 'centered-back-button';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
displayAfterpayBanner() {
|
||||
return this.isITACQuoteVisible
|
||||
|| this.isNoCompQuoteVisible
|
||||
|| (this.showDeductibleOnly && this.deductibleValue > 0);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
|
@ -386,18 +397,7 @@ export default {
|
|||
|
||||
// We only call the ITAC pricing endpoint if we are not repair or we are NoComp
|
||||
if (!this.isRepair || this.mainStore.isNoComp) {
|
||||
const pricingResults = await useMainStore().getITACPriceOrderItems(availableLineItems)
|
||||
.catch((err) => {
|
||||
useMainStore().setBailout(bailoutMessage.pricingResponseError(
|
||||
availableLineItems.map((li) => li.partNumber),
|
||||
{
|
||||
code: err.code,
|
||||
message: err.message,
|
||||
data: err.data
|
||||
}
|
||||
));
|
||||
this.navigateWithScenario(navigationScenarios.PRICING_LOOKUP_ERROR);
|
||||
});
|
||||
const pricingResults = await useMainStore().getITACPriceOrderItems(availableLineItems);
|
||||
this.setBaseServiceLineItems(pricingResults);
|
||||
}
|
||||
},
|
||||
|
|
@ -408,8 +408,7 @@ export default {
|
|||
this.mainStore.updateIsSafeliteProvider(true);
|
||||
this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE);
|
||||
} else {
|
||||
this.mainStore.setBailout(bailoutMessage.coverageStatementInvalidState());
|
||||
this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD_WITH_INVALID_STATE);
|
||||
this.$router.navigateBailout(bailoutMessage.coverageStatementInvalidState());
|
||||
}
|
||||
},
|
||||
navigateWithScenario(scenario) {
|
||||
|
|
@ -463,8 +462,7 @@ export default {
|
|||
},
|
||||
cancelClaim() {
|
||||
this.mainStore.updateIsSafeliteProvider(false);
|
||||
this.mainStore.setBailout(bailoutMessage.RequestCallback());
|
||||
this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP);
|
||||
this.$router.navigateBailout(bailoutMessage.CoverageCancelledByUser());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -357,11 +357,15 @@ describe('duplicateCheck.vue', () => {
|
|||
useMainStore().loadSession = jest.fn().mockImplementation(() => Promise.reject(error));
|
||||
|
||||
// Act
|
||||
await wrapper.vm.forwardButtonAction();
|
||||
|
||||
// Assert
|
||||
expect.assertions(2);
|
||||
try {
|
||||
await wrapper.vm.forwardButtonAction();
|
||||
} catch (e) {
|
||||
expect(e).toMatch(error);
|
||||
}
|
||||
expect(wrapper.vm.mainStore.loadSession).toHaveBeenCalledTimes(1);
|
||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
test('coverageType deductible and policy vehicles returned => CLICKED_FORWARD_POLICY_VERIFIED_WITH_VEHICLES', async () => {
|
||||
// Arrange
|
||||
|
|
|
|||
|
|
@ -140,18 +140,14 @@ export default {
|
|||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const selectedReferral =
|
||||
this.mainStore.applicationUser.duplicateOrders.find((o) => o.correlationId === this.selectedAnswer);
|
||||
const selectedReferral =
|
||||
this.mainStore.applicationUser.duplicateOrders.find((o) => o.correlationId === this.selectedAnswer);
|
||||
|
||||
if (selectedReferral) {
|
||||
await this.mainStore.loadSession(selectedReferral);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`Error on loading session from duplicate check ${err}`);
|
||||
} finally {
|
||||
this.navigateForward();
|
||||
if (selectedReferral) {
|
||||
await this.mainStore.loadSession(selectedReferral);
|
||||
}
|
||||
|
||||
this.navigateForward();
|
||||
},
|
||||
navigateForward() {
|
||||
this.mainStore.updateDuplicateCheckVisited(true);
|
||||
|
|
|
|||
|
|
@ -34,44 +34,51 @@ export default {
|
|||
computed: {
|
||||
},
|
||||
async mounted() {
|
||||
const queryStringParams = this.parseQueryParms();
|
||||
|
||||
const { isAuthorized, clientData, decryptedParams } = await this.validateClientTagOnEntry(queryStringParams);
|
||||
|
||||
this.unauthorized = !isAuthorized;
|
||||
if (!isAuthorized) {
|
||||
// Remove the loading animation if client tag validation fails so users can see the Unauthorized Access message.
|
||||
showIssLoadingModal(false);
|
||||
return;
|
||||
}
|
||||
|
||||
this.populateISSConfigValues(clientData);
|
||||
|
||||
try {
|
||||
// Check cookie
|
||||
const issCookie = getISSCookie();
|
||||
if (issCookie !== null && issCookie.VehicleMake && issCookie.VehicleModel) {
|
||||
const clientParentAccountNumber = clientData.parentAccountNumber;
|
||||
const cookieParentAccountNumber = issCookie.ReferralParentAccountNumber;
|
||||
const queryStringParams = this.parseQueryParms();
|
||||
|
||||
if (clientParentAccountNumber === cookieParentAccountNumber) {
|
||||
const savedSessionTimeStamp = new Date(issCookie.SavedSessionTimeoutDate);
|
||||
const isSavedSessionTimedOut = new Date(new Date().toUTCString()) > savedSessionTimeStamp;
|
||||
const { isAuthorized, clientData, decryptedParams } = await this.validateClientTagOnEntry(queryStringParams);
|
||||
|
||||
if (!isSavedSessionTimedOut) {
|
||||
this.mainStore.issConfig.enableContinueFromCookie = true;
|
||||
this.unauthorized = !isAuthorized;
|
||||
if (!isAuthorized) {
|
||||
// Remove the loading animation if client tag validation fails so users can see the Unauthorized Access message.
|
||||
showIssLoadingModal(false);
|
||||
return;
|
||||
}
|
||||
|
||||
this.populateISSConfigValues(clientData);
|
||||
|
||||
try {
|
||||
// Check cookie
|
||||
const issCookie = getISSCookie();
|
||||
if (issCookie !== null && issCookie.VehicleMake && issCookie.VehicleModel) {
|
||||
const clientParentAccountNumber = clientData.parentAccountNumber;
|
||||
const cookieParentAccountNumber = issCookie.ReferralParentAccountNumber;
|
||||
|
||||
if (clientParentAccountNumber === cookieParentAccountNumber) {
|
||||
const savedSessionTimeStamp = new Date(issCookie.SavedSessionTimeoutDate);
|
||||
const isSavedSessionTimedOut = new Date(new Date().toUTCString()) > savedSessionTimeStamp;
|
||||
|
||||
if (!isSavedSessionTimedOut) {
|
||||
this.mainStore.issConfig.enableContinueFromCookie = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
updateOrCreateISSCookie(true);
|
||||
}
|
||||
} else {
|
||||
} catch {
|
||||
updateOrCreateISSCookie(true);
|
||||
}
|
||||
} catch {
|
||||
updateOrCreateISSCookie(true);
|
||||
}
|
||||
|
||||
if (clientData.parameters?.length > 0) {
|
||||
const finalParams = this.combineClientParameters(clientData.parameters, { ...queryStringParams, ...decryptedParams });
|
||||
this.populateStoreItemsFromParams(finalParams);
|
||||
if (clientData.parameters?.length > 0) {
|
||||
const finalParams = this.combineClientParameters(clientData.parameters, { ...queryStringParams, ...decryptedParams });
|
||||
this.populateStoreItemsFromParams(finalParams);
|
||||
}
|
||||
} catch (e) {
|
||||
global.$logger.logError('[Entry Page] Client Setup Error:', e);
|
||||
this.unauthorized = true;
|
||||
showIssLoadingModal(false);
|
||||
return;
|
||||
}
|
||||
|
||||
this.mainStore.applicationUser.coverageAttempts = 0;
|
||||
|
|
@ -151,37 +158,33 @@ export default {
|
|||
this.mainStore.issConfig.isCoverageEnabled = data.coverageEnabled;
|
||||
this.mainStore.issConfig.siteType = data.siteType;
|
||||
|
||||
try {
|
||||
if (data.clientFlags) {
|
||||
const clientFlags = JSON.parse(data.clientFlags);
|
||||
if (data.clientFlags) {
|
||||
const clientFlags = JSON.parse(data.clientFlags);
|
||||
|
||||
if (clientFlags.TPAEnabled) {
|
||||
this.mainStore.issConfig.enableTPAFlow = true;
|
||||
}
|
||||
|
||||
if (clientFlags.ClientFullName != null) {
|
||||
this.mainStore.issConfig.clientFullName = clientFlags.ClientFullName;
|
||||
}
|
||||
|
||||
if (clientFlags.ClientDisplayName != null) {
|
||||
this.mainStore.issConfig.clientDisplayName = clientFlags.ClientDisplayName;
|
||||
this.mainStore.issConfig.clientPossessiveName = toPossessive(clientFlags.ClientDisplayName);
|
||||
}
|
||||
|
||||
if (clientFlags.ClientPossessiveName != null) {
|
||||
this.mainStore.issConfig.clientPossessiveName = clientFlags.ClientPossessiveName;
|
||||
}
|
||||
|
||||
if (clientFlags.ClaimRegistrationRequired) {
|
||||
this.mainStore.issConfig.isClaimRegistrationRequired = true;
|
||||
}
|
||||
|
||||
if (clientFlags.EnableNoCompQuote) {
|
||||
this.mainStore.issConfig.enableNoCompQuote = true;
|
||||
}
|
||||
if (clientFlags.TPAEnabled) {
|
||||
this.mainStore.issConfig.enableTPAFlow = true;
|
||||
}
|
||||
|
||||
if (clientFlags.ClientFullName != null) {
|
||||
this.mainStore.issConfig.clientFullName = clientFlags.ClientFullName;
|
||||
}
|
||||
|
||||
if (clientFlags.ClientDisplayName != null) {
|
||||
this.mainStore.issConfig.clientDisplayName = clientFlags.ClientDisplayName;
|
||||
this.mainStore.issConfig.clientPossessiveName = toPossessive(clientFlags.ClientDisplayName);
|
||||
}
|
||||
|
||||
if (clientFlags.ClientPossessiveName != null) {
|
||||
this.mainStore.issConfig.clientPossessiveName = clientFlags.ClientPossessiveName;
|
||||
}
|
||||
|
||||
if (clientFlags.ClaimRegistrationRequired) {
|
||||
this.mainStore.issConfig.isClaimRegistrationRequired = true;
|
||||
}
|
||||
|
||||
if (clientFlags.EnableNoCompQuote) {
|
||||
this.mainStore.issConfig.enableNoCompQuote = true;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`Error parsing client flags: ${e}`);
|
||||
}
|
||||
},
|
||||
combineClientParameters(configParams, queryStringParams) {
|
||||
|
|
|
|||
|
|
@ -148,8 +148,7 @@ export default {
|
|||
this.navigateForward(partsOrQuestions, null);
|
||||
},
|
||||
requestCallbackBailout() {
|
||||
this.mainStore.setBailout(bailoutMessage.RequestCallback());
|
||||
this.$router.navigate(navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT, this.$route);
|
||||
this.$router.navigateBailout(bailoutMessage.RequestCallback());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@
|
|||
</div>
|
||||
<cartDropdown
|
||||
v-if="showCart"
|
||||
class="cart-dropdown"
|
||||
:showAsPaid="payment.isPayInAdvance"
|
||||
:readOnly="true"
|
||||
recyclingModalCmsWidgetName="RecycleModal"
|
||||
|
|
@ -230,7 +231,7 @@ export default {
|
|||
address: this.appointmentLocation,
|
||||
inShopDuration: this.inShopAppointmentDuration,
|
||||
email: this.customerEmail,
|
||||
phone: this.contactInfo.servicePhone,
|
||||
phone: this.contactPhone,
|
||||
workOrderNumber: this.workOrderNumber,
|
||||
CUSTOMER_PORTAL_URL: applicationConfig.CUSTOMER_PORTAL_URL,
|
||||
CUSTOMER_PORTAL_LOGIN_TOKEN: this.customerPortalLoginToken
|
||||
|
|
@ -492,6 +493,13 @@ export default {
|
|||
widgetFields.TEXT_BLOCK_WIDGET.TEXT
|
||||
);
|
||||
}
|
||||
},
|
||||
contactPhone() {
|
||||
let phoneNumber = this.contactInfo.servicePhone;
|
||||
if (this.contactInfo.extension) {
|
||||
phoneNumber += `-${this.contactInfo.extension}`;
|
||||
}
|
||||
return phoneNumber;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
|
@ -718,5 +726,10 @@ export default {
|
|||
color: $black;
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
|
||||
.cart-dropdown:deep(.deductible-value) {
|
||||
font-size: 1rem;
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -148,8 +148,7 @@ export default {
|
|||
this.navigateForward(glassPartsForStore, null);
|
||||
},
|
||||
requestCallbackBailout() {
|
||||
this.mainStore.setBailout(bailoutMessage.RequestCallback());
|
||||
this.$router.navigate(navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT, this.$route);
|
||||
this.$router.navigateBailout(bailoutMessage.RequestCallback());
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,32 +2,30 @@
|
|||
<baseInputButton
|
||||
v-bind="$props"
|
||||
v-model="selectedValue"
|
||||
buttonWrapperClasses="list-group base-input-button list-button rounded-3 d-flex flex-column w-100 mb-2">
|
||||
<div class="button-content list-button-content d-flex flex-row py-3 px-4">
|
||||
buttonWrapperClasses="list-group base-input-button list-button payment-method d-flex flex-column w-100 no-hover">
|
||||
<div class="button-content list-button-content d-flex flex-row">
|
||||
<div>
|
||||
<span
|
||||
v-for="token in tokens"
|
||||
:key="token">
|
||||
<span v-if="isInlineImageToken(token)">
|
||||
<img
|
||||
v-if="hasImage"
|
||||
:src="buttonImage"
|
||||
:alt="getInlineAltText(token)" />
|
||||
<span v-else> {{ getInlineAltText(token) }}</span>
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ token }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<img
|
||||
v-if="shouldDisplaySideImage"
|
||||
:id="buttonImageId"
|
||||
class="ms-auto order-3"
|
||||
class="ms-auto"
|
||||
:src="buttonImage"
|
||||
:alt="altText" />
|
||||
<div class="order-2">
|
||||
<p class="m-0">
|
||||
<span
|
||||
v-for="token in tokens"
|
||||
:key="token">
|
||||
<span v-if="isInlineImageToken(token)">
|
||||
<img
|
||||
v-if="hasImage"
|
||||
:src="buttonImage"
|
||||
:alt="getInlineAltText(token)" />
|
||||
<span v-else> {{ getInlineAltText(token) }}</span>
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ token }}
|
||||
</span>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</baseInputButton>
|
||||
</template>
|
||||
|
|
@ -74,52 +72,41 @@ export default {
|
|||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.list-button {
|
||||
.list-button.payment-method.list-group {
|
||||
outline: none;
|
||||
input[type="radio"],
|
||||
input[type="checkbox"] {
|
||||
position: static; //override bootstrap
|
||||
|
||||
&:focus-visible + .list-button-content {
|
||||
box-shadow: 0 0 0 2.5px $blue;
|
||||
}
|
||||
&:focus + .list-button-content {
|
||||
box-shadow: 0 0 0 2.5px $blue;
|
||||
}
|
||||
&:checked + .list-button-content {
|
||||
background: $background-color-selected;
|
||||
border-color: $heritage-blue-primary;
|
||||
font-weight: 600;
|
||||
color: $black;
|
||||
font-weight: 500;
|
||||
background: $blue-100;
|
||||
box-shadow: 0 0 0 1px $blue;
|
||||
}
|
||||
&:checked:focus + .list-button-content {
|
||||
box-shadow: 0 0 0 2.5px $blue;
|
||||
}
|
||||
&:checked + .list-button-content p,
|
||||
&:checked + .list-button-content span {
|
||||
font-weight: 500;
|
||||
}
|
||||
&:checked + .list-button-content span:nth-child(2) {
|
||||
font-weight: 400;
|
||||
color: $gray-600;
|
||||
}
|
||||
margin-bottom: .75rem;
|
||||
border: none;
|
||||
|
||||
&.has-error {
|
||||
input[type="radio"],
|
||||
input[type="checkbox"] {
|
||||
&:focus + .list-button-content {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.list-button-content {
|
||||
color: $gray-600;
|
||||
color: $darker-gray;
|
||||
font-weight: 400;
|
||||
position: relative;
|
||||
background: $white;
|
||||
transition: all 150ms linear;
|
||||
border-radius: $border-radius-lg;
|
||||
border: 1px solid $gray-500;
|
||||
border-radius: 3.75rem;
|
||||
border: 1px solid #CACBCC;
|
||||
width: 100%;
|
||||
outline: none;
|
||||
|
||||
span {
|
||||
&.small {
|
||||
font-size: $font-size-xsm;
|
||||
color: $gray-550;
|
||||
}
|
||||
}
|
||||
padding: .625rem 2rem;
|
||||
height: 2.875rem;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
import buttonQuestion from '@/digital-components/button-question/button-question.vue';
|
||||
import widgetFields from '@/constants/cms-widget-fields.js';
|
||||
import paymentMethodListButton from './payment-method-list-button/payment-method-list-button.vue';
|
||||
import { markRaw } from 'vue';
|
||||
|
||||
export default {
|
||||
name: 'payment-method-question',
|
||||
|
|
@ -33,7 +34,7 @@ export default {
|
|||
emits: ['update:modelValue'],
|
||||
data() {
|
||||
return {
|
||||
paymentMethodListButton
|
||||
paymentMethodListButton: markRaw(paymentMethodListButton)
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
|
|||
|
|
@ -45,18 +45,23 @@ function setupMocks({ customMountOptions = {}, queryString }, mainInitialState =
|
|||
}
|
||||
|
||||
return 'false';
|
||||
})
|
||||
}),
|
||||
savePageDataToStore: jest.fn()
|
||||
}
|
||||
};
|
||||
const cmsMixin = {
|
||||
methods: {
|
||||
getCmsContent: jest.fn()
|
||||
}
|
||||
}
|
||||
|
||||
mountOptions.global.plugins = [testingPinia];
|
||||
mountOptions.global.mixins = [mockMixin];
|
||||
mountOptions.mixins = [mockMixin, cmsMixin];
|
||||
|
||||
const wrapper = shallowMount(paymentMethod, mountOptions);
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
const store = useMainStore();
|
||||
const defaultState = getDefaultState();
|
||||
|
|
@ -80,33 +85,10 @@ describe('payment-method.vue', () => {
|
|||
const wrapper = setupMocks({}, store);
|
||||
|
||||
// Act
|
||||
const paymethod = wrapper.vm.getPaymentMethodFromStore();
|
||||
wrapper.vm.initializeComponent();
|
||||
|
||||
// Assert
|
||||
expect(paymethod).toBe(payLaterMethod);
|
||||
});
|
||||
test('getting payment method when method is pay in advance', async () => {
|
||||
// Arrange
|
||||
const payInAdvanceMethod = paymentMethods.CREDIT_CARD;
|
||||
const store = {
|
||||
order: {
|
||||
payment: {
|
||||
paymentMethod: payInAdvanceMethod
|
||||
},
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.VERIFIED,
|
||||
coverageType: coverageType.Deductible
|
||||
},
|
||||
currentDeductible: 1000
|
||||
}
|
||||
};
|
||||
const wrapper = setupMocks({}, store);
|
||||
|
||||
// Act
|
||||
const paymethod = wrapper.vm.getPaymentMethodFromStore();
|
||||
|
||||
// Assert
|
||||
expect(paymethod).toBe(payInAdvanceMethod);
|
||||
expect(wrapper.vm.paymentMethod).toBe(payLaterMethod);
|
||||
});
|
||||
test('getting PAY_AT_TIME_OF_SERVICE when deductible is 0', async () => {
|
||||
// Arrange
|
||||
|
|
@ -120,16 +102,18 @@ describe('payment-method.vue', () => {
|
|||
coverageStatus: coverageStatuses.VERIFIED,
|
||||
coverageType: coverageType.Deductible
|
||||
},
|
||||
currentDeductible: 0
|
||||
currentDeductible: {
|
||||
replace: 0
|
||||
}
|
||||
}
|
||||
};
|
||||
const wrapper = setupMocks({}, store);
|
||||
|
||||
// Act
|
||||
const paymethod = wrapper.vm.getPaymentMethodFromStore();
|
||||
wrapper.vm.initializeComponent();
|
||||
|
||||
// Assert
|
||||
expect(paymethod).toBe(payInAdvanceMethod);
|
||||
expect(wrapper.vm.paymentMethod).toBe(payInAdvanceMethod);
|
||||
});
|
||||
test('getting PAY_AT_TIME_OF_SERVICE when Unverified', async () => {
|
||||
// Arrange
|
||||
|
|
@ -143,16 +127,18 @@ describe('payment-method.vue', () => {
|
|||
coverageStatus: coverageStatuses.PENDING,
|
||||
coverageType: coverageType.Deductible
|
||||
},
|
||||
currentDeductible: 1000
|
||||
currentDeductible: {
|
||||
replace: 1000
|
||||
}
|
||||
}
|
||||
};
|
||||
const wrapper = setupMocks({}, store);
|
||||
|
||||
// Act
|
||||
const paymethod = wrapper.vm.getPaymentMethodFromStore();
|
||||
wrapper.vm.initializeComponent();
|
||||
|
||||
// Assert
|
||||
expect(paymethod).toBe(payInAdvanceMethod);
|
||||
expect(wrapper.vm.paymentMethod).toBe(payInAdvanceMethod);
|
||||
});
|
||||
test('getting PAY_AT_TIME_OF_SERVICE when PIA Experiment disabled', async () => {
|
||||
// Arrange
|
||||
|
|
@ -176,16 +162,18 @@ describe('payment-method.vue', () => {
|
|||
coverageStatus: coverageStatuses.VERIFIED,
|
||||
coverageType: coverageType.Deductible
|
||||
},
|
||||
currentDeductible: 1000
|
||||
currentDeductible: {
|
||||
replace: 1000
|
||||
}
|
||||
}
|
||||
};
|
||||
const wrapper = setupMocks({}, store, mixin);
|
||||
|
||||
// Act
|
||||
const paymethod = wrapper.vm.getPaymentMethodFromStore();
|
||||
wrapper.vm.initializeComponent();
|
||||
|
||||
// Assert
|
||||
expect(paymethod).toBe(payInAdvanceMethod);
|
||||
expect(wrapper.vm.paymentMethod).toBe(payInAdvanceMethod);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -10,23 +10,32 @@
|
|||
ref="siteHeader"
|
||||
cmsWidgetName="SiteHeaderWidget" />
|
||||
</div>
|
||||
<div class="iss-heritage-container-width">
|
||||
<div class="payment-method-container iss-heritage-content-container-width">
|
||||
<div class="iss-heritage-container-width payment-method-container row">
|
||||
<div class="order-info-container col-md-7">
|
||||
<alert
|
||||
class=""
|
||||
alertClass="alert-warning"
|
||||
cmsWidgetName="AlertIncompleteWidget" />
|
||||
<siteSubHeader
|
||||
class="mb-5 mt-4 px-3"
|
||||
cmsWidgetName="SiteSubHeaderWidget"
|
||||
secondaryTextClasses="text-center"
|
||||
subHeaderClasses="mt-4" />
|
||||
class="subheader"
|
||||
cmsWidgetName="SiteSubHeaderWidget" />
|
||||
<div class="main-content-container">
|
||||
<hr class="my-0" />
|
||||
<reviewDropdown ref="reviewDropdown" />
|
||||
<hr class="my-0" />
|
||||
<cartDropdown
|
||||
:showAsPaid="false"
|
||||
:readOnly="false"
|
||||
recyclingModalCmsWidgetName="RecycleModal"
|
||||
servicePackageTitleWidgetName="ServicePackageTitle" />
|
||||
<hr class="mt-0 mb-5" />
|
||||
<hr class="section-divider" />
|
||||
<reviewDropdown
|
||||
ref="reviewDropdown"
|
||||
@edit-clicked="handleEditClicked"
|
||||
/>
|
||||
<div class="cart-header">
|
||||
<span class="cart-label">{{ cartHeaderText }}</span>
|
||||
<span v-if="showCartTotal" class="cart-value">{{ cartTotal }}</span>
|
||||
</div>
|
||||
<div class="cart-dropdown-container">
|
||||
<cartDropdown
|
||||
:showAsPaid="false"
|
||||
:readOnly="false"
|
||||
recyclingModalCmsWidgetName="RecycleModal"
|
||||
servicePackageTitleWidgetName="ServicePackageTitle" />
|
||||
</div>
|
||||
<alert
|
||||
v-if="displayPayInAdvanceAlert"
|
||||
name="payInAdvanceErrorAlert"
|
||||
|
|
@ -34,27 +43,79 @@
|
|||
cmsWidgetName="PayInAdvanceErrorAlertWidget"
|
||||
alertClass="alert-danger"
|
||||
:isDismissible="false" />
|
||||
<hr
|
||||
v-if="!isPayInAdvanceDisabled"
|
||||
class="pia-divider" />
|
||||
<paymentMethodQuestion
|
||||
v-if="!isPayInAdvanceDisabled"
|
||||
v-model="paymentMethod"
|
||||
:cmsWidgetName="paymentMethodWidgetName"
|
||||
:validationRules="rules.optionRequired" />
|
||||
<alert
|
||||
v-if="isPayInAdvanceDisabled"
|
||||
:isDismissible="false"
|
||||
alertClass="alert-info"
|
||||
cmsWidgetName="NoPayInAdvanceDisclaimerWidget"
|
||||
:shouldScrollToOnMount="false" />
|
||||
<div
|
||||
v-if="!hideSMSOptIn"
|
||||
class="sms-opt-in-section">
|
||||
<hr class="sms-divider" />
|
||||
<div class="sms-opt-in-header">{{ smsOptInHeaderText }}</div>
|
||||
<buttonQuestion
|
||||
class="sms-opt-in-question"
|
||||
v-model="smsOptIn"
|
||||
groupName="sms-opt-in"
|
||||
buttonTypeString="listButtonHorizontal"
|
||||
:questionText="smsOptinQuestionText"
|
||||
:answers="smsOptinQuestionAnswers"
|
||||
isRequired
|
||||
:validationRules="rules.optionRequired" />
|
||||
<textboxQuestion
|
||||
v-if="smsOptIn === 'Yes'"
|
||||
v-model="smsPhoneNumber"
|
||||
inputId="phoneNumber"
|
||||
:cmsWidgetName="widget.smsPhoneNumber"
|
||||
isRequired
|
||||
:mask="phoneMask"
|
||||
placeholderText="###-###-####"
|
||||
disableAutoFill
|
||||
:validationRules="rules.phoneNumber" />
|
||||
</div>
|
||||
<siteFooter
|
||||
ref="siteFooter"
|
||||
cmsWidgetName="SiteFooterWidget"
|
||||
:isForwardActionDisabled="!meta.valid"
|
||||
:isStackedVertically="true"
|
||||
@backClicked="backButtonAction"
|
||||
@ForwardClicked="forwardButtonAction" />
|
||||
<div
|
||||
v-if="!hideSMSOptIn"
|
||||
class="disclaimer">
|
||||
<textBlock :cmsWidgetName="widget.smsDisclaimer" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="offer-container col-lg-4 offset-lg-1 col-md-5 d-none d-md-flex">
|
||||
<div
|
||||
v-if="offerWipers"
|
||||
class="wiper-offer-panel">
|
||||
<div class="wiper-logo">
|
||||
<img
|
||||
:src="wiperOfferPanel.image"
|
||||
alt="Wiper Offer Logo" />
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="wiper-header">{{ wiperOfferPanel.header }}</div>
|
||||
<div class="wiper-body">{{ wiperOfferPanel.body }}</div>
|
||||
</div>
|
||||
<div class="panel-footer">
|
||||
<buttonMain
|
||||
class="edit-wiper-offer-button"
|
||||
:buttonText="wiperOfferPanel.edit"
|
||||
@click="handleEditClicked('wipers')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<contactDetailsDrawer
|
||||
ref="contactDetailsDrawer"
|
||||
class="contact-details-drawer"
|
||||
@updateContactDetails="refreshContactDetails"
|
||||
@closeWithoutSaving="closeContactDetailsWithoutSaving" />
|
||||
</div>
|
||||
</Form>
|
||||
</template>
|
||||
|
|
@ -69,6 +130,11 @@ import reviewDropdown from '@/layouts/payment-method/review-dropdown/review-drop
|
|||
import cartDropdown from '@/iss-components/cart-dropdown/cart-dropdown.vue';
|
||||
import paymentMethodQuestion from '@/layouts/payment-method/payment-method-question/payment-method-question.vue';
|
||||
import alert from '@/ux-components/alert/alert.vue';
|
||||
import textBlock from '@/digital-components/text-block/text-block.vue';
|
||||
import buttonQuestion from '@/digital-components/button-question/button-question.vue';
|
||||
import textboxQuestion from '@/digital-components/textbox-question/textbox-question.vue';
|
||||
import contactDetailsDrawer from '@/iss-components/contact-details-drawer/contact-details-drawer.vue';
|
||||
import buttonMain from '@/ux-components/button-main/button-main.vue';
|
||||
|
||||
// Supporting Items
|
||||
import settleAllPromises from '@/helpers/layout-helper';
|
||||
|
|
@ -86,6 +152,10 @@ import { experimentSettings } from '@/constants/experiments';
|
|||
import submitType from '@/constants/submit-type';
|
||||
import routerParams from '@/router/router-constants/router-params';
|
||||
import { supportsApplePay } from '@/helpers/browser-helper';
|
||||
import { getCartTotal } from '@/helpers/cart-helper';
|
||||
import { formatAmountInDollars } from '@/helpers/text-helper';
|
||||
import widgetFields from '@/constants/cms-widget-fields';
|
||||
import MaskaFormattedMasks from '@/constants/maska-masks';
|
||||
|
||||
export default {
|
||||
name: 'payment-method',
|
||||
|
|
@ -97,12 +167,18 @@ export default {
|
|||
reviewDropdown,
|
||||
cartDropdown,
|
||||
paymentMethodQuestion,
|
||||
alert
|
||||
alert,
|
||||
textBlock,
|
||||
buttonQuestion,
|
||||
textboxQuestion,
|
||||
contactDetailsDrawer,
|
||||
buttonMain
|
||||
},
|
||||
mixins: [baseFormMixin],
|
||||
async beforeRouteEnter(to, from, next) {
|
||||
// Call APIs
|
||||
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
|
||||
const pageData = useMainStore().pageData(to.query.issPage);
|
||||
|
||||
const promiseResultMap = [
|
||||
{
|
||||
|
|
@ -117,7 +193,7 @@ export default {
|
|||
|
||||
next((vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
vm.initializeComponent();
|
||||
vm.initializeComponent(pageData);
|
||||
vm.updateFooterButtonText(vm.customCallToActionButtonCopy);
|
||||
});
|
||||
},
|
||||
|
|
@ -126,20 +202,29 @@ export default {
|
|||
paymentMethod: null,
|
||||
widget: {
|
||||
paymentMethod: 'PaymentMethodWidget',
|
||||
paymentMethodApplePay: 'PaymentMethodWidgetApplePay'
|
||||
paymentMethodApplePay: 'PaymentMethodWidgetApplePay',
|
||||
amountDue: 'AmountDueCartHeaderTextWidget',
|
||||
payAtAppointment: 'PayAtAppointmentTextWidget',
|
||||
smsOptInHeader: 'SMSOptInHeaderWidget',
|
||||
smsOptInQuestion: 'SMSOptInQuestionWidget',
|
||||
smsPhoneNumber: 'SMSPhoneNumberWidget',
|
||||
smsDisclaimer: 'SMSDisclaimerWidget',
|
||||
wiperOffer: 'WiperOfferPanelWidget'
|
||||
},
|
||||
rules: {
|
||||
optionRequired: globalRules.OPTION_REQUIRED
|
||||
}
|
||||
optionRequired: globalRules.OPTION_REQUIRED,
|
||||
phoneNumber: `${globalRules.PHONE_NUMBER_REQUIRED}|${globalRules.PHONE_NUMBER_FORMAT}`
|
||||
},
|
||||
smsOptIn: '',
|
||||
smsPhoneNumber: ''
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
customCallToActionButtonCopy() {
|
||||
switch (this.paymentMethod) {
|
||||
case paymentMethods.PayNow:
|
||||
return 'Continue to checkout';
|
||||
case paymentMethods.AFTERPAY:
|
||||
return 'Continue to Afterpay';
|
||||
return 'Continue to checkout';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
@ -156,6 +241,60 @@ export default {
|
|||
},
|
||||
paymentMethodWidgetName() {
|
||||
return supportsApplePay() ? this.widget.paymentMethodApplePay : this.widget.paymentMethod;
|
||||
},
|
||||
showCartTotal() {
|
||||
return this.mainStore.isVerified && (this.mainStore.isITAC || this.mainStore.isNoComp
|
||||
|| this.mainStore.order.lineItems?.vaps?.length > 0);
|
||||
},
|
||||
cartHeaderText() {
|
||||
if (this.isPayInAdvanceDisabled) {
|
||||
return this.getCmsContent(
|
||||
this.widget.payAtAppointment,
|
||||
widgetFields.TEXT_BLOCK_WIDGET.TEXT
|
||||
);
|
||||
} else {
|
||||
return this.getCmsContent(
|
||||
this.widget.amountDue,
|
||||
widgetFields.TEXT_BLOCK_WIDGET.TEXT
|
||||
);
|
||||
}
|
||||
},
|
||||
cartTotal() {
|
||||
return formatAmountInDollars(getCartTotal(this.mainStore.order));
|
||||
},
|
||||
hideSMSOptIn() {
|
||||
return false;
|
||||
},
|
||||
smsOptInHeaderText() {
|
||||
return this.getCmsContent(
|
||||
this.widget.smsOptInHeader,
|
||||
widgetFields.TEXT_BLOCK_WIDGET.TEXT
|
||||
);
|
||||
},
|
||||
smsOptinQuestionText() {
|
||||
return this.getCmsContent(this.widget.smsOptInQuestion, widgetFields.INPUT_QUESTION_WIDGET.QUESTION_TEXT);
|
||||
},
|
||||
smsOptinQuestionAnswers() {
|
||||
return this.getCmsContent(this.widget.smsOptInQuestion, widgetFields.INPUT_QUESTION_WIDGET.ANSWERS) || [];
|
||||
},
|
||||
phoneMask() {
|
||||
return MaskaFormattedMasks.PHONE_NUMBER;
|
||||
},
|
||||
offerWipers() {
|
||||
return !this.mainStore.lineItems.vaps?.some((part) => part.partType.toLowerCase().includes('wiper'));
|
||||
},
|
||||
wiperOfferPanel() {
|
||||
const wiperImage = this.getCmsContent(this.widget.wiperOffer, widgetFields.CONTENT_GROUP_WIDGET.IMAGE);
|
||||
const wiperHeader = this.getCmsContent(this.widget.wiperOffer, widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT);
|
||||
const wiperBody = this.getCmsContent(this.widget.wiperOffer, widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT);
|
||||
const wiperEdit = this.getCmsContent(this.widget.wiperOffer, widgetFields.CONTENT_GROUP_WIDGET.FOOTER_TEXT);
|
||||
|
||||
return {
|
||||
image: wiperImage,
|
||||
header: wiperHeader,
|
||||
body: wiperBody,
|
||||
edit: wiperEdit
|
||||
};
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
|
@ -244,41 +383,40 @@ export default {
|
|||
&& customerReqs
|
||||
&& contactInfoReqs);
|
||||
},
|
||||
initializeComponent() {
|
||||
this.paymentMethod = this.getPaymentMethodFromStore();
|
||||
},
|
||||
getPaymentMethodFromStore() {
|
||||
if (this.isPayInAdvanceDisabled) {
|
||||
return paymentMethods.PAY_AT_TIME_OF_SERVICE;
|
||||
initializeComponent(pageData) {
|
||||
if (pageData) {
|
||||
this.smsOptIn = pageData.smsOptIn;
|
||||
}
|
||||
this.smsPhoneNumber = this.getSMSPhoneFromStore();
|
||||
if (this.isPayInAdvanceDisabled) {
|
||||
console.log('Pay in Advance is disabled, defaulting to Pay at Time of Service');
|
||||
this.paymentMethod = paymentMethods.PAY_AT_TIME_OF_SERVICE;
|
||||
}
|
||||
|
||||
return useMainStore().order.payment.paymentMethod;
|
||||
},
|
||||
updateFooterButtonText(newValue) {
|
||||
this.$refs.siteFooter.updateButtonText(newValue);
|
||||
},
|
||||
async forwardButtonAction() {
|
||||
useMainStore().savePaymentMethodChoice(this.paymentMethod);
|
||||
if (this.paymentMethod === paymentMethods.PAY_AT_TIME_OF_SERVICE) {
|
||||
try {
|
||||
await submitWorkOrder({ submitType: submitType.SAFELITE });
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD,
|
||||
this.$route
|
||||
);
|
||||
} catch (error) {
|
||||
useMainStore().setBailout(bailoutMessage.saveSessionError(error.data));
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.SAVE_SESSION_FAILED,
|
||||
this.$route,
|
||||
{ issPage: issPageValues.PAYMENT_METHOD }
|
||||
);
|
||||
console.error(`error: response from submit work order:${error.message}`);
|
||||
this.mainStore.savePaymentMethodChoice(this.paymentMethod);
|
||||
this.savePageDataToStore(issPageValues.PAYMENT_METHOD, { smsOptIn: this.smsOptIn });
|
||||
if (!this.hideSMSOptIn) {
|
||||
const requestTextUpdates = this.smsOptIn === 'Yes';
|
||||
this.mainStore.updateContactInfo({ requestTextUpdates });
|
||||
if (requestTextUpdates) {
|
||||
this.mainStore.updatePhoneNumbers({ alternative: this.smsPhoneNumber, service: this.smsPhoneNumber });
|
||||
}
|
||||
}
|
||||
if (this.paymentMethod === paymentMethods.PAY_AT_TIME_OF_SERVICE) {
|
||||
await submitWorkOrder({ submitType: submitType.SAFELITE });
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD,
|
||||
this.$route
|
||||
);
|
||||
} else {
|
||||
await saveSession({
|
||||
createWorkOrderNumberForPIA: true,
|
||||
shouldAwaitSaveSessionQueue: true
|
||||
shouldAwaitSaveSessionQueue: true,
|
||||
bailoutOnError: true
|
||||
});
|
||||
|
||||
this.$router.navigate(
|
||||
|
|
@ -293,10 +431,53 @@ export default {
|
|||
const scenario = this.mainStore.isMobileAppointment
|
||||
? this.navigationScenarios.CLICKED_BACK_MOBILE
|
||||
: this.navigationScenarios.CLICKED_BACK_INSHOP;
|
||||
this.$router.navigate(
|
||||
this.$router.navigateWithSpinner(
|
||||
scenario,
|
||||
this.$route
|
||||
);
|
||||
},
|
||||
getSMSPhoneFromStore() {
|
||||
return this.mainStore.contactInfo.alternativePhone ?? this.mainStore.contactInfo.servicePhone;
|
||||
},
|
||||
handleEditClicked(section) {
|
||||
switch (section) {
|
||||
case 'location':
|
||||
this.$router.navigateWithSpinner(
|
||||
this.navigationScenarios.EDIT_SERVICE_LOCATION,
|
||||
this.$route
|
||||
);
|
||||
break;
|
||||
case 'schedule':
|
||||
this.$router.navigateWithSpinner(
|
||||
this.navigationScenarios.EDIT_SCHEDULE,
|
||||
this.$route
|
||||
);
|
||||
break;
|
||||
case 'vehicle':
|
||||
this.$router.navigateWithSpinner(
|
||||
this.navigationScenarios.EDIT_VEHICLE,
|
||||
this.$route
|
||||
);
|
||||
break;
|
||||
case 'customer':
|
||||
this.openContactDetailsModal();
|
||||
break;
|
||||
case 'wipers':
|
||||
this.$router.navigateWithSpinner(
|
||||
this.navigationScenarios.EDIT_WIPERS,
|
||||
this.$route
|
||||
);
|
||||
break;
|
||||
}
|
||||
},
|
||||
openContactDetailsModal() {
|
||||
this.$refs.contactDetailsDrawer.openModal();
|
||||
},
|
||||
refreshContactDetails() {
|
||||
this.smsPhoneNumber = this.mainStore.contactInfo.servicePhone;
|
||||
},
|
||||
closeContactDetailsWithoutSaving() {
|
||||
this.smsOptIn = 'No';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -304,20 +485,145 @@ export default {
|
|||
|
||||
<style lang="scss" scoped>
|
||||
$page-side-padding: 1.5rem;
|
||||
.iss-heritage-container-width {
|
||||
.payment-method-container {
|
||||
position: relative;
|
||||
min-height: 1px;
|
||||
padding-left: .9375rem;
|
||||
padding-right: .9375rem;
|
||||
#app .iss-heritage-container-width.payment-method-container {
|
||||
.alert-warning {
|
||||
margin-top: 1.25rem;
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
|
||||
.subheader {
|
||||
margin-top: 1.25rem;
|
||||
|
||||
:deep(strong) {
|
||||
color: $black;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
:deep(.subheader-secondary) {
|
||||
margin-top: .625rem;
|
||||
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
color: #4d4e53;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.section-divider {
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 1.875rem;
|
||||
}
|
||||
|
||||
.cart-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: .25rem;
|
||||
margin-bottom: .625rem;
|
||||
|
||||
.cart-label {
|
||||
color: $black;
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
|
||||
.cart-value {
|
||||
color: $green;
|
||||
font-weight: $font-weight-bold;
|
||||
font-size: 1.625rem;
|
||||
}
|
||||
}
|
||||
|
||||
.cart-dropdown-container {
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.pia-divider {
|
||||
margin-top: .5rem;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.sms-divider {
|
||||
margin-top: 1.75rem;
|
||||
margin-bottom: 1.875rem;
|
||||
}
|
||||
|
||||
.sms-opt-in-section {
|
||||
margin-bottom: 1.875rem;
|
||||
}
|
||||
|
||||
.sms-opt-in-header {
|
||||
color: $black;
|
||||
font-weight: $font-weight-bold;
|
||||
margin-bottom: 1.125rem;
|
||||
}
|
||||
|
||||
.sms-opt-in-question {
|
||||
margin-bottom: 1.25rem;
|
||||
|
||||
:deep(.question-text) {
|
||||
margin-bottom: .625rem;
|
||||
}
|
||||
}
|
||||
|
||||
.disclaimer {
|
||||
margin-bottom: 1.875rem;
|
||||
font-weight: $font-weight-light;
|
||||
font-size: .8125rem;
|
||||
line-height: 1.54;
|
||||
color: #4d4e53;
|
||||
|
||||
:deep(a) {
|
||||
color: $heritage-blue-primary;
|
||||
text-decoration: none;
|
||||
font-weight: $font-weight-bold;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: $heritage-blue-secondary;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.contact-details-drawer {
|
||||
:deep(strong) {
|
||||
color: $black;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.wiper-offer-panel {
|
||||
margin-top: 1.25rem;
|
||||
margin-bottom: 1.375rem;
|
||||
box-shadow: 0 0 .625rem 1px #cacbcc;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: .25rem;
|
||||
max-height: fit-content;
|
||||
|
||||
.wiper-logo {
|
||||
margin: 1.25rem 2.25rem 0 2.25rem;
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
padding: 0 1rem 1.25rem 1rem;
|
||||
|
||||
.wiper-header {
|
||||
margin: 1.25rem 0 .625rem 0;
|
||||
color: $black;
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
}
|
||||
|
||||
.panel-footer {
|
||||
padding: 0 1rem 1.25rem 1rem;
|
||||
|
||||
.edit-wiper-offer-button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-content-container {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.page-container-grouped-styles {
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,34 +1,55 @@
|
|||
<template>
|
||||
<div class="py-3">
|
||||
<div class="block-container">
|
||||
<div class="d-flex">
|
||||
<textBlock
|
||||
:cmsWidgetName="headerCmsWidgetName"
|
||||
:customText="customHeaderText"
|
||||
typeStyle="body small dark"
|
||||
typeStyle="body dark"
|
||||
fontWeight="bold"
|
||||
:marginTopSizeOverride="0" />
|
||||
</div>
|
||||
<div
|
||||
v-for="item in content"
|
||||
:key="item"
|
||||
class="small review-block-content"
|
||||
data-test="contentLine"
|
||||
v-html="item"></div>
|
||||
<div class="review-block-body-container">
|
||||
<div
|
||||
v-for="item in content"
|
||||
:key="item"
|
||||
class="review-block-content"
|
||||
data-test="contentLine"
|
||||
v-html="item"></div>
|
||||
<div
|
||||
class="link-container"
|
||||
:class="{ 'expanded': linkVisible }">
|
||||
<textLink
|
||||
linkType="text"
|
||||
:text="editLinkText"
|
||||
useLoadingModal
|
||||
href="javascript:void(0)"
|
||||
class="link"
|
||||
@clickEvent="linkClicked">
|
||||
</textLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import textBlock from '@/digital-components/text-block/text-block.vue';
|
||||
import textLink from '@/ux-components/text-link/text-link.vue';
|
||||
|
||||
export default {
|
||||
name: 'review-block',
|
||||
components: {
|
||||
textBlock
|
||||
textBlock,
|
||||
textLink
|
||||
},
|
||||
props: {
|
||||
headerCmsWidgetName: String,
|
||||
customHeaderText: String,
|
||||
content: Array // Specifically an array of strings.
|
||||
content: Array, // Specifically an array of strings.
|
||||
editLinkText: {
|
||||
type: String,
|
||||
default: 'Edit'
|
||||
},
|
||||
linkVisible: Boolean
|
||||
},
|
||||
emits: ['edit-clicked'],
|
||||
data() {
|
||||
|
|
@ -51,7 +72,17 @@ export default {
|
|||
:deep(.review-block-content > ul) {
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
.review-block-content {
|
||||
width: 90%;
|
||||
.review-block-body-container {
|
||||
margin-top: .625rem;
|
||||
}
|
||||
.link-container {
|
||||
max-height: 0;
|
||||
transition: $transition-swing;
|
||||
overflow: hidden;
|
||||
|
||||
&.expanded {
|
||||
margin-top: .625rem;
|
||||
max-height: 2rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,45 +1,39 @@
|
|||
<template>
|
||||
<div :class="[isExpanded ? 'pb-3' : 'pb-4']">
|
||||
<div>
|
||||
<div
|
||||
class="row review-toggle flex align-items-center pt-4"
|
||||
class="review-toggle d-flex align-items-center justify-content-between"
|
||||
:class="[isExpanded ? 'expanded' : '']"
|
||||
@click="toggleIsExpanded">
|
||||
<a
|
||||
aria-label="expand appointment details"
|
||||
href="javascript:void(0)"
|
||||
class="col d-flex justify-content-between py-0">
|
||||
class="d-flex py-0">
|
||||
<span class="label">Appointment details</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="review-table px-4">
|
||||
<vehicleReview
|
||||
cmsWidgetName="VehicleReviewWidget"
|
||||
:vehicle="vehicleInfo" />
|
||||
<hr class="my-0" />
|
||||
<damageReview
|
||||
cmsWidgetName="DamageReviewWidget"
|
||||
damageLocationsWidgetName="DamageLocationsWidget"
|
||||
:damage="damageInfo" />
|
||||
<hr class="my-0" />
|
||||
<servicePackageReview
|
||||
ref="servicePackageReview"
|
||||
servicePackageOptionsCmsName="ServicePackageTitle"
|
||||
defaultPackageItemsCmsName="DefaultPackageItemDescriptions"
|
||||
vapsItemsCmsName="VapsItemDescriptions"
|
||||
:damage="damageInfo"
|
||||
:lineItems="lineItems" />
|
||||
<hr class="my-0" />
|
||||
<div class="review-table">
|
||||
<serviceLocationReview
|
||||
cmsWidgetName="ServiceLocationTitleWidget"
|
||||
:serviceLocation="serviceLocationInfo" />
|
||||
<hr class="my-0" />
|
||||
:linkVisible="isExpanded"
|
||||
cmsWidgetName="ServiceLocationWidget"
|
||||
:serviceLocation="serviceLocationInfo"
|
||||
@edit-clicked="editServiceLocation" />
|
||||
<scheduleReview
|
||||
:linkVisible="isExpanded"
|
||||
cmsWidgetName="ScheduleWidget"
|
||||
:appointmentType="appointmentType" />
|
||||
<hr class="my-0" />
|
||||
<customerReview
|
||||
cmsWidgetName="CustomerReviewWidget"
|
||||
:customer="customerInfo" />
|
||||
@edit-clicked="editSchedule" />
|
||||
<hr class="section-divider" />
|
||||
<div class="review-table-expander">
|
||||
<vehicleReview
|
||||
:linkVisible="true"
|
||||
cmsWidgetName="VehicleReviewWidget"
|
||||
@edit-clicked="editVehicle" />
|
||||
<hr class="section-divider" />
|
||||
<customerReview
|
||||
:linkVisible="true"
|
||||
cmsWidgetName="CustomerReviewWidget"
|
||||
@edit-clicked="editCustomer" />
|
||||
<hr class="section-divider" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -47,26 +41,18 @@
|
|||
<script>
|
||||
// import textBlock from '@/digital-components/text-block/text-block.vue';
|
||||
import customerReview from '@/layouts/payment-method/review-dropdown/review-sections/customer-review/customer-review.vue';
|
||||
import damageReview from '@/layouts/payment-method/review-dropdown/review-sections/damage-review/damage-review.vue';
|
||||
import scheduleReview from '@/layouts/payment-method/review-dropdown/review-sections/schedule-review/schedule-review.vue';
|
||||
import
|
||||
serviceLocationReview from '@/layouts/payment-method/review-dropdown/review-sections/service-location-review/service-location-review.vue';
|
||||
import
|
||||
servicePackageReview
|
||||
from '@/layouts/payment-method/review-dropdown/review-sections/service-package-review/service-package-review.vue';
|
||||
import serviceLocationReview from '@/layouts/payment-method/review-dropdown/review-sections/service-location-review/service-location-review.vue';
|
||||
import vehicleReview from '@/layouts/payment-method/review-dropdown/review-sections/vehicle-review/vehicle-review.vue';
|
||||
|
||||
import { useMainStore } from '@/store';
|
||||
import settleAllPromises from '@/helpers/layout-helper';
|
||||
|
||||
export default {
|
||||
name: 'review-dropdown',
|
||||
components: {
|
||||
customerReview,
|
||||
damageReview,
|
||||
scheduleReview,
|
||||
serviceLocationReview,
|
||||
servicePackageReview,
|
||||
vehicleReview
|
||||
},
|
||||
props: {},
|
||||
|
|
@ -79,12 +65,6 @@ export default {
|
|||
vehicleInfo() {
|
||||
return useMainStore().vehicle;
|
||||
},
|
||||
damageInfo() {
|
||||
return useMainStore().damage;
|
||||
},
|
||||
lineItems() {
|
||||
return useMainStore().lineItems;
|
||||
},
|
||||
serviceLocationInfo() {
|
||||
return useMainStore().order.serviceLocation;
|
||||
},
|
||||
|
|
@ -95,9 +75,22 @@ export default {
|
|||
return useMainStore().contactInfo;
|
||||
}
|
||||
},
|
||||
emits: ['edit-clicked'],
|
||||
methods: {
|
||||
toggleIsExpanded() {
|
||||
this.isExpanded = !this.isExpanded;
|
||||
},
|
||||
editServiceLocation() {
|
||||
this.$emit('edit-clicked', 'location');
|
||||
},
|
||||
editSchedule() {
|
||||
this.$emit('edit-clicked', 'schedule');
|
||||
},
|
||||
editVehicle() {
|
||||
this.$emit('edit-clicked', 'vehicle');
|
||||
},
|
||||
editCustomer() {
|
||||
this.$emit('edit-clicked', 'customer');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -111,34 +104,37 @@ export default {
|
|||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.review-table {
|
||||
.review-table-expander {
|
||||
max-height: 0;
|
||||
transition: all 350ms ease-in;
|
||||
transition: $transition-swing;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.section-divider {
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 1.875rem;
|
||||
}
|
||||
|
||||
.review-toggle {
|
||||
margin-bottom: .625rem;
|
||||
flex-direction: row;
|
||||
&:after {
|
||||
content: "";
|
||||
transition: all 0.5s ease;
|
||||
background-image: url($svg-payment-method-review-toggle);
|
||||
transition: transform none;
|
||||
background-image: url(~@/assets/img/icons/chevron-circle_review_dropdown.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: right center;
|
||||
width: 16px;
|
||||
height: 9px;
|
||||
background-size: 1.25rem 1.25rem;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
right: 0.75rem;
|
||||
margin: 0.5rem 0 0.5rem 1rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
&.expanded:after {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
&.expanded + .review-table {
|
||||
max-height: 800px;
|
||||
transition: all 150ms ease-in;
|
||||
&.expanded + .review-table .review-table-expander {
|
||||
max-height: 24rem;
|
||||
overflow: hidden;
|
||||
visibility: visible;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,129 +0,0 @@
|
|||
// Components
|
||||
import customerReview from '@/layouts/payment-method/review-dropdown/review-sections/customer-review/customer-review.vue';
|
||||
|
||||
// Supporting Files
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
||||
|
||||
const testConstants = {
|
||||
cms: {
|
||||
header: {
|
||||
text: 'Header'
|
||||
},
|
||||
sms: {
|
||||
text: 'Sms'
|
||||
}
|
||||
},
|
||||
customerInfo: {
|
||||
firstName: 'First',
|
||||
lastName: 'Last',
|
||||
servicePhone: '111-111-1111',
|
||||
emailAddress: 'builddigitaltest@safelite.com',
|
||||
isSmsOptIn: false
|
||||
},
|
||||
displayContent: {
|
||||
fullName: 'First Last',
|
||||
phoneNumber: '111-111-1111',
|
||||
emailAddress: 'builddigitaltest@safelite.com',
|
||||
smsOptIn: 'Sms'
|
||||
}
|
||||
};
|
||||
|
||||
function generateDefaultProps() {
|
||||
return {
|
||||
cmsWidgetName: 'CustomerWidget',
|
||||
customer: {
|
||||
firstName: testConstants.customerInfo.firstName,
|
||||
lastName: testConstants.customerInfo.lastName,
|
||||
servicePhone: testConstants.customerInfo.servicePhone,
|
||||
emailAddress: testConstants.customerInfo.emailAddress,
|
||||
isSmsOptIn: testConstants.customerInfo.isSmsOptIn
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let cmsContent;
|
||||
const mockMixin = {
|
||||
methods: {
|
||||
getCmsContent: jest.fn((widgetName, cmsFieldName) => cmsContent?.[widgetName]?.[cmsFieldName] ?? '')
|
||||
}
|
||||
};
|
||||
|
||||
function getShallowMountedComponent(initialData = {}, methodToRun = () => {}) {
|
||||
const mountOptions = getMountOptions({
|
||||
router: {
|
||||
navigate: jest.fn()
|
||||
}
|
||||
});
|
||||
|
||||
methodToRun();
|
||||
|
||||
mountOptions.data = () => (
|
||||
initialData
|
||||
);
|
||||
|
||||
mountOptions.mixins = [mockMixin];
|
||||
|
||||
const wrapper = shallowMount(customerReview, mountOptions);
|
||||
return { wrapper };
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
cmsContent = {
|
||||
CustomerWidget: {
|
||||
HeaderText: testConstants.cms.header.text,
|
||||
SubheaderText: testConstants.cms.sms.text
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
describe('Customer Review Block', () => {
|
||||
test('Should display header text from cms', async () => {
|
||||
// Arrange
|
||||
const props = generateDefaultProps();
|
||||
const { wrapper } = getShallowMountedComponent({
|
||||
...props
|
||||
});
|
||||
|
||||
// Act
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.header).toEqual(testConstants.cms.header.text);
|
||||
});
|
||||
|
||||
test('Should display sms text from cms', async () => {
|
||||
// Arrange
|
||||
const props = generateDefaultProps();
|
||||
|
||||
const { wrapper } = getShallowMountedComponent({
|
||||
...props
|
||||
});
|
||||
|
||||
// Act
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.smsOptIn).toEqual(testConstants.cms.sms.text);
|
||||
});
|
||||
|
||||
test('Should render correct display content', async () => {
|
||||
// Arrange
|
||||
const props = generateDefaultProps();
|
||||
|
||||
const { wrapper } = getShallowMountedComponent({
|
||||
...props
|
||||
});
|
||||
|
||||
// Act
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.displayContent).toEqual([
|
||||
testConstants.displayContent.fullName,
|
||||
testConstants.displayContent.emailAddress,
|
||||
testConstants.displayContent.phoneNumber,
|
||||
testConstants.displayContent.smsOptIn
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
<template>
|
||||
<reviewBlock
|
||||
:customHeaderText="header"
|
||||
:content="displayContent" />
|
||||
:content="displayContent"
|
||||
:editLinkText="editLinkText" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
@ -13,30 +14,20 @@ export default {
|
|||
reviewBlock
|
||||
},
|
||||
props: {
|
||||
cmsWidgetName: String,
|
||||
customer: Object
|
||||
cmsWidgetName: String
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
computed: {
|
||||
displayContent() {
|
||||
return [this.fullName, this.email, this.phoneNumber, this.smsOptIn];
|
||||
return [this.getCmsContent(this.cmsWidgetName, 'BodyText')];
|
||||
},
|
||||
header() {
|
||||
return this.getCmsContent(this.cmsWidgetName, 'HeaderText');
|
||||
},
|
||||
fullName() {
|
||||
return `${this.customer?.firstName} ${this.customer?.lastName}`;
|
||||
},
|
||||
email() {
|
||||
return this.customer?.emailAddress;
|
||||
},
|
||||
phoneNumber() {
|
||||
return this.customer?.servicePhone;
|
||||
},
|
||||
smsOptIn() {
|
||||
return this.getCmsContent(this.cmsWidgetName, 'SubheaderText');
|
||||
editLinkText() {
|
||||
return this.getCmsContent(this.cmsWidgetName, 'FooterText');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,80 +0,0 @@
|
|||
// Components
|
||||
import damageReview from '@/layouts/payment-method/review-dropdown/review-sections/damage-review/damage-review.vue';
|
||||
|
||||
// Supporting Files
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import { useMainStore } from '@/store';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
||||
import { getDamageDisplayContent } from '@/helpers/damage-review-content-generator.js';
|
||||
import baseMixin from '@/mixins/base-mixin';
|
||||
|
||||
jest.mock('@/helpers/damage-review-content-generator.js', () => ({
|
||||
getDamageDisplayContent: jest.fn(),
|
||||
getLocationAnswer: jest.fn()
|
||||
}));
|
||||
|
||||
let cmsContent;
|
||||
const mockMixin = {
|
||||
methods: {
|
||||
...baseMixin.methods,
|
||||
getCmsContent: jest.fn((widgetName, cmsFieldName) => cmsContent?.[widgetName]?.[cmsFieldName] ?? '')
|
||||
}
|
||||
};
|
||||
|
||||
function getShallowMountedComponent(mainInitialState = {}, initialData = {}, methodToRun = () => {}) {
|
||||
const mountOptions = getMountOptions({
|
||||
router: {
|
||||
navigate: jest.fn()
|
||||
}
|
||||
});
|
||||
|
||||
const testingPinia = createTestingPinia({
|
||||
initialState: {
|
||||
main: mainInitialState
|
||||
}
|
||||
});
|
||||
useMainStore(testingPinia);
|
||||
methodToRun();
|
||||
|
||||
mountOptions.global.plugins = [testingPinia];
|
||||
mountOptions.data = () => (
|
||||
initialData
|
||||
);
|
||||
|
||||
mountOptions.mixins = [mockMixin];
|
||||
|
||||
const wrapper = shallowMount(damageReview, mountOptions);
|
||||
return { wrapper };
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
getDamageDisplayContent.mockClear();
|
||||
});
|
||||
|
||||
describe('Damage Review Block', () => {
|
||||
test('computed displayContent calls getDamageDisplayContent with expected', () => {
|
||||
// Arrange
|
||||
const glassToReplace = ['front-window', 'side-window'];
|
||||
const isRepair = false;
|
||||
const initialStore = {
|
||||
order: {
|
||||
damage: {
|
||||
glassToReplace,
|
||||
isRepair
|
||||
}
|
||||
}
|
||||
};
|
||||
const expected = ['a', 'v', 'n'];
|
||||
getDamageDisplayContent.mockImplementationOnce(() => expected);
|
||||
const { wrapper } = getShallowMountedComponent(initialStore, {});
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.displayContent;
|
||||
|
||||
// Assert
|
||||
expect(result).toStrictEqual(expected);
|
||||
expect(getDamageDisplayContent).toBeCalledTimes(1);
|
||||
expect(getDamageDisplayContent).toBeCalledWith([], [], [], glassToReplace, isRepair);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
<template>
|
||||
<reviewBlock
|
||||
:headerCmsWidgetName="cmsWidgetName"
|
||||
:content="displayContent" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import damageLocationsSelected from '@/constants/damage-locations-selected.js';
|
||||
import { useMainStore } from '@/store';
|
||||
import { getDamageDisplayContent, getLocationAnswer } from '@/helpers/damage-review-content-generator.js';
|
||||
import reviewBlock from '@/layouts/payment-method/review-dropdown/review-block/review-block.vue';
|
||||
|
||||
export default {
|
||||
name: 'damage-review',
|
||||
components: {
|
||||
reviewBlock
|
||||
},
|
||||
props: {
|
||||
cmsWidgetName: String,
|
||||
damageLocationsWidgetName: String,
|
||||
damage: Object
|
||||
},
|
||||
computed: {
|
||||
displayContent() {
|
||||
const { glassToReplace, isRepair } = useMainStore().order.damage;
|
||||
return getDamageDisplayContent(
|
||||
this.locationAnswers,
|
||||
this.driverSideDamageAnswers,
|
||||
this.passengerSideDamageAnswers,
|
||||
glassToReplace,
|
||||
isRepair
|
||||
);
|
||||
},
|
||||
driverSideDamageAnswers() {
|
||||
const answerContent = getLocationAnswer(damageLocationsSelected.DRIVER, this.locationAnswers);
|
||||
return this.getInputQuestionWidgetAnswersNullSafe(answerContent?.SubWidgetName);
|
||||
},
|
||||
passengerSideDamageAnswers() {
|
||||
const answerContent = getLocationAnswer(damageLocationsSelected.PASSENGER, this.locationAnswers);
|
||||
return this.getInputQuestionWidgetAnswersNullSafe(answerContent?.SubWidgetName);
|
||||
},
|
||||
locationAnswers() {
|
||||
return this.getInputQuestionWidgetAnswersNullSafe(this.damageLocationsWidgetName);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
<template>
|
||||
<reviewBlock
|
||||
:customHeaderText="headerText"
|
||||
:content="displayContent" />
|
||||
:content="displayContent"
|
||||
:editLinkText="editLinkText" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
@ -13,8 +14,7 @@ export default {
|
|||
reviewBlock
|
||||
},
|
||||
props: {
|
||||
cmsWidgetName: String,
|
||||
appointmentType: String
|
||||
cmsWidgetName: String
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
|
|
@ -28,6 +28,9 @@ export default {
|
|||
},
|
||||
headerText() {
|
||||
return this.getCmsContent(this.cmsWidgetName, 'HeaderText');
|
||||
},
|
||||
editLinkText() {
|
||||
return this.getCmsContent(this.cmsWidgetName, 'FooterText');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,135 +0,0 @@
|
|||
// Components
|
||||
import
|
||||
serviceLocationReview from '@/layouts/payment-method/review-dropdown/review-sections/service-location-review/service-location-review.vue';
|
||||
|
||||
// Supporting Files
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
||||
import { AppointmentTypeStrings } from '@/constants/schedule-constants';
|
||||
|
||||
const cmsContent = {
|
||||
ServiceLocationTitleWidget: {
|
||||
Text: 'Title Text'
|
||||
}
|
||||
};
|
||||
|
||||
const mockMixin = {
|
||||
methods: {
|
||||
getCmsContent: jest.fn((widgetName, cmsFieldName) => cmsContent?.[widgetName]?.[cmsFieldName] ?? '')
|
||||
}
|
||||
};
|
||||
|
||||
function generateDefaultProps() {
|
||||
return {
|
||||
cmsWidgetName: 'ServiceLocationTitleWidget',
|
||||
serviceLocation: {
|
||||
address: 'Mobile Address 1',
|
||||
address2: 'Mobile Address 2',
|
||||
city: 'Mobile City',
|
||||
state: 'MO',
|
||||
zipCode: '11111',
|
||||
zipCodeCtu: '',
|
||||
appointmentType: AppointmentTypeStrings.MOBILE,
|
||||
isVehicleProtected: false,
|
||||
provider: {
|
||||
providerNumber: '',
|
||||
address: {
|
||||
streetAddress: 'Service Location Address',
|
||||
city: 'Service Location City',
|
||||
state: 'SL',
|
||||
zipCode: '22222',
|
||||
zipCodeCtu: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getShallowMountedComponent(initialData = {}, methodToRun = () => {}) {
|
||||
const mountOptions = getMountOptions({
|
||||
router: {
|
||||
navigate: jest.fn()
|
||||
}
|
||||
});
|
||||
|
||||
methodToRun();
|
||||
|
||||
mountOptions.data = () => (
|
||||
initialData
|
||||
);
|
||||
|
||||
mountOptions.mixins = [mockMixin];
|
||||
|
||||
const wrapper = shallowMount(serviceLocationReview, mountOptions);
|
||||
return { wrapper };
|
||||
}
|
||||
|
||||
describe('Service Location Review Block', () => {
|
||||
test('Should render mobile address if mobile appointment', async () => {
|
||||
// Arrange
|
||||
const props = generateDefaultProps();
|
||||
const { wrapper } = getShallowMountedComponent({
|
||||
...props
|
||||
});
|
||||
|
||||
// Act
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.displayContent).toEqual([
|
||||
'Mobile Address 1, Mobile Address 2, Mobile City, MO 11111'
|
||||
]);
|
||||
});
|
||||
|
||||
test('Should render service location address if inshop appointment.', async () => {
|
||||
// Arrange
|
||||
const props = generateDefaultProps();
|
||||
props.serviceLocation.appointmentType = AppointmentTypeStrings.IN_SHOP;
|
||||
|
||||
const { wrapper } = getShallowMountedComponent({
|
||||
...props
|
||||
});
|
||||
|
||||
// Act
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.displayContent).toEqual([
|
||||
'Service Location Address, Service Location City, SL 22222'
|
||||
]);
|
||||
});
|
||||
|
||||
test('Should render service location address if drop-off appointment', async () => {
|
||||
// Arrange
|
||||
const props = generateDefaultProps();
|
||||
props.serviceLocation.appointmentType = AppointmentTypeStrings.DROP_OFF;
|
||||
|
||||
const { wrapper } = getShallowMountedComponent({
|
||||
...props
|
||||
});
|
||||
|
||||
// Act
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.displayContent).toEqual([
|
||||
'Service Location Address, Service Location City, SL 22222'
|
||||
]);
|
||||
});
|
||||
|
||||
test('Should not add comma or any text if address2 is null', async () => {
|
||||
// Arrange
|
||||
const props = generateDefaultProps();
|
||||
props.serviceLocation.address2 = null;
|
||||
|
||||
const { wrapper } = getShallowMountedComponent({
|
||||
...props
|
||||
});
|
||||
|
||||
// Act
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.displayContent).toEqual(['Mobile Address 1, Mobile City, MO 11111']);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,11 +1,13 @@
|
|||
<template>
|
||||
<reviewBlock
|
||||
:headerCmsWidgetName="cmsWidgetName"
|
||||
:content="displayContent" />
|
||||
:customHeaderText="headerText"
|
||||
:content="displayContent"
|
||||
:editLinkText="editLinkText" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { AppointmentTypeStrings } from '@/constants/schedule-constants';
|
||||
import { toTitleCase } from '@/helpers/text-helper';
|
||||
import reviewBlock from '@/layouts/payment-method/review-dropdown/review-block/review-block.vue';
|
||||
|
||||
export default {
|
||||
|
|
@ -21,11 +23,14 @@ export default {
|
|||
return {};
|
||||
},
|
||||
computed: {
|
||||
headerText() {
|
||||
return this.getCmsContent(this.cmsWidgetName, 'HeaderText');
|
||||
},
|
||||
displayContent() {
|
||||
return [
|
||||
`${this.addressInfo.address}${this.addressInfo.address2 ? ', ' : ''}${
|
||||
this.addressInfo.address2
|
||||
}, ${this.addressInfo.city}, ${this.addressInfo.state} ${this.addressInfo.zipCode}`
|
||||
}, ${toTitleCase(this.addressInfo.city)}, ${this.addressInfo.state} ${this.addressInfo.zipCode}`
|
||||
];
|
||||
},
|
||||
addressInfo() {
|
||||
|
|
@ -46,6 +51,9 @@ export default {
|
|||
state: this.serviceLocation?.provider?.address?.state,
|
||||
zipCode: this.serviceLocation?.provider?.address?.zipCode
|
||||
};
|
||||
},
|
||||
editLinkText() {
|
||||
return this.getCmsContent(this.cmsWidgetName, 'FooterText');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,965 +0,0 @@
|
|||
// Components
|
||||
import
|
||||
servicePackageReview from '@/layouts/payment-method/review-dropdown/review-sections/service-package-review/service-package-review.vue';
|
||||
|
||||
// Supporting Files
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
||||
import packageNames from '@/constants/package-names';
|
||||
import partTypeStrings from '@/constants/part-type-strings';
|
||||
import damageLocationsSelected from '@/constants/damage-locations-selected';
|
||||
import { useMainStore } from '@/store';
|
||||
|
||||
const testConstants = {
|
||||
cmsPropValues: {
|
||||
servicePackageOptionsCmsName: 'ServicePackageTitle',
|
||||
defaultPackageItemsCmsName: 'DefaultPackageItemDescriptions',
|
||||
vapsItemsCmsName: 'VapsItemDescriptions'
|
||||
},
|
||||
widgetNames: {
|
||||
tierOneTitle: 'EconomyServiceTitle',
|
||||
tierTwoTitle: 'StandardServiceTitle',
|
||||
tierThreeTitle: 'PremiumServiceTitle'
|
||||
},
|
||||
defaultItemCopy: {
|
||||
itemOne: 'Item Description 1',
|
||||
itemTwo: 'Item Description 2',
|
||||
itemThree: 'Item Description 3',
|
||||
itemFour: 'Item Description 4',
|
||||
defaultItemCopyArray: ['Item Description 1', 'Item Description 2', 'Item Description 3']
|
||||
},
|
||||
vapsCopy: {
|
||||
frontWiperCopy: 'Front Wiper copy',
|
||||
rearWiperCopy: 'Rear Wiper copy',
|
||||
rainDefenseCopy: 'Rain repel copy'
|
||||
},
|
||||
parts: {
|
||||
frontWiperPart: {
|
||||
partNumber: 'SBB16',
|
||||
description: 'SAFELITE BEAM BLADE 16',
|
||||
partType: 'FRONT WIPER',
|
||||
price: 32.64
|
||||
},
|
||||
rearWiperPart: {
|
||||
partNumber: 'SBBR12A',
|
||||
description: 'SAFELITE REAR BLADE 12A',
|
||||
partType: 'REAR WIPER',
|
||||
price: 24.48
|
||||
},
|
||||
rainDefensePart: {
|
||||
partNumber: 'RAIN REPEL',
|
||||
description: null,
|
||||
partType: 'RAIN REPEL',
|
||||
price: 35.5
|
||||
},
|
||||
recalPart: {
|
||||
partNumber: 'RECAL STATIC',
|
||||
Description: 'Recalibration',
|
||||
partType: 'recalibration',
|
||||
Quantity: '1',
|
||||
price: 150.0
|
||||
}
|
||||
},
|
||||
damages: {
|
||||
frontWindshield: {
|
||||
glassLocation: damageLocationsSelected.WINDSHIELD,
|
||||
glassName: damageLocationsSelected.SINGLE
|
||||
},
|
||||
rearWindshield: {
|
||||
glassLocation: damageLocationsSelected.REAR,
|
||||
glassName: damageLocationsSelected.STATIONARY
|
||||
},
|
||||
sideGlass: {
|
||||
glassLocation: damageLocationsSelected.PASSENGER,
|
||||
glassName: damageLocationsSelected.QUARTER
|
||||
}
|
||||
},
|
||||
imageId: '00000000-0000-0000-0000-000000000000'
|
||||
};
|
||||
|
||||
const figmaScenarios = [
|
||||
{
|
||||
name: '05_01_CSR_Quote_Cash',
|
||||
params: {
|
||||
wiperResponse: [testConstants.parts.frontWiperPart, testConstants.parts.rearWiperPart],
|
||||
rainDefenseResponse: testConstants.parts.rainDefensePart,
|
||||
damage: {
|
||||
isRepair: false,
|
||||
glassToReplace: [testConstants.damages.frontWindshield]
|
||||
},
|
||||
glassParts: [],
|
||||
supportingItems: []
|
||||
},
|
||||
iterations: [
|
||||
{
|
||||
name: 'Economy',
|
||||
vapsCombo: [],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
||||
displayContent: testConstants.defaultItemCopy.defaultItemCopyArray
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Standard',
|
||||
vapsCombo: [testConstants.parts.frontWiperPart],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierTwoTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.frontWiperCopy
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Premium',
|
||||
vapsCombo: [
|
||||
testConstants.parts.rainDefensePart,
|
||||
testConstants.parts.frontWiperPart
|
||||
],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierThreeTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.frontWiperCopy,
|
||||
testConstants.vapsCopy.rainDefenseCopy
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Standard+RearWiper',
|
||||
vapsCombo: [testConstants.parts.frontWiperPart, testConstants.parts.rearWiperPart],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierTwoTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.frontWiperCopy,
|
||||
testConstants.vapsCopy.rearWiperCopy
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '05_01_CSR_Quote_Standard_Repair',
|
||||
params: {
|
||||
wiperResponse: [testConstants.parts.frontWiperPart, testConstants.parts.rearWiperPart],
|
||||
rainDefenseResponse: testConstants.parts.rainDefensePart,
|
||||
damage: {
|
||||
isRepair: true,
|
||||
glassToReplace: []
|
||||
},
|
||||
glassParts: [],
|
||||
supportingItems: []
|
||||
},
|
||||
iterations: [
|
||||
{
|
||||
name: 'Economy',
|
||||
vapsCombo: [],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
||||
displayContent: testConstants.defaultItemCopy.defaultItemCopyArray
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Standard',
|
||||
vapsCombo: [testConstants.parts.frontWiperPart],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierTwoTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.frontWiperCopy
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Premium',
|
||||
vapsCombo: [
|
||||
testConstants.parts.rainDefensePart,
|
||||
testConstants.parts.frontWiperPart
|
||||
],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierThreeTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.frontWiperCopy,
|
||||
testConstants.vapsCopy.rainDefenseCopy
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Standard+RearWiper',
|
||||
vapsCombo: [testConstants.parts.frontWiperPart, testConstants.parts.rearWiperPart],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierTwoTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.frontWiperCopy,
|
||||
testConstants.vapsCopy.rearWiperCopy
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '05_01_CSR_Quote_Recal',
|
||||
params: {
|
||||
wiperResponse: [testConstants.parts.frontWiperPart, testConstants.parts.rearWiperPart],
|
||||
rainDefenseResponse: testConstants.parts.rainDefensePart,
|
||||
damage: {
|
||||
isRepair: false,
|
||||
glassToReplace: [testConstants.damages.frontWindshield]
|
||||
},
|
||||
glassParts: [],
|
||||
supportingItems: [testConstants.parts.recalPart]
|
||||
},
|
||||
iterations: [
|
||||
{
|
||||
name: 'Economy',
|
||||
vapsCombo: [],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
||||
displayContent: testConstants.defaultItemCopy.defaultItemCopyArray
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Standard',
|
||||
vapsCombo: [testConstants.parts.frontWiperPart],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierTwoTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.frontWiperCopy
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Premium',
|
||||
vapsCombo: [
|
||||
testConstants.parts.rainDefensePart,
|
||||
testConstants.parts.frontWiperPart
|
||||
],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierThreeTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.frontWiperCopy,
|
||||
testConstants.vapsCopy.rainDefenseCopy
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Standard+RearWiper',
|
||||
vapsCombo: [testConstants.parts.frontWiperPart, testConstants.parts.rearWiperPart],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierTwoTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.frontWiperCopy,
|
||||
testConstants.vapsCopy.rearWiperCopy
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
// 05_01_CSR_Quote_RearGlass omitted as a duplicate of below.
|
||||
{
|
||||
name: '05_01_CSR_Quote_RearGlass+NonWindshield',
|
||||
params: {
|
||||
wiperResponse: [testConstants.parts.frontWiperPart, testConstants.parts.rearWiperPart],
|
||||
rainDefenseResponse: testConstants.parts.rainDefensePart,
|
||||
damage: {
|
||||
isRepair: false,
|
||||
glassToReplace: [testConstants.damages.rearWindshield]
|
||||
},
|
||||
glassParts: [],
|
||||
supportingItems: []
|
||||
},
|
||||
iterations: [
|
||||
{
|
||||
name: 'Economy',
|
||||
vapsCombo: [],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
||||
displayContent: testConstants.defaultItemCopy.defaultItemCopyArray
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Economy+Frontwiper',
|
||||
vapsCombo: [testConstants.parts.frontWiperPart],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.frontWiperCopy
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Standard',
|
||||
vapsCombo: [testConstants.parts.rearWiperPart],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierTwoTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.rearWiperCopy
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Standard+RainDefense',
|
||||
vapsCombo: [testConstants.parts.rearWiperPart, testConstants.parts.rainDefensePart],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierTwoTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.rearWiperCopy,
|
||||
testConstants.vapsCopy.rainDefenseCopy
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Premium',
|
||||
vapsCombo: [testConstants.parts.rearWiperPart, testConstants.parts.frontWiperPart],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierThreeTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.frontWiperCopy,
|
||||
testConstants.vapsCopy.rearWiperCopy
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '05_01_CSR_Quote_RearGlass+Windshield',
|
||||
params: {
|
||||
wiperResponse: [testConstants.parts.frontWiperPart, testConstants.parts.rearWiperPart],
|
||||
rainDefenseResponse: testConstants.parts.rainDefensePart,
|
||||
damage: {
|
||||
isRepair: false,
|
||||
glassToReplace: [
|
||||
testConstants.damages.frontWindshield,
|
||||
testConstants.damages.rearWindshield
|
||||
]
|
||||
},
|
||||
glassParts: [],
|
||||
supportingItems: []
|
||||
},
|
||||
iterations: [
|
||||
{
|
||||
name: 'Economy',
|
||||
vapsCombo: [],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
||||
displayContent: testConstants.defaultItemCopy.defaultItemCopyArray
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Economy+Frontwiper',
|
||||
vapsCombo: [testConstants.parts.frontWiperPart],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.frontWiperCopy
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Economy+Rearwiper',
|
||||
vapsCombo: [testConstants.parts.rearWiperPart],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.rearWiperCopy
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Economy+RainDefense',
|
||||
vapsCombo: [testConstants.parts.rainDefensePart],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.rainDefenseCopy
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Economy+Frontwiper+RainDefense',
|
||||
vapsCombo: [
|
||||
testConstants.parts.frontWiperPart,
|
||||
testConstants.parts.rainDefensePart
|
||||
],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.frontWiperCopy,
|
||||
testConstants.vapsCopy.rainDefenseCopy
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Economy+Rearwiper+RainDefense',
|
||||
vapsCombo: [testConstants.parts.rearWiperPart, testConstants.parts.rainDefensePart],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.rearWiperCopy,
|
||||
testConstants.vapsCopy.rainDefenseCopy
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Standard',
|
||||
vapsCombo: [testConstants.parts.frontWiperPart, testConstants.parts.rearWiperPart],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierTwoTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.frontWiperCopy,
|
||||
testConstants.vapsCopy.rearWiperCopy
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Premium',
|
||||
vapsCombo: [
|
||||
testConstants.parts.rearWiperPart,
|
||||
testConstants.parts.frontWiperPart,
|
||||
testConstants.parts.rainDefensePart
|
||||
],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierThreeTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.frontWiperCopy,
|
||||
testConstants.vapsCopy.rearWiperCopy,
|
||||
testConstants.vapsCopy.rainDefenseCopy
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '05_01_CSR_Quote_RearGlassNoFrontFit',
|
||||
params: {
|
||||
wiperResponse: [testConstants.parts.rearWiperPart],
|
||||
rainDefenseResponse: testConstants.parts.rainDefensePart,
|
||||
damage: {
|
||||
isRepair: false,
|
||||
glassToReplace: [testConstants.damages.rearWindshield]
|
||||
},
|
||||
glassParts: [],
|
||||
supportingItems: []
|
||||
},
|
||||
iterations: [
|
||||
{
|
||||
name: 'Economy',
|
||||
vapsCombo: [],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
||||
displayContent: testConstants.defaultItemCopy.defaultItemCopyArray
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Economy+Raindefense',
|
||||
vapsCombo: [testConstants.parts.rainDefensePart],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.rainDefenseCopy
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Standard',
|
||||
vapsCombo: [testConstants.parts.rearWiperPart],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierTwoTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.rearWiperCopy
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Premium',
|
||||
vapsCombo: [testConstants.parts.rearWiperPart, testConstants.parts.rainDefensePart],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierThreeTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.rearWiperCopy,
|
||||
testConstants.vapsCopy.rainDefenseCopy
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
// 05_01_CSR_Quote_Windshield+SideGlass has identical outcomes to 05_01_CSR_Quote_Cash, but included in case that changes in the future.
|
||||
{
|
||||
name: '05_01_CSR_Quote_Windshield+SideGlass',
|
||||
params: {
|
||||
wiperResponse: [testConstants.parts.frontWiperPart, testConstants.parts.rearWiperPart],
|
||||
rainDefenseResponse: testConstants.parts.rainDefensePart,
|
||||
damage: {
|
||||
isRepair: false,
|
||||
glassToReplace: [
|
||||
testConstants.damages.frontWindshield,
|
||||
testConstants.damages.sideGlass
|
||||
]
|
||||
},
|
||||
glassParts: [],
|
||||
supportingItems: []
|
||||
},
|
||||
iterations: [
|
||||
{
|
||||
name: 'Economy',
|
||||
vapsCombo: [],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
||||
displayContent: testConstants.defaultItemCopy.defaultItemCopyArray
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Standard',
|
||||
vapsCombo: [testConstants.parts.frontWiperPart],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierTwoTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.frontWiperCopy
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Premium',
|
||||
vapsCombo: [
|
||||
testConstants.parts.rainDefensePart,
|
||||
testConstants.parts.frontWiperPart
|
||||
],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierThreeTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.frontWiperCopy,
|
||||
testConstants.vapsCopy.rainDefenseCopy
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Standard+RearWiper',
|
||||
vapsCombo: [testConstants.parts.frontWiperPart, testConstants.parts.rearWiperPart],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierTwoTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.frontWiperCopy,
|
||||
testConstants.vapsCopy.rearWiperCopy
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
// Has no standard package
|
||||
{
|
||||
name: '05_01_CSR_Quote_SideGlass',
|
||||
params: {
|
||||
wiperResponse: [testConstants.parts.frontWiperPart, testConstants.parts.rearWiperPart],
|
||||
rainDefenseResponse: testConstants.parts.rainDefensePart,
|
||||
damage: {
|
||||
isRepair: false,
|
||||
glassToReplace: [testConstants.damages.sideGlass]
|
||||
},
|
||||
glassParts: [],
|
||||
supportingItems: []
|
||||
},
|
||||
iterations: [
|
||||
{
|
||||
name: 'Economy',
|
||||
vapsCombo: [],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
||||
displayContent: testConstants.defaultItemCopy.defaultItemCopyArray
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Economy+Frontwiper',
|
||||
vapsCombo: [testConstants.parts.frontWiperPart],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.frontWiperCopy
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Economy+RainDefense',
|
||||
vapsCombo: [testConstants.parts.rainDefensePart],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.rainDefenseCopy
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Economy+Rearwiper',
|
||||
vapsCombo: [testConstants.parts.rearWiperPart],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.rearWiperCopy
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Premium',
|
||||
vapsCombo: [
|
||||
testConstants.parts.rainDefensePart,
|
||||
testConstants.parts.frontWiperPart
|
||||
],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierThreeTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.frontWiperCopy,
|
||||
testConstants.vapsCopy.rainDefenseCopy
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Premium+Rearwiper',
|
||||
vapsCombo: [
|
||||
testConstants.parts.rainDefensePart,
|
||||
testConstants.parts.frontWiperPart,
|
||||
testConstants.parts.rearWiperPart
|
||||
],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierThreeTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.frontWiperCopy,
|
||||
testConstants.vapsCopy.rearWiperCopy,
|
||||
testConstants.vapsCopy.rainDefenseCopy
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
// Has no standard package
|
||||
{
|
||||
name: '05_01_CSR_Quote_NoWiperFit',
|
||||
params: {
|
||||
wiperResponse: [],
|
||||
rainDefenseResponse: testConstants.parts.rainDefensePart,
|
||||
damage: {
|
||||
isRepair: false,
|
||||
glassToReplace: [testConstants.damages.frontWindshield]
|
||||
},
|
||||
glassParts: [],
|
||||
supportingItems: []
|
||||
},
|
||||
iterations: [
|
||||
{
|
||||
name: 'Economy',
|
||||
vapsCombo: [],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
||||
displayContent: testConstants.defaultItemCopy.defaultItemCopyArray
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Premium',
|
||||
vapsCombo: [testConstants.parts.rainDefensePart],
|
||||
expected: {
|
||||
packageNameWidget: testConstants.widgetNames.tierThreeTitle,
|
||||
displayContent: [
|
||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
||||
testConstants.vapsCopy.rainDefenseCopy
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
function generateDefaultProps() {
|
||||
return {
|
||||
servicePackageOptionsCmsName: testConstants.cmsPropValues.servicePackageOptionsCmsName,
|
||||
defaultPackageItemsCmsName: testConstants.cmsPropValues.defaultPackageItemsCmsName,
|
||||
vapsItemsCmsName: testConstants.cmsPropValues.vapsItemsCmsName,
|
||||
lineItems: {
|
||||
glassParts: [],
|
||||
supportingItems: [],
|
||||
vaps: [testConstants.parts.frontWiperPart]
|
||||
},
|
||||
damage: {
|
||||
isRepair: false,
|
||||
glassToReplace: [testConstants.damages.frontWindshield]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let cmsContent;
|
||||
const mockMixin = {
|
||||
methods: {
|
||||
getCmsContent: jest.fn((widgetName, cmsFieldName) => cmsContent?.[widgetName]?.[cmsFieldName] ?? '')
|
||||
}
|
||||
};
|
||||
|
||||
function initializeWithDefault() {
|
||||
useMainStore().order.availableVaps = [testConstants.parts.frontWiperPart, testConstants.parts.rainDefensePart];
|
||||
}
|
||||
|
||||
function getShallowMountedComponent(initialData = {}, methodToRun = () => {}) {
|
||||
const mountOptions = getMountOptions({
|
||||
router: {
|
||||
navigate: jest.fn()
|
||||
}
|
||||
});
|
||||
|
||||
methodToRun();
|
||||
|
||||
mountOptions.data = () => (
|
||||
initialData
|
||||
);
|
||||
|
||||
mountOptions.mixins = [mockMixin];
|
||||
|
||||
const wrapper = shallowMount(servicePackageReview, mountOptions);
|
||||
wrapper.vm.setCmsContent = jest.fn();
|
||||
return { wrapper };
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
cmsContent = {
|
||||
ServicePackageTitle: {
|
||||
Answers: [
|
||||
{
|
||||
Name: packageNames.TIER_ONE,
|
||||
Text: '',
|
||||
SubText: '',
|
||||
ImageId: testConstants.imageId,
|
||||
Image: '',
|
||||
SubWidgetName: testConstants.widgetNames.tierOneTitle
|
||||
},
|
||||
{
|
||||
Name: packageNames.TIER_TWO,
|
||||
Text: '',
|
||||
SubText: '',
|
||||
ImageId: testConstants.imageId,
|
||||
Image: '',
|
||||
SubWidgetName: testConstants.widgetNames.tierTwoTitle
|
||||
},
|
||||
{
|
||||
Name: packageNames.TIER_THREE,
|
||||
Text: '',
|
||||
SubText: '',
|
||||
ImageId: testConstants.imageId,
|
||||
Image: '',
|
||||
SubWidgetName: testConstants.widgetNames.tierThreeTitle
|
||||
}
|
||||
]
|
||||
},
|
||||
DefaultPackageItemDescriptions: {
|
||||
Answers: [
|
||||
{
|
||||
Name: 'Item1',
|
||||
Text: testConstants.defaultItemCopy.itemOne,
|
||||
SubText: '',
|
||||
ImageId: testConstants.imageId,
|
||||
Image: '',
|
||||
SubWidgetName: ''
|
||||
},
|
||||
{
|
||||
Name: 'Item2',
|
||||
Text: testConstants.defaultItemCopy.itemTwo,
|
||||
SubText: '',
|
||||
ImageId: testConstants.imageId,
|
||||
Image: '',
|
||||
SubWidgetName: ''
|
||||
},
|
||||
{
|
||||
Name: 'Item3',
|
||||
Text: testConstants.defaultItemCopy.itemThree,
|
||||
SubText: '',
|
||||
ImageId: testConstants.imageId,
|
||||
Image: '',
|
||||
SubWidgetName: ''
|
||||
}
|
||||
]
|
||||
},
|
||||
VapsItemDescriptions: {
|
||||
Answers: [
|
||||
{
|
||||
Name: partTypeStrings.FRONT_WIPER,
|
||||
Text: testConstants.vapsCopy.frontWiperCopy,
|
||||
SubText: '',
|
||||
ImageId: testConstants.imageId,
|
||||
Image: '',
|
||||
SubWidgetName: ''
|
||||
},
|
||||
{
|
||||
Name: partTypeStrings.REAR_WIPER,
|
||||
Text: testConstants.vapsCopy.rearWiperCopy,
|
||||
SubText: '',
|
||||
ImageId: testConstants.imageId,
|
||||
Image: '',
|
||||
SubWidgetName: ''
|
||||
},
|
||||
{
|
||||
Name: partTypeStrings.RAIN_DEFENSE,
|
||||
Text: testConstants.vapsCopy.rainDefenseCopy,
|
||||
SubText: '',
|
||||
ImageId: testConstants.imageId,
|
||||
Image: '',
|
||||
SubWidgetName: ''
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
describe('Service Package Review Block', () => {
|
||||
describe('General functionality', () => {
|
||||
test('Should properly "Round Down" package tier', async () => {
|
||||
// Slightly longer explanation:
|
||||
// Should only return the highest tier where *every* offered VAP is part of the order.
|
||||
// However, there may be vaps not offered in the qualifying tier. Hence rounding *down*.
|
||||
//
|
||||
// I.e. Economy=[], Standard=[front wipers], Premium=[front wipers, rain defense].
|
||||
// Current vaps=[rain defense]. Though rain defense is in Premium, we don't satisfy it or standard.
|
||||
// So our tier should still be Economy.
|
||||
// Should still display extra vaps.
|
||||
|
||||
// Arrange
|
||||
const props = generateDefaultProps();
|
||||
props.lineItems.vaps = [testConstants.parts.rainDefensePart];
|
||||
|
||||
const { wrapper } = getShallowMountedComponent({
|
||||
...props
|
||||
});
|
||||
|
||||
initializeWithDefault();
|
||||
|
||||
// Act
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.packageNameWidget).toEqual(testConstants.widgetNames.tierOneTitle);
|
||||
|
||||
const containsRainDefenseCopy = wrapper.vm.displayContent.includes(testConstants.vapsCopy.rainDefenseCopy);
|
||||
expect(containsRainDefenseCopy).toBe(true);
|
||||
});
|
||||
|
||||
test('Should display all default items from cms', async () => {
|
||||
// Arrange
|
||||
cmsContent.DefaultPackageItemDescriptions.Answers.push({
|
||||
Name: 'Item4',
|
||||
Text: testConstants.defaultItemCopy.itemFour,
|
||||
SubText: '',
|
||||
ImageId: testConstants.imageId,
|
||||
Image: '',
|
||||
SubWidgetName: ''
|
||||
});
|
||||
|
||||
const props = generateDefaultProps();
|
||||
props.lineItems.vaps = [];
|
||||
|
||||
const { wrapper } = getShallowMountedComponent({
|
||||
...props
|
||||
});
|
||||
|
||||
initializeWithDefault();
|
||||
|
||||
// Act
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
const expectedResult = [
|
||||
testConstants.defaultItemCopy.itemOne,
|
||||
testConstants.defaultItemCopy.itemTwo,
|
||||
testConstants.defaultItemCopy.itemThree,
|
||||
testConstants.defaultItemCopy.itemFour
|
||||
];
|
||||
|
||||
expect(wrapper.vm.displayContent).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
test('Should display vaps if and only if they are added', async () => {
|
||||
// Arrange
|
||||
const props = generateDefaultProps();
|
||||
const { wrapper } = getShallowMountedComponent({
|
||||
...props
|
||||
});
|
||||
|
||||
// Act
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
initializeWithDefault();
|
||||
|
||||
// Assert
|
||||
const includesFrontWiperCopy = wrapper.vm.displayContent.includes(testConstants.vapsCopy.frontWiperCopy);
|
||||
const includesRainDefenseCopy = wrapper.vm.displayContent.includes(testConstants.vapsCopy.rainDefenseCopy);
|
||||
expect(includesFrontWiperCopy).toBe(true);
|
||||
expect(includesRainDefenseCopy).toBe(false);
|
||||
});
|
||||
|
||||
test('Should not error if cms content is missing (though may display poorly).', async () => {
|
||||
// Arrange
|
||||
cmsContent = {};
|
||||
const props = generateDefaultProps();
|
||||
const { wrapper } = getShallowMountedComponent({
|
||||
...props
|
||||
});
|
||||
|
||||
initializeWithDefault();
|
||||
|
||||
// Act
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.packageNameWidget).toEqual('');
|
||||
expect(wrapper.vm.displayContent).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Match Figma Scenarios', () => {
|
||||
figmaScenarios.forEach((scenario) => {
|
||||
scenario.iterations.forEach((iteration) => {
|
||||
it(`Should match figma scenario "${scenario.name}", iteration "${iteration.name}"`, async () => {
|
||||
// Arrange
|
||||
const props = generateDefaultProps();
|
||||
props.damage = scenario.params.damage;
|
||||
props.glassParts = scenario.params.glassParts;
|
||||
props.lineItems.supportingItems = scenario.params.supportingItems;
|
||||
props.lineItems.vaps = iteration.vapsCombo;
|
||||
|
||||
const { wrapper } = getShallowMountedComponent({
|
||||
...props
|
||||
});
|
||||
|
||||
useMainStore().order.availableVaps = [scenario.params.rainDefenseResponse, ...scenario.params.wiperResponse];
|
||||
|
||||
// Act
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.packageNameWidget).toEqual(iteration.expected.packageNameWidget);
|
||||
expect(wrapper.vm.displayContent).toEqual(iteration.expected.displayContent);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
<template>
|
||||
<reviewBlock
|
||||
:headerCmsWidgetName="packageNameWidget"
|
||||
:content="displayContent" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import settleAllPromises from '@/helpers/layout-helper';
|
||||
// import baseMixin from '@/mixins/base-mixin.js';
|
||||
import { useMainStore } from '@/store';
|
||||
import reviewBlock from '@/layouts/payment-method/review-dropdown/review-block/review-block.vue';
|
||||
import {
|
||||
getHighestFullySatisfiedTier,
|
||||
containsLineItemWithPartType
|
||||
} from '@/helpers/service-package-helper.js';
|
||||
|
||||
export default {
|
||||
name: 'service-package-review',
|
||||
components: {
|
||||
reviewBlock
|
||||
},
|
||||
props: {
|
||||
servicePackageOptionsCmsName: String,
|
||||
defaultPackageItemsCmsName: String,
|
||||
vapsItemsCmsName: String,
|
||||
lineItems: Object,
|
||||
damage: Object
|
||||
},
|
||||
emits: ['edit-clicked'],
|
||||
data() {
|
||||
return {
|
||||
availableVaps: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
displayContent() {
|
||||
return [...this.defaultPackageText, ...this.vapsItemText];
|
||||
},
|
||||
packageNameWidget() {
|
||||
const servicePackageNames = this.getCmsContent(
|
||||
this.servicePackageOptionsCmsName,
|
||||
'Answers'
|
||||
);
|
||||
|
||||
if (!servicePackageNames) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const currentPackage = servicePackageNames.find((entry) => entry.Name === this.packageLevel);
|
||||
|
||||
return currentPackage.SubWidgetName;
|
||||
},
|
||||
defaultPackageText() {
|
||||
const defaultItems = this.getCmsContent(this.defaultPackageItemsCmsName, 'Answers');
|
||||
|
||||
if (!defaultItems) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return defaultItems.map((answer) => answer.Text);
|
||||
},
|
||||
vapsItemText() {
|
||||
const vapsDescriptions = this.getCmsContent(this.vapsItemsCmsName, 'Answers');
|
||||
|
||||
if (!vapsDescriptions) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const vapsDescriptionsOnOrder = vapsDescriptions.filter((answer) =>
|
||||
containsLineItemWithPartType(answer.Name, this.vaps));
|
||||
|
||||
return vapsDescriptionsOnOrder.map((answer) => answer.Text);
|
||||
},
|
||||
packageLevel() {
|
||||
return getHighestFullySatisfiedTier(
|
||||
this.glassToReplace,
|
||||
this.availableLineItems,
|
||||
this.isRepair,
|
||||
this.vaps
|
||||
);
|
||||
},
|
||||
glassToReplace() {
|
||||
return this.damage.glassToReplace;
|
||||
},
|
||||
isRepair() {
|
||||
return this.damage.isRepair;
|
||||
},
|
||||
vaps() {
|
||||
return this.lineItems?.vaps;
|
||||
},
|
||||
availableLineItems() {
|
||||
return [
|
||||
...(this.lineItems?.glassParts ?? []),
|
||||
...(this.lineItems?.supportingItems ?? []),
|
||||
...(useMainStore().order.availableVaps ?? [])
|
||||
];
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
// Components
|
||||
import vehicleReview from '@/layouts/payment-method/review-dropdown/review-sections/vehicle-review/vehicle-review.vue';
|
||||
|
||||
// Supporting Files
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
||||
|
||||
jest.mock('@/helpers/cms-content-helper', () => ({
|
||||
fetchCmsContentForPage: () => Promise.resolve('content')
|
||||
}));
|
||||
|
||||
function getShallowMountedComponent(initialData = {}, methodToRun = () => {}) {
|
||||
const mountOptions = getMountOptions({
|
||||
router: {
|
||||
navigate: jest.fn()
|
||||
}
|
||||
});
|
||||
|
||||
methodToRun();
|
||||
|
||||
mountOptions.data = () => (
|
||||
initialData
|
||||
);
|
||||
|
||||
const wrapper = shallowMount(vehicleReview, mountOptions);
|
||||
wrapper.vm.setCmsContent = jest.fn();
|
||||
return { wrapper };
|
||||
}
|
||||
|
||||
describe('Vehicle Review Block', () => {
|
||||
test('Correctly assembles vehicle info into a display string', async () => {
|
||||
// Arrange
|
||||
const { wrapper } = getShallowMountedComponent({
|
||||
vehicle: {
|
||||
year: '2019',
|
||||
make: 'Honda',
|
||||
model: 'Odyssey'
|
||||
}
|
||||
});
|
||||
|
||||
// Act
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.displayContent).toStrictEqual(['2019 Honda Odyssey']);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,10 +1,13 @@
|
|||
<template>
|
||||
<reviewBlock
|
||||
:headerCmsWidgetName="cmsWidgetName"
|
||||
:content="displayContent" />
|
||||
:customHeaderText="header"
|
||||
:content="displayContent"
|
||||
:editLinkText="editLinkText" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getStringWithCustomValues, processIfStatements } from '@/helpers/cms-content-helper';
|
||||
import { getGlassList } from '@/helpers/damage-helper';
|
||||
import reviewBlock from '@/layouts/payment-method/review-dropdown/review-block/review-block.vue';
|
||||
|
||||
export default {
|
||||
|
|
@ -13,15 +16,40 @@ export default {
|
|||
reviewBlock
|
||||
},
|
||||
props: {
|
||||
cmsWidgetName: String,
|
||||
vehicle: Object
|
||||
cmsWidgetName: String
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
computed: {
|
||||
header() {
|
||||
return this.getCmsContent(this.cmsWidgetName, 'HeaderText');
|
||||
},
|
||||
displayContent() {
|
||||
return [`${this.vehicle.year} ${this.vehicle.make} ${this.vehicle.model}`];
|
||||
return [this.getCmsContentWithCustomValues(this.cmsWidgetName, 'BodyText')];
|
||||
},
|
||||
editLinkText() {
|
||||
return this.getCmsContent(this.cmsWidgetName, 'FooterText');
|
||||
},
|
||||
customValueMap() {
|
||||
return {
|
||||
glassList: this.glassList,
|
||||
};
|
||||
},
|
||||
glassList() {
|
||||
const glassPieces = this.mainStore.lineItems.glassParts?.map((part) => part.partType.toLowerCase()) ?? [];
|
||||
return getGlassList(glassPieces);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getCmsContentWithCustomValues(widgetName, widgetField) {
|
||||
const rawText = this.getCmsContent(widgetName, widgetField);
|
||||
const processedIfStatements = processIfStatements(
|
||||
rawText,
|
||||
'custom',
|
||||
(v) => this.customValueMap[v]
|
||||
);
|
||||
return getStringWithCustomValues(processedIfStatements, this.customValueMap);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -124,13 +124,7 @@ export default {
|
|||
},
|
||||
async saveAndSubmitWorkOrder() {
|
||||
// Final work order submit after returning from pay in advance.
|
||||
try {
|
||||
await submitWorkOrder({ submitType: submitType.SAFELITE });
|
||||
} catch (error) {
|
||||
console.error(`error: response from submit work order:${error.message}`);
|
||||
this.navigateOnPayInAdvanceError();
|
||||
return;
|
||||
}
|
||||
await submitWorkOrder({ submitType: submitType.SAFELITE });
|
||||
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.PAY_IN_ADVANCE_SUCCESS,
|
||||
|
|
|
|||
|
|
@ -252,12 +252,12 @@ describe('policy-vehicles.vue', () => {
|
|||
);
|
||||
|
||||
test(
|
||||
'Error in lookupVehicleByVin call => bailout true and navigate forward with CLICKED_FORWARD_WITH_BAILOUT scenario.',
|
||||
'Error in lookupVehicleByVin call => error thrown in forwardButtonAction.',
|
||||
async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
const lookupReturnValue = { error: true, status: 500, data: 'error' };
|
||||
wrapper.vm.lookupVehicleByVin = jest.fn().mockReturnValue(lookupReturnValue);
|
||||
wrapper.vm.lookupVehicleByVin = jest.fn().mockReturnValue(Promise.reject(lookupReturnValue));
|
||||
|
||||
const vin = getRandomString(17, 17);
|
||||
await wrapper.setData({
|
||||
|
|
@ -270,21 +270,14 @@ describe('policy-vehicles.vue', () => {
|
|||
});
|
||||
|
||||
// Act
|
||||
wrapper.vm.mainStore.applicationUser.pageData[issPageValues.BAILOUT_PAGE] = {
|
||||
'bailout-page': {
|
||||
bailoutCode: bailoutCode.VehicleVinLookupError
|
||||
}
|
||||
};
|
||||
await wrapper.vm.forwardButtonAction();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.mainStore.setBailout).toHaveBeenCalledWith(bailoutMessage.vehicleVinLookupError(vin, lookupReturnValue.data));
|
||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
|
||||
navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
|
||||
undefined,
|
||||
{},
|
||||
{}
|
||||
);
|
||||
expect.assertions(1);
|
||||
try {
|
||||
await wrapper.vm.forwardButtonAction();
|
||||
} catch (e) {
|
||||
expect(e).toBe(lookupReturnValue);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -292,7 +285,7 @@ describe('policy-vehicles.vue', () => {
|
|||
async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.lookupVehicleByVin = jest.fn().mockReturnValue({ error: true, status: 404 });
|
||||
wrapper.vm.lookupVehicleByVin = jest.fn().mockRejectedValue({ isAxiosError: true, status: 404 });
|
||||
|
||||
const vin = getRandomString(17, 17);
|
||||
await wrapper.setData({
|
||||
|
|
|
|||
|
|
@ -59,7 +59,6 @@ import vehicleSelectionOptions from '@/constants/vehicle-selection-options.js';
|
|||
import endorsementOptions from '@/constants/endorsement-options.js';
|
||||
import globalRules from '@/constants/global-rules.js';
|
||||
import { useMainStore } from '@/store/index.js';
|
||||
import bailoutMessage from '@/constants/bailoutMessage';
|
||||
import {
|
||||
deductibleForSelectedVehicle,
|
||||
endorsementsForSelectedVehicle,
|
||||
|
|
@ -144,10 +143,6 @@ export default {
|
|||
},
|
||||
repairWaivedForSelectedVehicle() {
|
||||
return repairWaivedForSelectedVehicle(this.selectedPolicyVehicle);
|
||||
},
|
||||
selectedVehicle() {
|
||||
const vehicle = this.mainStore.lookupVehicleByVin(this.selectedVehicleVin);
|
||||
return vehicle;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
|
@ -156,15 +151,12 @@ export default {
|
|||
if (value === vehicleSelectionOptions.VEHICLE_NOT_LISTED) {
|
||||
// clear previously selected vehicle and image
|
||||
this.mainStore.resetVehicleState();
|
||||
} else {
|
||||
// get vehicle details from selected VIN
|
||||
const vehicle = await this.lookupVehicleByVin(value);
|
||||
return;
|
||||
}
|
||||
|
||||
// handle error in case vehicle info doesn't come back for selected VIN
|
||||
if (vehicle?.error === true) {
|
||||
this.mainStore.resetVehicleState();
|
||||
return;
|
||||
}
|
||||
// get vehicle details from selected VIN
|
||||
try {
|
||||
const vehicle = await this.lookupVehicleByVin(value);
|
||||
if (!vehicle?.data.canSafeliteService) {
|
||||
this.displayNoServiceAlert = true;
|
||||
return;
|
||||
|
|
@ -177,6 +169,8 @@ export default {
|
|||
vin: value
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
this.mainStore.resetVehicleState();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -196,33 +190,16 @@ export default {
|
|||
);
|
||||
},
|
||||
async forwardButtonAction() {
|
||||
if (this.selectedVehicleVin !== vehicleSelectionOptions.VEHICLE_NOT_LISTED) {
|
||||
if (this.selectedVehicleVin === vehicleSelectionOptions.VEHICLE_NOT_LISTED) {
|
||||
useMainStore().updateCoverageType(coverageType.NONE);
|
||||
useMainStore().updateCoverageStatus(coverageStatuses.NO_COVERAGE);
|
||||
this.navigateForward();
|
||||
return;
|
||||
}
|
||||
|
||||
const vehicle = this.policyVehicles.find((pv) => pv.vin === this.selectedVehicleVin);
|
||||
try {
|
||||
const vehicleLookupResponse = await this.lookupVehicleByVin(this.selectedVehicleVin);
|
||||
const vehicle = this.policyVehicles.find((pv) => pv.vin === this.selectedVehicleVin);
|
||||
|
||||
if (vehicleLookupResponse.error) {
|
||||
if (vehicleLookupResponse.status === 404) {
|
||||
this.mainStore.resetVehicleState();
|
||||
useMainStore().updateVehicle({
|
||||
policyVehicleId: vehicle.id,
|
||||
carId: '0',
|
||||
category: '',
|
||||
year: vehicle.vehicleYear || '',
|
||||
make: vehicle.vehicleMake || '',
|
||||
model: vehicle.vehicleModel || '',
|
||||
style: vehicle.vehicleStyle || '',
|
||||
vin: vehicle.vin
|
||||
});
|
||||
this.policyVinFound = false;
|
||||
return this.navigateForward();
|
||||
}
|
||||
|
||||
this.mainStore.setBailout(bailoutMessage.vehicleVinLookupError(
|
||||
vehicle.vin,
|
||||
vehicleLookupResponse.data
|
||||
));
|
||||
return this.navigateForward();
|
||||
}
|
||||
|
||||
useMainStore().updateVehicle({
|
||||
...vehicleLookupResponse.data,
|
||||
|
|
@ -235,21 +212,30 @@ export default {
|
|||
repairWaived: this.repairWaivedForSelectedVehicle,
|
||||
endorsements: this.endorsementsForSelectedVehicle
|
||||
});
|
||||
} else {
|
||||
useMainStore().updateCoverageType(coverageType.NONE);
|
||||
useMainStore().updateCoverageStatus(coverageStatuses.NO_COVERAGE);
|
||||
this.navigateForward();
|
||||
} catch (e) {
|
||||
if (e.isAxiosError && e.status === 404) {
|
||||
this.mainStore.resetVehicleState();
|
||||
useMainStore().updateVehicle({
|
||||
policyVehicleId: vehicle.id,
|
||||
carId: '0',
|
||||
category: '',
|
||||
year: vehicle.vehicleYear || '',
|
||||
make: vehicle.vehicleMake || '',
|
||||
model: vehicle.vehicleModel || '',
|
||||
style: vehicle.vehicleStyle || '',
|
||||
vin: vehicle.vin
|
||||
});
|
||||
this.policyVinFound = false;
|
||||
this.navigateForward();
|
||||
return;
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
return this.navigateForward();
|
||||
},
|
||||
navigateForward() {
|
||||
if (this.mainStore.isBailout) {
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
|
||||
this.$route,
|
||||
{},
|
||||
{}
|
||||
);
|
||||
} else if (this.selectedVehicleVin === vehicleSelectionOptions.VEHICLE_NOT_LISTED) {
|
||||
if (this.selectedVehicleVin === vehicleSelectionOptions.VEHICLE_NOT_LISTED) {
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD_NON_LISTED_VEHICLE,
|
||||
this.$route,
|
||||
|
|
@ -281,15 +267,7 @@ export default {
|
|||
}
|
||||
},
|
||||
async lookupVehicleByVin(vin) {
|
||||
try {
|
||||
return await useMainStore().lookupVehicleByVin(vin);
|
||||
} catch (responseError) {
|
||||
return {
|
||||
error: true,
|
||||
status: responseError.status,
|
||||
data: responseError.data
|
||||
};
|
||||
}
|
||||
return useMainStore().lookupVehicleByVin(vin);
|
||||
},
|
||||
async addAnotherVehicle() {
|
||||
this.selectedVehicleVin = vehicleSelectionOptions.VEHICLE_NOT_LISTED;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ jest.mock('@/helpers/cms-content-helper', () => ({
|
|||
function setupMocks(mockApiResponses) {
|
||||
const mountOptions = getMountOptions({
|
||||
router: {
|
||||
navigate: jest.fn()
|
||||
navigate: jest.fn(),
|
||||
navigateBailout: jest.fn()
|
||||
},
|
||||
route: 'provider-preference'
|
||||
});
|
||||
|
|
@ -43,7 +44,7 @@ function setupMocks(mockApiResponses) {
|
|||
}
|
||||
|
||||
describe('provider-preference.vue', () => {
|
||||
test('Should navigate to tpa disabled route page when TPAOption selected and TPA Flow disabled', () => {
|
||||
test('Should navigateBailout when TPAOption selected and TPA Flow disabled', () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks();
|
||||
|
||||
|
|
@ -52,7 +53,7 @@ describe('provider-preference.vue', () => {
|
|||
wrapper.vm.findAnotherShopClicked();
|
||||
|
||||
// Test
|
||||
expect(wrapper.vm.$router.navigate).toBeCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_TPA_DISABLED, 'provider-preference');
|
||||
expect(wrapper.vm.$router.navigateBailout).toBeCalled();
|
||||
});
|
||||
|
||||
test('Should navigate to safelite flow when navigateWithTPARecalAnswer is called with SafeliteOption', () => {
|
||||
|
|
|
|||
|
|
@ -186,9 +186,7 @@ export default {
|
|||
this.scheduleWithTPA();
|
||||
}
|
||||
} else {
|
||||
this.mainStore.setBailout(bailoutMessage.TPANotEnabled());
|
||||
const scenario = this.navigationScenarios.CLICKED_FORWARD_WITH_TPA_DISABLED;
|
||||
this.navigateForward(scenario);
|
||||
this.$router.navigateBailout(bailoutMessage.TPANotEnabled());
|
||||
}
|
||||
},
|
||||
openStateSteeringModal() {
|
||||
|
|
|
|||
|
|
@ -89,9 +89,9 @@ import {
|
|||
import settleAllPromises from '@/helpers/layout-helper';
|
||||
import showIssLoadingModal from '@/helpers/loading-modal-helper.js';
|
||||
import {
|
||||
getPricedMobileFeePart,
|
||||
getServiceabilityDetails,
|
||||
getZipCodeData
|
||||
getProviderData,
|
||||
getZipCodeData,
|
||||
onProviderChanged
|
||||
} from '@/helpers/service-location-helper';
|
||||
import { Form } from 'vee-validate';
|
||||
import BaseFormMixin from '@/mixins/base-form-mixin.js';
|
||||
|
|
@ -171,20 +171,20 @@ const getAvailableDates = async (
|
|||
await Promise.all(storeActionConfigs.map(async (storeAction) => {
|
||||
let timeSlotsResponse = null;
|
||||
if (storeAction.storeAction === GET_SHOP_TIME_SLOTS) {
|
||||
timeSlotsResponse = await useMainStore().getShopTimeSlots(
|
||||
storeAction.payload.startDate,
|
||||
storeAction.payload.endDate,
|
||||
storeAction.payload.shopAppointmentType,
|
||||
storeAction.payload.providerNumber
|
||||
);
|
||||
if (storeAction.payload.providerNumber) {
|
||||
timeSlotsResponse = await useMainStore().getShopTimeSlots(
|
||||
storeAction.payload.startDate,
|
||||
storeAction.payload.endDate,
|
||||
storeAction.payload.shopAppointmentType,
|
||||
storeAction.payload.providerNumber
|
||||
);
|
||||
}
|
||||
} else if (storeAction.payload?.zipCodeOverride) {
|
||||
timeSlotsResponse = await useMainStore().getMobileTimeSlots(
|
||||
storeAction.payload.startDate,
|
||||
storeAction.payload.endDate,
|
||||
storeAction.payload.zipCodeOverride
|
||||
).catch(() => {
|
||||
console.warn('Error fetching mobile time slots...');
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
if (!timeSlotsResponse || !timeSlotsResponse.data) {
|
||||
|
|
@ -204,6 +204,9 @@ const getAvailableDates = async (
|
|||
// sort days chronologically
|
||||
timeSlotsResponsesData.days.sort(compareDayStrings);
|
||||
return timeSlotsResponsesData;
|
||||
})
|
||||
.catch(() => {
|
||||
return timeSlotsResponsesData;
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -222,16 +225,9 @@ export default {
|
|||
async beforeRouteEnter(to, from, next) {
|
||||
// Call APIs
|
||||
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
|
||||
const storeServiceLocation = useMainStore().order.serviceLocation;
|
||||
const storeSelectedAppointmentType = storeServiceLocation?.appointmentType;
|
||||
const isMobileAppointment = storeSelectedAppointmentType === AppointmentTypeStrings.MOBILE
|
||||
|| storeSelectedAppointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP;
|
||||
const storeSelectedProvider = storeServiceLocation?.provider;
|
||||
const serviceZipCode = storeServiceLocation?.zipCode || useMainStore().order.customer.address.zipCode;
|
||||
const { isMobileAppointment, storeServiceLocation, storeSelectedProvider, serviceZipCode } = getProviderData();
|
||||
const zipCodeData = getZipCodeData(serviceZipCode);
|
||||
const mobileFeePartPromise = getPricedMobileFeePart(serviceZipCode);
|
||||
const serviceabilityDetailsPromise = getServiceabilityDetails(serviceZipCode);
|
||||
const getGlassFeesPromise = useMainStore().getGlassFees();
|
||||
const serviceabilityDetailsPromise = useMainStore().getServiceabilityDetails(serviceZipCode);
|
||||
const providersPromise = useMainStore().getSafeliteProviders(serviceZipCode, 150);
|
||||
|
||||
const premiumFeePromise = useMainStore().getMobilePremiumFee();
|
||||
|
|
@ -248,14 +244,6 @@ export default {
|
|||
resultKey: 'cmsContent',
|
||||
promise: cmsContentPromise
|
||||
},
|
||||
{
|
||||
resultKey: 'mobileFeePart',
|
||||
promise: mobileFeePartPromise
|
||||
},
|
||||
{
|
||||
resultKey: 'glassFees',
|
||||
promise: getGlassFeesPromise
|
||||
},
|
||||
{
|
||||
resultKey: 'serviceabilityDetails',
|
||||
promise: serviceabilityDetailsPromise
|
||||
|
|
@ -268,6 +256,10 @@ export default {
|
|||
resultKey: 'providers',
|
||||
promise: providersPromise
|
||||
},
|
||||
{
|
||||
resultKey: 'providerChanged',
|
||||
promise: onProviderChanged()
|
||||
},
|
||||
{
|
||||
resultKey: 'premiumFeeWithPrice',
|
||||
promise: premiumFeeWithPricePromise
|
||||
|
|
@ -329,7 +321,6 @@ export default {
|
|||
inShopDatesData: [],
|
||||
isMobileView: false,
|
||||
mobileDatesData: [],
|
||||
mobileFeePart: null,
|
||||
mobilePremiumAppointmentFee: null,
|
||||
mobileProviderNumber: null,
|
||||
selectableDatesData: [],
|
||||
|
|
@ -656,7 +647,9 @@ export default {
|
|||
}
|
||||
},
|
||||
mobileZipUpdatedFromServiceLocation(mobileZipServiceLocation) {
|
||||
if (mobileZipServiceLocation) {
|
||||
if (mobileZipServiceLocation
|
||||
&& (this.mobileProviderNumber !== mobileZipServiceLocation.mobileProviderNumber
|
||||
|| !this.hasNeededServiceLocationData)) {
|
||||
this.mobileProviderNumber = mobileZipServiceLocation.mobileProviderNumber;
|
||||
this.selectedMobileZipCode = mobileZipServiceLocation.zipCode;
|
||||
this.selectedServiceLocation = mobileZipServiceLocation;
|
||||
|
|
@ -668,18 +661,15 @@ export default {
|
|||
},
|
||||
async providerChangedFromServiceLocation(newProvider) {
|
||||
this.selectedProvider = newProvider?.provider;
|
||||
if (newProvider?.refreshDatePicker && newProvider.provider) {
|
||||
this.mainStore.updateServiceLocationProvider(newProvider.provider);
|
||||
|
||||
if (newProvider?.provider) {
|
||||
this.mainStore.updateServiceLocation(newProvider);
|
||||
showIssLoadingModal(true);
|
||||
await this.mainStore.getBillToInfo(newProvider.provider?.providerNumber)
|
||||
.then(() => {
|
||||
this.refreshDatePicker();
|
||||
})
|
||||
.catch(() => {
|
||||
console.warn('Error fetching bill to info...');
|
||||
showIssLoadingModal(false);
|
||||
});
|
||||
await onProviderChanged();
|
||||
if (newProvider.refreshDatePicker) {
|
||||
await this.refreshDatePicker();
|
||||
} else {
|
||||
showIssLoadingModal(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
async refreshDatePicker() {
|
||||
|
|
@ -710,8 +700,7 @@ export default {
|
|||
appointmentTypeToUse = AppointmentTypeStrings.DROP_OFF;
|
||||
}
|
||||
|
||||
// Save service location then save schedule then navigate forward
|
||||
await this.$refs.serviceLocation.forwardButtonAction(appointmentTypeToUse);
|
||||
this.mainStore.updateAppointmentType(appointmentTypeToUse);
|
||||
this.mainStore.saveSchedule(this.selectedTimeSlotInfo.timeSlot);
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD,
|
||||
|
|
|
|||
|
|
@ -88,7 +88,6 @@ import { useMainStore } from '@/store';
|
|||
import {
|
||||
getMobileZipCodeData,
|
||||
getPricedMobileFeePart,
|
||||
getServiceabilityDetails
|
||||
} from '@/helpers/service-location-helper';
|
||||
import { deepClone } from '@/helpers/object-helper.js';
|
||||
import vehicleProtectedQuestion from '@/layouts/schedule-page/service-location/mobile-location-modal-question/vehicle-protected-question/vehicle-protected-question.vue';
|
||||
|
|
@ -273,7 +272,7 @@ export default {
|
|||
const mobileFeePart = await getPricedMobileFeePart(serviceZipCode);
|
||||
|
||||
// retrieve serviceability details
|
||||
const serviceabilityDetails = await getServiceabilityDetails(serviceZipCode);
|
||||
const serviceabilityDetails = await useMainStore().getServiceabilityDetails(serviceZipCode);
|
||||
|
||||
// update content related to service zip code
|
||||
this.$emit('updated-mobile-fee-part', mobileFeePart);
|
||||
|
|
|
|||
|
|
@ -86,7 +86,6 @@ const mockProviders = () => Promise.resolve([
|
|||
jest.mock(
|
||||
'@/helpers/service-location-helper',
|
||||
() => ({
|
||||
getServiceabilityDetails: jest.fn((mockServiceZipCode) => mockGetServiceabilityDetails(mockServiceZipCode)),
|
||||
getZipCodeData: jest.fn((mockServiceZipCode) => mockZipcodeData(mockServiceZipCode)),
|
||||
getMobileZipCodeData: jest.fn((mockServiceZipCode) => mockZipcodeData(mockServiceZipCode))
|
||||
})
|
||||
|
|
@ -147,6 +146,7 @@ function setupMocks({ appointmentType = AppointmentTypeStrings.IN_SHOP }) {
|
|||
}
|
||||
})];
|
||||
useMainStore().getSafeliteProviders = jest.fn().mockImplementation(() => mockProviders());
|
||||
useMainStore().getServiceabilityDetails = jest.fn((mockServiceZipCode) => mockGetServiceabilityDetails(mockServiceZipCode));
|
||||
const wrapper = mount(serviceLocation, mountOptions);
|
||||
|
||||
wrapper.vm.$router.navigateWithSpinner = jest.fn();
|
||||
|
|
|
|||
|
|
@ -129,7 +129,6 @@ import errorMessages from '@/constants/error-messages';
|
|||
import { useMainStore } from '@/store';
|
||||
import showIssLoadingModal from '@/helpers/loading-modal-helper.js';
|
||||
import {
|
||||
getServiceabilityDetails,
|
||||
getZipCodeData,
|
||||
getMobileZipCodeData
|
||||
} from '@/helpers/service-location-helper';
|
||||
|
|
@ -183,7 +182,6 @@ export default {
|
|||
isRecalibrationServiceableMobile: null,
|
||||
selectedAppointmentType: this.getSelectedAppointmentType(),
|
||||
selectedProvider: this.getSelectedProvider(),
|
||||
mobileFeePart: null,
|
||||
mobileProviderNumber: null,
|
||||
zipContainsMilitaryBase: false,
|
||||
zipCodeCtu: null,
|
||||
|
|
@ -233,8 +231,8 @@ export default {
|
|||
},
|
||||
isInshop() {
|
||||
return (
|
||||
this.selectedAppointmentType === 'Inshop'
|
||||
|| this.selectedAppointmentType === 'Dropoff'
|
||||
this.selectedAppointmentType === AppointmentTypeStrings.IN_SHOP
|
||||
|| this.selectedAppointmentType === AppointmentTypeStrings.DROP_OFF
|
||||
);
|
||||
},
|
||||
isMobile() {
|
||||
|
|
@ -317,7 +315,7 @@ export default {
|
|||
},
|
||||
selectedAppointmentType(newValue, oldValue) {
|
||||
if (this.isInshop && this.selectedProvider && this.availabilityRating === null) {
|
||||
const appointmenTypeServiceLocationObj = {
|
||||
const appointmentTypeServiceLocationObj = {
|
||||
appointmentType: newValue,
|
||||
mobileProviderNumber: null,
|
||||
provider: this.selectedProvider,
|
||||
|
|
@ -326,9 +324,9 @@ export default {
|
|||
zipCode: this.zipCode,
|
||||
zipCodeCtu: this.zipCodeCtu
|
||||
};
|
||||
this.$emit('appointment-type-changed', appointmenTypeServiceLocationObj);
|
||||
this.$emit('appointment-type-changed', appointmentTypeServiceLocationObj);
|
||||
} else {
|
||||
const appointmenTypeServiceLocationObj = {
|
||||
const appointmentTypeServiceLocationObj = {
|
||||
appointmentType: newValue,
|
||||
mobileProviderNumber: this.mobileProviderNumber,
|
||||
provider: null,
|
||||
|
|
@ -337,20 +335,31 @@ export default {
|
|||
zipCode: this.zipCode,
|
||||
zipCodeCtu: this.zipCodeCtu
|
||||
};
|
||||
this.$emit('appointment-type-changed', appointmenTypeServiceLocationObj);
|
||||
this.$emit('appointment-type-changed', appointmentTypeServiceLocationObj);
|
||||
}
|
||||
},
|
||||
selectedProvider(newProvider, oldProvider) {
|
||||
if (newProvider?.providerNumber !== oldProvider?.providerNumber || this.availabilityRating === null) {
|
||||
if (newProvider?.providerNumber !== oldProvider?.providerNumber) {
|
||||
const appointmentIsInshop = this.isInshop;
|
||||
const returnedProvider = {
|
||||
provider: newProvider,
|
||||
refreshDatePicker: appointmentIsInshop && oldProvider?.providerNumber !== null
|
||||
refreshDatePicker: appointmentIsInshop && oldProvider?.providerNumber !== null,
|
||||
state: this.state,
|
||||
zipCode: this.zipCode,
|
||||
zipCodeCtu: this.zipCodeCtu,
|
||||
appointmentType: this.selectedAppointmentType,
|
||||
provider: !this.isMobile ? newProvider : {
|
||||
providerNumber: this.mobileProviderNumber,
|
||||
address: {
|
||||
streetAddress: null,
|
||||
city: null,
|
||||
state: null,
|
||||
zipCode: null,
|
||||
zipCodeCtu: null
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (newProvider?.providerNumber !== oldProvider?.providerNumber) {
|
||||
this.$emit('provider-changed', returnedProvider);
|
||||
}
|
||||
this.$emit('provider-changed', returnedProvider);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -360,34 +369,6 @@ export default {
|
|||
await this.setData(initialData);
|
||||
this.initializingData = false;
|
||||
},
|
||||
async forwardButtonAction(appointmentType) {
|
||||
let provider = this.selectedProvider;
|
||||
this.mainStore.updateMobileFee(null);
|
||||
if (this.isMobile) {
|
||||
if (this.mainStore.isNoComp || this.mainStore.isITAC) {
|
||||
this.mainStore.updateMobileFee(this.mobileFeePart);
|
||||
}
|
||||
|
||||
provider = {
|
||||
providerNumber: this.mobileProviderNumber,
|
||||
address: {
|
||||
streetAddress: null,
|
||||
city: null,
|
||||
state: null,
|
||||
zipCode: null,
|
||||
zipCodeCtu: null
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
this.mainStore.saveServiceLocation({
|
||||
state: this.state,
|
||||
zipCode: this.zipCode,
|
||||
zipCodeCtu: this.zipCodeCtu,
|
||||
appointmentType: appointmentType || this.selectedAppointmentType,
|
||||
provider
|
||||
});
|
||||
},
|
||||
openModalAction(modalName) {
|
||||
this.$refs[modalName].openModal();
|
||||
},
|
||||
|
|
@ -427,7 +408,7 @@ export default {
|
|||
this.setCtuForMobile(zipCodeData.zipCodeCtu);
|
||||
this.setContainsMilitaryBase(zipCodeData.containsMilitaryBase);
|
||||
|
||||
const serviceabilityDetailsPromise = getServiceabilityDetails(this.zipCode);
|
||||
const serviceabilityDetailsPromise = this.mainStore.getServiceabilityDetails(this.zipCode);
|
||||
await serviceabilityDetailsPromise.then((result) => {
|
||||
const details = result.data;
|
||||
if (details) {
|
||||
|
|
@ -465,10 +446,6 @@ export default {
|
|||
this.setServiceabilityDetails(initialData.serviceabilityDetails);
|
||||
}
|
||||
|
||||
if (initialData.mobileFeePart) {
|
||||
this.mobileFeePart = initialData.mobileFeePart;
|
||||
}
|
||||
|
||||
if (initialData.providers) {
|
||||
this.setMobileProviderNumber(initialData.providers.mobileProviderNumber);
|
||||
let foundMatch = false;
|
||||
|
|
@ -485,22 +462,12 @@ export default {
|
|||
this.selectedProvider = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (initialData.glassFees) {
|
||||
useMainStore().getCombinedQuote(initialData.glassFees)
|
||||
.then((combinedQuote) => {
|
||||
this.setGlassFeeItems(combinedQuote);
|
||||
});
|
||||
}
|
||||
},
|
||||
setContainsMilitaryBase(val) {
|
||||
if (this.zipContainsMilitaryBase !== val) {
|
||||
this.zipContainsMilitaryBase = val;
|
||||
}
|
||||
},
|
||||
setGlassFeeItems(newGlassFeeItems) {
|
||||
this.mainStore.updateGlassFees(newGlassFeeItems);
|
||||
},
|
||||
setMobileProviderNumber(providerNumber) {
|
||||
this.mobileProviderNumber = providerNumber;
|
||||
},
|
||||
|
|
@ -531,7 +498,7 @@ export default {
|
|||
this.setCtuForMobile(zipCodeData.zipCodeCtu);
|
||||
this.setContainsMilitaryBase(zipCodeData.containsMilitaryBase);
|
||||
|
||||
const serviceabilityDetailsPromise = getServiceabilityDetails(this.mobileZipCode);
|
||||
const serviceabilityDetailsPromise = this.mainStore.getServiceabilityDetails(this.mobileZipCode);
|
||||
await serviceabilityDetailsPromise.then((result) => {
|
||||
const details = result.data;
|
||||
if (details) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<textboxQuestion
|
||||
ref="zipInputTextQuestion"
|
||||
v-model="internalZipcode"
|
||||
:alwaysEmitFieldErrorOnClick="true"
|
||||
:alwaysEmitFieldErrorOnEvent="true"
|
||||
:cmsWidgetName="cmsWidgetName"
|
||||
inputId="serviceZipCode"
|
||||
cornerStyle="rounded"
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ export default {
|
|||
|
||||
const wipersPromise = await store.getWipers();
|
||||
const rainDefensePromise = await store.getRainDefense();
|
||||
const supportingItemsPromise = await store.getSupportingItems();
|
||||
const promiseResultMap = [
|
||||
{
|
||||
resultKey: 'cmsContent',
|
||||
|
|
@ -115,10 +116,18 @@ export default {
|
|||
{
|
||||
resultKey: 'rainDefense',
|
||||
promise: rainDefensePromise
|
||||
},
|
||||
{
|
||||
resultKey: 'supportingItems',
|
||||
promise: supportingItemsPromise
|
||||
}
|
||||
];
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
|
||||
if (resultMap.supportingItems) {
|
||||
useMainStore().updateSupportingItems(resultMap.supportingItems);
|
||||
}
|
||||
|
||||
const { feeItems } = store.order.lineItems;
|
||||
|
||||
const availableLineItems = [];
|
||||
|
|
|
|||
|
|
@ -53,7 +53,8 @@ const loaderStub = {
|
|||
function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRun = () => {}) {
|
||||
const mountOptions = getMountOptions({
|
||||
router: {
|
||||
navigate: jest.fn()
|
||||
navigate: jest.fn(),
|
||||
navigateBailout: jest.fn()
|
||||
},
|
||||
route: {
|
||||
query: { issPage: 'tpa-search' }
|
||||
|
|
@ -787,7 +788,7 @@ describe('tpa-search.vue', () => {
|
|||
});
|
||||
|
||||
describe('needHelpLinkClick', () => {
|
||||
test('sets bailout and navigates', () => {
|
||||
test('navigatesBailout', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
|
||||
|
|
@ -795,11 +796,7 @@ describe('tpa-search.vue', () => {
|
|||
wrapper.vm.needHelpLinkClick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.mainStore.setBailout).toHaveBeenCalled();
|
||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
|
||||
navigationScenarios.CLICKED_NEED_HELP,
|
||||
expect.anything()
|
||||
);
|
||||
expect(wrapper.vm.$router.navigateBailout).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -165,7 +165,9 @@ export default {
|
|||
},
|
||||
mixins: [BaseFormMixin],
|
||||
async beforeRouteEnter(to, from, next) {
|
||||
const zipCode = useMainStore().order.customer.address.zipCode;
|
||||
const serviceZipCode = useMainStore().order.serviceLocation.zipCode;
|
||||
const customerZipCode = useMainStore().order.customer.address.zipCode;
|
||||
const zipCode = serviceZipCode || customerZipCode;
|
||||
const pageData = useMainStore().pageData(to.query.issPage);
|
||||
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
|
||||
const providersPromise = useMainStore().getTpaAndSafeliteProviders(zipCode, pageData?.tpaSearchValue ?? '');
|
||||
|
|
@ -295,7 +297,7 @@ export default {
|
|||
const provider = this.providers?.find((p) => p.providerNumber === newNumber);
|
||||
if (provider && this.dataLoaded) {
|
||||
useMainStore().updateServiceLocation({
|
||||
zipCode: this.zipCode,
|
||||
zipCode: this.mapZipCode,
|
||||
provider: {
|
||||
providerNumber: provider?.providerNumber,
|
||||
address: {
|
||||
|
|
@ -358,11 +360,7 @@ export default {
|
|||
return getTpaProvidersResult?.data ?? [];
|
||||
},
|
||||
needHelpLinkClick() {
|
||||
this.mainStore.setBailout(bailoutMessage.RequestCallback());
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_NEED_HELP,
|
||||
this.$route
|
||||
);
|
||||
this.$router.navigateBailout(bailoutMessage.RequestCallback());
|
||||
},
|
||||
async searchClick() {
|
||||
if (isNaN(this.tpaSearchValue)) {
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ import buttonMain from '@/ux-components/button-main/button-main.vue';
|
|||
import textBlock from '@/digital-components/text-block/text-block.vue';
|
||||
import reviewBlock from '@/layouts/tpa-submit/review-block/review-block.vue';
|
||||
import deductibleBox from '@/layouts/tpa-submit/deductible-box/deductible-box.vue';
|
||||
import contactDetailsDrawer from '@/layouts/tpa-submit/contact-details-drawer/contact-details-drawer.vue';
|
||||
import contactDetailsDrawer from '@/iss-components/contact-details-drawer/contact-details-drawer.vue';
|
||||
import siteFooter from '@/iss-components/site-footer/site-footer.vue';
|
||||
import alert from '@/ux-components/alert/alert.vue';
|
||||
import modal from '@/digital-components/modal/modal.vue';
|
||||
|
|
@ -315,20 +315,8 @@ export default {
|
|||
};
|
||||
},
|
||||
async forwardButtonAction() {
|
||||
try {
|
||||
await submitWorkOrder({ submitType: submitType.TPA }).then(() => {
|
||||
this.navigate(this.navigationScenarios.CLICKED_FORWARD);
|
||||
}).catch((submitError) => {
|
||||
this.mainStore.setBailout(bailoutMessage.saveSessionError(submitError.data));
|
||||
this.navigate(
|
||||
this.navigationScenarios.SAVE_SESSION_FAILED,
|
||||
this.$route,
|
||||
{ issPage: this.issPageValues.TPA_SUBMIT }
|
||||
);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`error: response from submit work order:${error.message}`);
|
||||
}
|
||||
await submitWorkOrder({ submitType: submitType.TPA })
|
||||
this.navigate(this.navigationScenarios.CLICKED_FORWARD);
|
||||
},
|
||||
navigate(scenario) {
|
||||
this.$router.navigate(scenario, this.$route);
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ describe('vehicle-damage.vue', () => {
|
|||
expect(vehicleQuestionsMixin.methods.getPartsOrQuestions).toHaveBeenCalledTimes(1);
|
||||
expect(vehicleQuestionsMixin.methods.navigateForward).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
test('Error in getPartsOrQuestions call => bailout true and navigate forward with CLICKED_FORWARD_WITH_BAILOUT scenario', async () => {
|
||||
test('Error in getPartsOrQuestions call => error thrown in forwardButtonAction', async () => {
|
||||
mountOptions.global.plugins = [createTestingPinia({
|
||||
initialState: {
|
||||
main: {
|
||||
|
|
@ -154,23 +154,21 @@ describe('vehicle-damage.vue', () => {
|
|||
}
|
||||
}
|
||||
})];
|
||||
mountOptions.data = () => ({
|
||||
hasBailedOut: true
|
||||
});
|
||||
mountOptions.data = () => ({});
|
||||
|
||||
const wrapper = mount(VehicleDamageComponent, mountOptions);
|
||||
const siteFooterWrapper = wrapper.getComponent({ ref: 'siteFooter' });
|
||||
const partsQuestionsErrorResponse = {
|
||||
error: 'Error getting parts'
|
||||
};
|
||||
vehicleQuestionsMixin.methods.getPartsOrQuestions.mockImplementation(() => (
|
||||
partsQuestionsErrorResponse
|
||||
Promise.reject(partsQuestionsErrorResponse)
|
||||
));
|
||||
siteFooterWrapper.vm.$emit('forwardClicked');
|
||||
|
||||
await flushPromises();
|
||||
expect(mockRouter.navigate).toHaveBeenCalledTimes(1);
|
||||
expect(mockRouter.navigate)
|
||||
.toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, mockRoute);
|
||||
expect.assertions(1);
|
||||
try {
|
||||
await wrapper.vm.forwardButtonAction();
|
||||
} catch (e) {
|
||||
expect(e).toBe(partsQuestionsErrorResponse);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -189,8 +189,7 @@ export default {
|
|||
this.getPassengerSideReplaceOptionsFromStore()
|
||||
},
|
||||
selectedWindshieldOptions: this.getWindshieldOptionsFromStore(),
|
||||
selectedRearReplaceOptions: this.getRearReplaceOptionsFromStore(),
|
||||
hasBailedOut: false
|
||||
selectedRearReplaceOptions: this.getRearReplaceOptionsFromStore()
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -429,23 +428,12 @@ export default {
|
|||
// If vin already exists or not replacing windshield, get parts/questions and navigate forward
|
||||
|
||||
const partsOrQuestionsResponse = await this.getPartsOrQuestions();
|
||||
if (partsOrQuestionsResponse.error) {
|
||||
this.mainStore.setBailout(bailoutMessage.PartsServiceError(partsOrQuestionsResponse.error.data));
|
||||
window.console.error('Error on retrieving PartsOrQuestions');
|
||||
this.hasBailedOut = true;
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
|
||||
this.$route
|
||||
);
|
||||
}
|
||||
|
||||
// Comes from vehicleQuestionsMixin.navigateForward()
|
||||
if (!this.hasBailedOut) {
|
||||
await this.navigateForward(
|
||||
partsOrQuestionsResponse.data.partsOrQuestions,
|
||||
this
|
||||
);
|
||||
}
|
||||
await this.navigateForward(
|
||||
partsOrQuestionsResponse.data.partsOrQuestions,
|
||||
this
|
||||
);
|
||||
} else {
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD_WITHOUT_VIN,
|
||||
|
|
|
|||
|
|
@ -113,21 +113,10 @@ export default {
|
|||
break;
|
||||
case vinLookupMethodSelections.NOVIN:
|
||||
const partsOrQuestionsResponse = await this.getPartsOrQuestions();
|
||||
if (partsOrQuestionsResponse.error) {
|
||||
this.mainStore.setBailout(bailoutMessage.PartsServiceError(partsOrQuestionsResponse.error.data));
|
||||
window.console.error('Error on retrieving PartsOrQuestions');
|
||||
this.hasBailedOut = true;
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
|
||||
this.$route
|
||||
);
|
||||
}
|
||||
else {
|
||||
await this.navigateForward(
|
||||
partsOrQuestionsResponse.data.partsOrQuestions,
|
||||
this
|
||||
);
|
||||
}
|
||||
await this.navigateForward(
|
||||
partsOrQuestionsResponse.data.partsOrQuestions,
|
||||
this
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
v-for="(tint, index) in tintSelectionOptions"
|
||||
:key="index">
|
||||
<div class="tint-option">
|
||||
<span>{{ `${partType} - ${tint}` }}</span>
|
||||
<div class="tint-option-text">{{ `${partType} - ${tint}` }}</div>
|
||||
<span :class="`tint-image ${convertTintToCSSClass(tint)}`"></span>
|
||||
</div>
|
||||
<buttonQuestion
|
||||
|
|
@ -59,7 +59,7 @@ export default {
|
|||
partValidationRules() {
|
||||
const validationRuleName = `${this.glassLocation}-${this.glassName}-part-required`;
|
||||
|
||||
defineRule(validationRuleName, required(errorMessages.OPTION_REQUIRED));
|
||||
defineRule(validationRuleName, required(`${this.partType} not selected`));
|
||||
return validationRuleName;
|
||||
},
|
||||
|
||||
|
|
@ -155,7 +155,7 @@ export default {
|
|||
return tint.toLowerCase().replaceAll(' ', '-').replaceAll(',', '');
|
||||
},
|
||||
toTitleCaseWithExceptions(text) {
|
||||
const exceptions = ['side', 'glass', 'dimming'];
|
||||
const exceptions = ['side', 'glass', 'dimming', 'BPassenger', 'soundproofing'];
|
||||
let temp = toTitleCase(text);
|
||||
exceptions.forEach((exception) => {
|
||||
const regEx = new RegExp(exception, 'ig');
|
||||
|
|
@ -176,7 +176,8 @@ export default {
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
height: 2rem;
|
||||
position: relative;
|
||||
min-height: 2rem;
|
||||
margin-top: .5rem;
|
||||
margin-bottom: .25rem;
|
||||
font-weight: 600;
|
||||
|
|
@ -185,19 +186,48 @@ export default {
|
|||
@include media-breakpoint-up(md) {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.tint-option-text {
|
||||
padding-right: 2.5rem;
|
||||
}
|
||||
}
|
||||
.radioQuestion {
|
||||
:deep(.list-button) {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
:deep(.form-test-error) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
:deep(.form-test-error) {
|
||||
position: absolute;
|
||||
margin: -10px 0 10px 10px;
|
||||
font-weight: 600;
|
||||
> * {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.tint-image {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
margin-left: 1rem;
|
||||
margin-top: -.25rem;
|
||||
background-repeat: no-repeat;
|
||||
background-image: url(~@/assets/img/tints/GlassTint.png);
|
||||
position: absolute;
|
||||
top: -.25rem;
|
||||
right: 1rem;
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
position: relative;
|
||||
top: -.25rem;
|
||||
right: 1.5rem;
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
.blue-tint-green-shade {
|
||||
background-position: -53px -53px;
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin';
|
|||
import BaseFormMixin from '@/mixins/base-form-mixin.js';
|
||||
import { useMainStore } from '@/store';
|
||||
import widgetFields from '@/constants/cms-widget-fields';
|
||||
import bailoutMessage from '@/constants/bailoutMessage';
|
||||
|
||||
export default {
|
||||
name: 'vehicle-parts',
|
||||
|
|
@ -213,8 +214,7 @@ export default {
|
|||
});
|
||||
},
|
||||
requestCallbackBailout() {
|
||||
this.mainStore.setBailout(bailoutMessage.RequestCallback());
|
||||
this.$router.navigate(navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT, this.$route);
|
||||
this.$router.navigateBailout(bailoutMessage.RequestCallback());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -163,16 +163,18 @@ export default {
|
|||
}
|
||||
},
|
||||
selectedStyle(value) {
|
||||
this.resetAlert();
|
||||
this.mainStore.updateVehicleStyle(value);
|
||||
this.mainStore.setVehicle(
|
||||
this.selectedYear,
|
||||
this.selectedMake,
|
||||
this.selectedModel,
|
||||
this.selectedStyle
|
||||
).then((result) => {
|
||||
this.displayNoServiceAlert = !result.data.canSafeliteService;
|
||||
});
|
||||
if (value) {
|
||||
this.resetAlert();
|
||||
this.mainStore.updateVehicleStyle(value);
|
||||
this.mainStore.setVehicle(
|
||||
this.selectedYear,
|
||||
this.selectedMake,
|
||||
this.selectedModel,
|
||||
this.selectedStyle
|
||||
).then((result) => {
|
||||
this.displayNoServiceAlert = !result.data.canSafeliteService;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
|
@ -206,77 +208,25 @@ export default {
|
|||
},
|
||||
|
||||
navigateForward() {
|
||||
this.mainStore.setVehicle().then(
|
||||
() => {
|
||||
if (this.mainStore.isBailout) {
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
|
||||
this.$route
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.mainStore.setVehicle().then(() => {
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD,
|
||||
this.$route
|
||||
);
|
||||
},
|
||||
(error) => {
|
||||
this.mainStore.setBailout(bailoutMessage.vehicleYMMSLookupError(this.mainStore.vehicle.year, this.mainStore.vehicle.make, this.mainStore.vehicle.model, this.mainStore.vehicle.style, error));
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
|
||||
this.$route
|
||||
);
|
||||
}
|
||||
);
|
||||
},
|
||||
async updateYearValues() {
|
||||
return this.mainStore.getVehicleYears().then(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
this.mainStore.setBailout(bailoutMessage.vehicleYMMSLookupError(null, null, null, null, error));
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
|
||||
this.$route
|
||||
);
|
||||
}
|
||||
);
|
||||
return this.mainStore.getVehicleYears();
|
||||
},
|
||||
async updateMakeValues() {
|
||||
return this.mainStore.getVehicleMakes().then(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
this.mainStore.setBailout(bailoutMessage.vehicleYMMSLookupError(this.mainStore.vehicle.year, null, null, null, error));
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
|
||||
this.$route
|
||||
);
|
||||
}
|
||||
);
|
||||
return this.mainStore.getVehicleMakes();
|
||||
},
|
||||
async updateModelValues() {
|
||||
return this.mainStore.getVehicleModels().then(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
this.mainStore.setBailout(bailoutMessage.vehicleYMMSLookupError(this.mainStore.vehicle.year, this.mainStore.vehicle.make, null, null, error));
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
|
||||
this.$route
|
||||
);
|
||||
}
|
||||
);
|
||||
return this.mainStore.getVehicleModels();
|
||||
},
|
||||
async updateStyleValues() {
|
||||
return this.mainStore.getVehicleStyles().then(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
this.mainStore.setBailout(bailoutMessage.vehicleYMMSLookupError(this.mainStore.vehicle.year, this.mainStore.vehicle.make, this.mainStore.vehicle.model, null, error));
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
|
||||
this.$route
|
||||
);
|
||||
}
|
||||
);
|
||||
return this.mainStore.getVehicleStyles();
|
||||
},
|
||||
resetAlert() {
|
||||
this.displayNoServiceAlert = false;
|
||||
|
|
|
|||
|
|
@ -148,7 +148,8 @@ const mockRoute = {
|
|||
};
|
||||
const mockRouter = {
|
||||
navigate: jest.fn(),
|
||||
navigateWithSpinner: jest.fn()
|
||||
navigateWithSpinner: jest.fn(),
|
||||
navigateBailout: jest.fn()
|
||||
};
|
||||
const maska = jest.fn();
|
||||
jest.mock('@/helpers/damage-helper', () => ({
|
||||
|
|
@ -553,45 +554,6 @@ describe('vin-lookup.vue', () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
test(
|
||||
'Error in getPartsOrQuestions call => bailout true and navigate forward with CLICKED_FORWARD_WITH_BAILOUT scenario',
|
||||
async () => {
|
||||
const user = userEvent.setup();
|
||||
mountOptions.global.stubs.vinQuestion = false;
|
||||
|
||||
mountOptions.data = () => ({
|
||||
vinWithNonMatchingCarId: false,
|
||||
isCarIdDifferentFromTheStore: false,
|
||||
vin: mockValidVin,
|
||||
hasBailedOut: true
|
||||
});
|
||||
|
||||
getPartsOrQuestions.mockResponse = partsOrQuestionsErrorMockResponse;
|
||||
|
||||
jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName)
|
||||
.mockResolvedValue(lookupVehicleByVin.mockResponse);
|
||||
jest.spyOn(vehicleQuestionsMixin.methods, getPartsOrQuestions.methodName)
|
||||
.mockResolvedValue(getPartsOrQuestions.mockResponse);
|
||||
|
||||
const { container } = render(VinLookupComponent, mountOptions);
|
||||
|
||||
const vinInput = container.querySelector(vinInputSelector);
|
||||
await user.type(vinInput, mockValidVin);
|
||||
|
||||
const continueButton = container.querySelector(continueButtonQuerySelector);
|
||||
await user.click(continueButton);
|
||||
|
||||
await flushPromises();
|
||||
await waitFor(() => {
|
||||
expect(mockRouter.navigate).toHaveBeenCalledTimes(1);
|
||||
expect(mockRouter.navigate)
|
||||
.toHaveBeenCalledWith(
|
||||
navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
|
||||
mockRoute
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -112,8 +112,7 @@ export default {
|
|||
vinWithNonMatchingCarId: vin?.length > 0 && !this.hasValidCarId(),
|
||||
vin,
|
||||
forwardButtonCarStyle: '',
|
||||
vinPopulatedOnPageLoad: vin?.length > 0 && this.hasValidCarId(),
|
||||
hasBailedOut: false
|
||||
vinPopulatedOnPageLoad: vin?.length > 0 && this.hasValidCarId()
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -176,35 +175,37 @@ export default {
|
|||
showIssLoadingModal(true);
|
||||
|
||||
if (this.needToLookupVehicle) {
|
||||
const vehicleLookupResponse = await this.lookupVehicleByVin(this.vin);
|
||||
try {
|
||||
const vehicleLookupResponse = await this.lookupVehicleByVin(this.vin);
|
||||
|
||||
if (vehicleLookupResponse.error) {
|
||||
this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.NOT_FOUND;
|
||||
this.mainStore.setBailout(bailoutMessage.vehicleNotFound(this.vin));
|
||||
this.resetVehicleFromLookup();
|
||||
// Temp solution to turn on 'disabled' style on the Continue button
|
||||
// because the form itself actually passes its client-side validation.
|
||||
// SSR-189 Scenario #4.
|
||||
this.$refs.siteFooter.enableForwardAction();
|
||||
showIssLoadingModal(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!vehicleLookupResponse.data.canSafeliteService) {
|
||||
this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.NO_SERVICE;
|
||||
this.resetVehicleFromLookup();
|
||||
this.$refs.siteFooter.disableForwardButton();
|
||||
showIssLoadingModal(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add vin bcs the response from the service doesn't contain vin
|
||||
this.vehicleFromLookup = Object.assign(
|
||||
vehicleLookupResponse.data,
|
||||
{
|
||||
vin: this.vin
|
||||
if (!vehicleLookupResponse.data.canSafeliteService) {
|
||||
this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.NO_SERVICE;
|
||||
this.resetVehicleFromLookup();
|
||||
this.$refs.siteFooter.disableForwardButton();
|
||||
showIssLoadingModal(false);
|
||||
return;
|
||||
}
|
||||
);
|
||||
|
||||
// Add vin bcs the response from the service doesn't contain vin
|
||||
this.vehicleFromLookup = Object.assign(
|
||||
vehicleLookupResponse.data,
|
||||
{
|
||||
vin: this.vin
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
if (e.status === 404) {
|
||||
this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.NOT_FOUND;
|
||||
this.resetVehicleFromLookup();
|
||||
// Temp solution to turn on 'disabled' style on the Continue button
|
||||
// because the form itself actually passes its client-side validation.
|
||||
// SSR-189 Scenario #4.
|
||||
this.$refs.siteFooter.enableForwardAction();
|
||||
showIssLoadingModal(false);
|
||||
return;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.needToLookupVehicle && this.isCarIdDifferentFromTheStore) {
|
||||
|
|
@ -259,34 +260,15 @@ export default {
|
|||
}
|
||||
|
||||
const partsOrQuestionsResponse = await this.getPartsOrQuestions();
|
||||
if (partsOrQuestionsResponse.error) {
|
||||
this.mainStore.setBailout(bailoutMessage.PartsServiceError(partsOrQuestionsResponse.error.data));
|
||||
window.console.error('Error on retrieving PartsOrQuestions');
|
||||
this.hasBailedOut = true;
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
|
||||
this.$route
|
||||
);
|
||||
}
|
||||
|
||||
// Comes from vehicleQuestionsMixin.navigateForward()
|
||||
if (!this.hasBailedOut) {
|
||||
await this.navigateForward(
|
||||
partsOrQuestionsResponse.data.partsOrQuestions,
|
||||
this
|
||||
);
|
||||
}
|
||||
await this.navigateForward(
|
||||
partsOrQuestionsResponse.data.partsOrQuestions,
|
||||
this
|
||||
);
|
||||
},
|
||||
async lookupVehicleByVin(vin) {
|
||||
try {
|
||||
return await this.mainStore.lookupVehicleByVin(vin);
|
||||
} catch (responseError) {
|
||||
return {
|
||||
error: {
|
||||
status: responseError.status
|
||||
}
|
||||
};
|
||||
}
|
||||
return this.mainStore.lookupVehicleByVin(vin);
|
||||
},
|
||||
resetActiveAlert() {
|
||||
this.activeVehicleLookupAlertType = null;
|
||||
|
|
|
|||
|
|
@ -72,7 +72,8 @@ function setupMocks({
|
|||
const mockDataMountOptions = {
|
||||
...mountOptionsMockData,
|
||||
router: {
|
||||
navigate: jest.fn()
|
||||
navigate: jest.fn(),
|
||||
navigateBailout: jest.fn()
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -166,7 +167,7 @@ describe('navigation', () => {
|
|||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.mainStore.getDuplicateReferrals = jest.fn().mockImplementation(() => Promise.resolve({}));
|
||||
wrapper.vm.mainStore.applicationUser.duplicateOrders = [{ test: 'a' }];
|
||||
wrapper.vm.mainStore.validateZip = jest.fn().mockImplementation(() => Promise.reject());
|
||||
wrapper.vm.mainStore.validateZip = jest.fn().mockImplementation(() => Promise.reject({ isAxiosError: false }));
|
||||
|
||||
// Act
|
||||
await wrapper.vm.forwardButtonAction();
|
||||
|
|
@ -316,10 +317,14 @@ describe('navigation', () => {
|
|||
}));
|
||||
|
||||
// Act
|
||||
await wrapper.vm.forwardButtonAction();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
|
||||
expect.assertions(1);
|
||||
try {
|
||||
await wrapper.vm.forwardButtonAction();
|
||||
} catch (e) {
|
||||
expect(e).toMatch(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -177,6 +177,7 @@ import { getPropertyCaseInsensitive } from '@/helpers/object-helper';
|
|||
import { saveSession } from '@/helpers/order-helper';
|
||||
import { getISSCookie } from '@/helpers/cookie-helper.js';
|
||||
import bailoutMessage from '@/constants/bailoutMessage';
|
||||
import showIssLoadingModal from '@/helpers/loading-modal-helper';
|
||||
|
||||
// define validation rules
|
||||
defineRule(
|
||||
|
|
@ -303,54 +304,45 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
async forwardButtonAction() {
|
||||
try {
|
||||
this.mainStore.updatePolicyData(this.welcomePageModel);
|
||||
const promises = [];
|
||||
promises.push(this.configureZip().then(async () => await this.mainStore.getBillToInfo()));
|
||||
this.mainStore.updatePolicyData(this.welcomePageModel);
|
||||
|
||||
// Clear duplicate orders if navigating away from the welcome page after visiting duplicate check page.
|
||||
if (this.mainStore.order.visitedDuplicateCheckPage) {
|
||||
this.mainStore.clearDuplicateOrders();
|
||||
}
|
||||
|
||||
// Skip duplicate check if loaded from cookie or already visited duplicate check page.
|
||||
if (!this.mainStore.order.loadedFromCookie && !this.mainStore.order.visitedDuplicateCheckPage) {
|
||||
promises.push(this.mainStore.getDuplicateReferrals());
|
||||
}
|
||||
promises.push(this.mainStore.getCoveragePolicyInfo());
|
||||
await Promise.allSettled(promises);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
// TODO: Bailout?
|
||||
} finally {
|
||||
if (!this.displayInvalidZipAlert) {
|
||||
await saveSession({ shouldAwaitSaveSessionQueue: true })
|
||||
.catch((error) => {
|
||||
this.mainStore.setBailout(bailoutMessage.saveSessionError(error.data));
|
||||
})
|
||||
.finally(() => this.navigateForward());
|
||||
}
|
||||
// We want to await this separately. It's quick and if this has an invalid zip we don't want to be waiting on the slower API calls
|
||||
await this.configureZip();
|
||||
if (this.displayInvalidZipAlert) {
|
||||
showIssLoadingModal(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear duplicate orders if navigating away from the welcome page after visiting duplicate check page.
|
||||
if (this.mainStore.order.visitedDuplicateCheckPage) {
|
||||
this.mainStore.clearDuplicateOrders();
|
||||
}
|
||||
|
||||
const promises = [
|
||||
this.mainStore.getBillToInfo()
|
||||
];
|
||||
// Skip duplicate check if loaded from cookie or already visited duplicate check page.
|
||||
if (!this.mainStore.order.loadedFromCookie && !this.mainStore.order.visitedDuplicateCheckPage) {
|
||||
promises.push(this.mainStore.getDuplicateReferrals());
|
||||
}
|
||||
promises.push(this.mainStore.getCoveragePolicyInfo());
|
||||
await Promise.all(promises);
|
||||
await saveSession({ shouldAwaitSaveSessionQueue: true, bailoutOnError: true })
|
||||
this.navigateForward();
|
||||
},
|
||||
async configureZip() {
|
||||
try {
|
||||
this.displayInvalidZipAlert = false;
|
||||
await this.mainStore.validateZip({ zip: this.welcomePageModel.policyZipCode });
|
||||
return Promise.resolve();
|
||||
} catch (e) {
|
||||
if (e.isAxiosError) {
|
||||
throw e;
|
||||
}
|
||||
this.displayInvalidZipAlert = true;
|
||||
return Promise.reject(e);
|
||||
}
|
||||
},
|
||||
navigateForward() {
|
||||
if (this.mainStore.isBailout) {
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.SAVE_SESSION_FAILED,
|
||||
this.$route,
|
||||
{},
|
||||
{ [routerParams.SKIP_SAVE_SESSION]: true }
|
||||
);
|
||||
} else if ((this.mainStore.applicationUser.duplicateOrders?.length > 0 ?? false)
|
||||
if ((this.mainStore.applicationUser.duplicateOrders?.length > 0 ?? false)
|
||||
&& !this.answeredContinueModal) {
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD_WITH_DUPLICATES,
|
||||
|
|
@ -445,12 +437,7 @@ export default {
|
|||
.then((response) => {
|
||||
this.answeredContinueModal = true;
|
||||
if (response && !response.provider?.isSafeliteProvider) {
|
||||
this.mainStore.setBailout(bailoutMessage.SafeliteNotTheProvider());
|
||||
this.hasBailedOut = true;
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
|
||||
this.$route
|
||||
);
|
||||
this.$router.navigateBailout(bailoutMessage.SafeliteNotTheProvider());
|
||||
} else {
|
||||
this.updateWelcomePageModel(response);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ function getPageName(vm) {
|
|||
// Vue Error Handling
|
||||
vueApp.config.errorHandler = (err, vm, info) => {
|
||||
const pageName = getPageName(vm);
|
||||
global.$logger.logError(`Page Name - ${pageName} - ${info}: ${err.message}\n${err.stack}`);
|
||||
global.$logger.logError(`[${pageName}] ${info}`, err);
|
||||
if (applicationConfig.BAILOUT_ON_APPLICATION_ERROR) {
|
||||
router.navigateBailout(bailoutMessage.applicationError(`[${pageName}] ${info}: ${err.message}\n${err.stack}`));
|
||||
}
|
||||
|
|
@ -56,7 +56,7 @@ vueApp.config.errorHandler = (err, vm, info) => {
|
|||
|
||||
// Vue Router Error Handling
|
||||
router.onError((err) => {
|
||||
global.$logger.logError(err.message, err.cause);
|
||||
global.$logger.logError('Router Error:', err);
|
||||
if (applicationConfig.BAILOUT_ON_ROUTER_ERROR) {
|
||||
router.navigateBailout(bailoutMessage.routerError(`${err.message}\n${err.stack}`));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -353,16 +353,7 @@ export default {
|
|||
},
|
||||
|
||||
async getPartsOrQuestions() {
|
||||
try {
|
||||
return await useMainStore().getPartsOrQuestions();
|
||||
} catch (responseError) {
|
||||
return {
|
||||
error: {
|
||||
status: responseError.status,
|
||||
data: responseError.data
|
||||
}
|
||||
};
|
||||
}
|
||||
return useMainStore().getPartsOrQuestions();
|
||||
},
|
||||
|
||||
// Can't use `this` because navigateForward is also called from vin-pages-mixin
|
||||
|
|
|
|||
|
|
@ -25,100 +25,95 @@ const routes = [
|
|||
path: '/',
|
||||
name: 'root',
|
||||
async beforeEnter(to, from, next) {
|
||||
try {
|
||||
const issPageToUse = !to.query.issPage ? issPageValues.WELCOME_PAGE : to.query.issPage;
|
||||
const fromQueryPage = from.query?.issPage;
|
||||
const issPageToUse = !to.query.issPage ? issPageValues.WELCOME_PAGE : to.query.issPage;
|
||||
const fromQueryPage = from.query?.issPage;
|
||||
|
||||
if ((issPageToUse === issPageValues.ACCESS_DENIED
|
||||
if ((issPageToUse === issPageValues.ACCESS_DENIED
|
||||
|| (issPageToUse !== issPageValues.ENTRY_PAGE && !useMainStore().issConfig.parentAccountNumber))
|
||||
&& process.env.VUE_APP_CURRENT_ENVIRONMENT !== 'Localhost'
|
||||
) {
|
||||
return await GoToAccessIsDenied(next);
|
||||
&& process.env.VUE_APP_CURRENT_ENVIRONMENT !== 'Localhost'
|
||||
) {
|
||||
return await GoToAccessIsDenied(next);
|
||||
}
|
||||
|
||||
// Do not run these for the main entry page - as it is not part of the user flow.
|
||||
if (issPageToUse !== issPageValues.ENTRY_PAGE) {
|
||||
if (analyticsMixin.methods.noSession()) {
|
||||
await analyticsMixin.methods.initSession();
|
||||
} else {
|
||||
updateSessionIdCookie();
|
||||
}
|
||||
|
||||
// Do not run these for the main entry page - as it is not part of the user flow.
|
||||
if (issPageToUse !== issPageValues.ENTRY_PAGE) {
|
||||
if (analyticsMixin.methods.noSession()) {
|
||||
await analyticsMixin.methods.initSession();
|
||||
} else {
|
||||
updateSessionIdCookie();
|
||||
}
|
||||
await runExperiments(issPageToUse); // fmg has this further down
|
||||
}
|
||||
|
||||
await runExperiments(issPageToUse); // fmg has this further down
|
||||
// Intercept all navigation if a submitted order exists in storage
|
||||
if (useMainStore().hasSubmittedOrder()) {
|
||||
if (to.query.issPage !== issPageValues.ENTRY_PAGE) {
|
||||
return await GoToConfirmationPage(next, useMainStore().getSubmittedOrder());
|
||||
}
|
||||
}
|
||||
|
||||
// If the saved session has timed out, clear the session, execute 404 logic.
|
||||
if (getISSCookie() !== null && !isSavedSessionStillActive()) {
|
||||
// await baseMixin.methods.dispatchStoreAction(storeActions.RESET_STATE);
|
||||
await GoToStartOn404(next);
|
||||
}
|
||||
|
||||
// Process ISS cookie.
|
||||
// Skip if Entry Page or Refreshing Welcome page
|
||||
if (issPageToUse !== issPageValues.ENTRY_PAGE
|
||||
&& !(issPageToUse === issPageValues.WELCOME_PAGE && fromQueryPage === undefined)) {
|
||||
updateOrCreateISSCookie();
|
||||
}
|
||||
|
||||
if (router.hasRoute(issPageToUse)) {
|
||||
// Since our route is already in scope, we can grab the component and call the arePagePrerequisitesValid function.
|
||||
let component = router.getRoutes().filter((x) => x.name === issPageToUse)[0].components;
|
||||
|
||||
// If the component hasn't been loaded fully, load it before we check prerequisites.
|
||||
if (component.default.methods === undefined) {
|
||||
component = await component.default();
|
||||
}
|
||||
|
||||
// Intercept all navigation if a submitted order exists in storage
|
||||
if (useMainStore().hasSubmittedOrder()) {
|
||||
if (to.query.issPage !== issPageValues.ENTRY_PAGE) {
|
||||
return await GoToConfirmationPage(next, useMainStore().getSubmittedOrder());
|
||||
}
|
||||
}
|
||||
|
||||
// If the saved session has timed out, clear the session, execute 404 logic.
|
||||
if (getISSCookie() !== null && !isSavedSessionStillActive()) {
|
||||
// await baseMixin.methods.dispatchStoreAction(storeActions.RESET_STATE);
|
||||
if (!arePagePrerequisitesValid(component)) {
|
||||
await GoToStartOn404(next);
|
||||
}
|
||||
|
||||
// Process ISS cookie.
|
||||
// Skip if Entry Page or Refreshing Welcome page
|
||||
if (issPageToUse !== issPageValues.ENTRY_PAGE
|
||||
&& !(issPageToUse === issPageValues.WELCOME_PAGE && fromQueryPage === undefined)) {
|
||||
updateOrCreateISSCookie();
|
||||
}
|
||||
|
||||
if (router.hasRoute(issPageToUse)) {
|
||||
// Since our route is already in scope, we can grab the component and call the arePagePrerequisitesValid function.
|
||||
let component = router.getRoutes().filter((x) => x.name === issPageToUse)[0].components;
|
||||
|
||||
// If the component hasn't been loaded fully, load it before we check prerequisites.
|
||||
if (component.default.methods === undefined) {
|
||||
component = await component.default();
|
||||
}
|
||||
|
||||
if (!arePagePrerequisitesValid(component)) {
|
||||
await GoToStartOn404(next);
|
||||
}
|
||||
|
||||
return next({ name: issPageToUse, query: to.query, params: to.params });
|
||||
}
|
||||
|
||||
const routeData = await GetRouteInfoFromPageName(issPageToUse);
|
||||
|
||||
if (routeData[0].name.toLowerCase() === 'error') {
|
||||
throw new Error('Page not found!');
|
||||
}
|
||||
|
||||
// Add our dynamic route.
|
||||
router.addRoute({
|
||||
path: routeData[0].path, // Always the same path, because we control it with query strings.
|
||||
name: routeData[0].name,
|
||||
component: routeData[0].component
|
||||
});
|
||||
|
||||
// Call the next components arePagePrerequisitesValid method before load.
|
||||
// If it returns false, use the 404 logic.
|
||||
const nextComponent = await router
|
||||
.getRoutes()
|
||||
.filter((x) => x.name === routeData[0].name)[0]
|
||||
.components.default();
|
||||
|
||||
if (!arePagePrerequisitesValid(nextComponent)) {
|
||||
const tempMsgCopy = 'Pre Requisites failed, need to handle. Default is Welcome Page.';
|
||||
const tempMsgHeadline = `${issPageToUse}: pre-req failed...`;
|
||||
await GoToStartOn404(next, tempMsgCopy, tempMsgHeadline);
|
||||
}
|
||||
|
||||
// Assign current query string parameters, as well as our issPage one.
|
||||
next({
|
||||
name: routeData[0].name,
|
||||
query: Object.assign(to.query, { issPage: routeData[0].name }),
|
||||
params: to.params
|
||||
});
|
||||
} catch (error) {
|
||||
window.console.warn(error);
|
||||
await GoToStartOn404(next);
|
||||
return next({name: issPageToUse, query: to.query, params: to.params});
|
||||
}
|
||||
|
||||
const routeData = await GetRouteInfoFromPageName(issPageToUse);
|
||||
|
||||
if (routeData[0].name.toLowerCase() === 'error') {
|
||||
throw new Error('Page not found!');
|
||||
}
|
||||
|
||||
// Add our dynamic route.
|
||||
router.addRoute({
|
||||
path: routeData[0].path, // Always the same path, because we control it with query strings.
|
||||
name: routeData[0].name,
|
||||
component: routeData[0].component
|
||||
});
|
||||
|
||||
// Call the next components arePagePrerequisitesValid method before load.
|
||||
// If it returns false, use the 404 logic.
|
||||
const nextComponent = await router
|
||||
.getRoutes()
|
||||
.filter((x) => x.name === routeData[0].name)[0]
|
||||
.components.default();
|
||||
|
||||
if (!arePagePrerequisitesValid(nextComponent)) {
|
||||
const tempMsgCopy = 'Pre Requisites failed, need to handle. Default is Welcome Page.';
|
||||
const tempMsgHeadline = `${issPageToUse}: pre-req failed...`;
|
||||
await GoToStartOn404(next, tempMsgCopy, tempMsgHeadline);
|
||||
}
|
||||
|
||||
// Assign current query string parameters, as well as our issPage one.
|
||||
next({
|
||||
name: routeData[0].name,
|
||||
query: Object.assign(to.query, {issPage: routeData[0].name}),
|
||||
params: to.params
|
||||
});
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -177,7 +172,7 @@ router.afterEach(async (to, from) => {
|
|||
|
||||
const skipSaveSession = !!router.options.history.state[routerParams.SKIP_SAVE_SESSION];
|
||||
if (from.name !== undefined && !skipSaveSession && !store.hasSubmittedOrder()) {
|
||||
await saveSession({});
|
||||
await saveSession({ bailoutOnError: from.name === issPageValues.ENTRY_PAGE});
|
||||
}
|
||||
|
||||
if (to.query.issPage !== issPageValues.ENTRY_PAGE) {
|
||||
|
|
@ -350,17 +345,12 @@ router.navigateBailout = (bailoutData = null) => {
|
|||
// Get navigation map depending on the scenario and the current 'page' you're on.
|
||||
function getNavigationMap(scenario, currentRoute) {
|
||||
const issPageValue = currentRoute.query.issPage;
|
||||
try {
|
||||
const matchedQueryValue = routingTable(useMainStore())
|
||||
.filter((item) => item.issPageValue === issPageValue && item.maps.filter((map) => map.scenario === scenario).length > 0);
|
||||
const matchedQueryValue = routingTable(useMainStore())
|
||||
.filter((item) => item.issPageValue === issPageValue && item.maps.filter((map) => map.scenario === scenario).length > 0);
|
||||
|
||||
const maps = matchedQueryValue ? matchedQueryValue.map((m) => m.maps.filter((map) => map.scenario === scenario))[0] : undefined;
|
||||
const maps = matchedQueryValue ? matchedQueryValue.map((m) => m.maps.filter((map) => map.scenario === scenario))[0] : undefined;
|
||||
|
||||
return maps ? maps.filter((x) => x.filter === true || x.filter === undefined)[0] : undefined;
|
||||
} catch (e) {
|
||||
window.console.error(e);
|
||||
return undefined;
|
||||
}
|
||||
return maps ? maps.filter((x) => x.filter === true || x.filter === undefined)[0] : undefined;
|
||||
}
|
||||
|
||||
async function GoToAccessIsDenied(next) {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ const navigationScenarios = Object.freeze({
|
|||
CLICKED_FORWARD_POLICY_UNVERIFIED: 'CLICKED_FORWARD_POLICY_UNVERIFIED',
|
||||
CLICKED_FORWARD_POLICY_VERIFIED_NO_VEHICLES: 'CLICKED_FORWARD_POLICY_VERIFIED_NO_VEHICLES',
|
||||
CLICKED_FORWARD_POLICY_VERIFIED_WITH_VEHICLES: 'CLICKED_FORWARD_POLICY_VERIFIED_WITH_VEHICLES',
|
||||
SAVE_SESSION_FAILED: 'SAVE_SESSION_FAILED',
|
||||
|
||||
// Duplicate Check
|
||||
CLICKED_FORWARD_LOADED_DUPLICATE_WITH_POLICY_VEHICLE: 'CLICKED_FORWARD_LOADED_DUPLICATE_WITH_POLICY_VEHICLE',
|
||||
|
|
@ -70,11 +69,8 @@ const navigationScenarios = Object.freeze({
|
|||
|
||||
// Coverage Statement
|
||||
CLICKED_BACK_WITH_REPAIR: 'CLICKED_BACK_WITH_REPAIR',
|
||||
CLICKED_FORWARD_WITH_INVALID_STATE: 'CLICKED_FORWARD_WITH_INVALID_STATE',
|
||||
PRICING_LOOKUP_ERROR: 'PRICING_LOOKUP_ERROR',
|
||||
|
||||
// TPA Search
|
||||
CLICKED_NEED_HELP: 'CLICKED_NEED_HELP',
|
||||
CLICKED_FORWARD_WITH_SAFELITE_SHOP: 'CLICKED_FORWARD_WITH_SAFELITE_SHOP',
|
||||
CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP: 'CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP',
|
||||
|
||||
|
|
@ -88,7 +84,6 @@ const navigationScenarios = Object.freeze({
|
|||
// Provider Preference
|
||||
CLICKED_FORWARD_WITH_SAFELITE: 'CLICKED_FORWARD_WITH_SAFELITE',
|
||||
CLICKED_FORWARD_WITH_TPA_ENABLED: 'CLICKED_FORWARD_WITH_TPA_ENABLED',
|
||||
CLICKED_FORWARD_WITH_TPA_DISABLED: 'CLICKED_FORWARD_WITH_TPA_DISABLED',
|
||||
CLICKED_FORWARD_WITH_POLICY_AND_VEHICLES: 'CLICKED_FORWARD_WITH_POLICY_AND_VEHICLES',
|
||||
|
||||
// Service Package
|
||||
|
|
@ -110,10 +105,11 @@ const navigationScenarios = Object.freeze({
|
|||
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',
|
||||
EDIT_SERVICE_LOCATION: 'EDIT_SERVICE_LOCATION',
|
||||
EDIT_SCHEDULE: 'EDIT_SCHEDULE',
|
||||
EDIT_WIPERS: 'EDIT_WIPERS',
|
||||
|
||||
// Bailout
|
||||
CLICKED_FORWARD_WITH_BAILOUT: 'CLICKED_FORWARD_WITH_BAILOUT',
|
||||
CLICKED_NEED_HELP_WITH_BAILOUT: 'CLICKED_NEED_HELP_WITH_BAILOUT',
|
||||
BAILOUT: 'BAILOUT'
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -14,10 +14,6 @@ const routingTable = () => [
|
|||
{
|
||||
scenario: navigationScenarios.CLICKED_FORWARD,
|
||||
destinationIssPageValue: issPageValues.POLICY_HOLDER_DETAILS
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
|
||||
destinationIssPageValue: issPageValues.BAILOUT_PAGE
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -55,10 +51,6 @@ const routingTable = () => [
|
|||
{
|
||||
scenario: navigationScenarios.CLICKED_FORWARD_WITHOUT_VIN,
|
||||
destinationIssPageValue: issPageValues.VEHICLE_LOOKUP
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
|
||||
destinationIssPageValue: issPageValues.BAILOUT_PAGE
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -141,14 +133,6 @@ const routingTable = () => [
|
|||
{
|
||||
scenario: issPageValues.VEHICLE_LOOKUP,
|
||||
destinationIssPageValue: issPageValues.VEHICLE_LOOKUP
|
||||
},
|
||||
{
|
||||
scenario: issPageValues.BAILOUT_PAGE, // TODO is this a bug
|
||||
destinationIssPageValue: issPageValues.BAILOUT_PAGE
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
|
||||
destinationIssPageValue: issPageValues.BAILOUT_PAGE
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -215,10 +199,6 @@ const routingTable = () => [
|
|||
{
|
||||
scenario: navigationScenarios.CLICKED_FORWARD_WITH_NO_MORE_QUESTIONS,
|
||||
destinationIssPageValue: issPageValues.COVERAGE_STATEMENT
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT,
|
||||
destinationIssPageValue: issPageValues.BAILOUT_PAGE
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -256,10 +236,6 @@ const routingTable = () => [
|
|||
{
|
||||
scenario: navigationScenarios.CLICKED_FORWARD_WITH_NO_MORE_QUESTIONS,
|
||||
destinationIssPageValue: issPageValues.COVERAGE_STATEMENT
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT,
|
||||
destinationIssPageValue: issPageValues.BAILOUT_PAGE
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -293,10 +269,6 @@ const routingTable = () => [
|
|||
{
|
||||
scenario: navigationScenarios.CLICKED_FORWARD_WITH_NO_MORE_QUESTIONS,
|
||||
destinationIssPageValue: issPageValues.COVERAGE_STATEMENT
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT,
|
||||
destinationIssPageValue: issPageValues.BAILOUT_PAGE
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -455,14 +427,6 @@ const routingTable = () => [
|
|||
{
|
||||
scenario: navigationScenarios.CLICKED_FORWARD_POLICY_VERIFIED_WITH_VEHICLES,
|
||||
destinationIssPageValue: issPageValues.POLICY_VEHICLES
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
|
||||
destinationIssPageValue: issPageValues.BAILOUT_PAGE
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.SAVE_SESSION_FAILED,
|
||||
destinationIssPageValue: issPageValues.BAILOUT_PAGE
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -539,10 +503,6 @@ const routingTable = () => [
|
|||
scenario: navigationScenarios.CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND,
|
||||
destinationIssPageValue: issPageValues.VIN_LOOKUP
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
|
||||
destinationIssPageValue: issPageValues.BAILOUT_PAGE
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_FORWARD_WITH_ENDORSEMENTS,
|
||||
destinationIssPageValue: issPageValues.POLICY_ENDORSEMENTS
|
||||
|
|
@ -591,18 +551,6 @@ const routingTable = () => [
|
|||
{
|
||||
scenario: navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE,
|
||||
destinationIssPageValue: issPageValues.SCHEDULE_PAGE
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP,
|
||||
destinationIssPageValue: issPageValues.BAILOUT_PAGE
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_FORWARD_WITH_INVALID_STATE,
|
||||
destinationIssPageValue: issPageValues.BAILOUT_PAGE
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.PRICING_LOOKUP_ERROR,
|
||||
destinationIssPageValue: issPageValues.BAILOUT_PAGE
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -620,10 +568,6 @@ const routingTable = () => [
|
|||
{
|
||||
scenario: navigationScenarios.CLICKED_FORWARD_WITH_TPA_ENABLED,
|
||||
destinationIssPageValue: issPageValues.TPA_SEARCH
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_FORWARD_WITH_TPA_DISABLED,
|
||||
destinationIssPageValue: issPageValues.BAILOUT_PAGE
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -694,8 +638,20 @@ const routingTable = () => [
|
|||
destinationIssPageValue: issPageValues.PAYMENT_PAGE
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.SAVE_SESSION_FAILED,
|
||||
destinationIssPageValue: issPageValues.BAILOUT_PAGE
|
||||
scenario: navigationScenarios.EDIT_SERVICE_LOCATION,
|
||||
destinationIssPageValue: issPageValues.SCHEDULE_PAGE
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.EDIT_SCHEDULE,
|
||||
destinationIssPageValue: issPageValues.SCHEDULE_PAGE
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.EDIT_VEHICLE,
|
||||
destinationIssPageValue: issPageValues.VEHICLE_DAMAGE,
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.EDIT_WIPERS,
|
||||
destinationIssPageValue: issPageValues.SERVICE_PACKAGES
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -768,10 +724,6 @@ const routingTable = () => [
|
|||
{
|
||||
scenario: navigationScenarios.CLICKED_FORWARD,
|
||||
destinationIssPageValue: issPageValues.TPA_CONFIRMATION
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.SAVE_SESSION_FAILED,
|
||||
destinationIssPageValue: issPageValues.BAILOUT_PAGE
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -798,10 +750,6 @@ const routingTable = () => [
|
|||
{
|
||||
scenario: navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP,
|
||||
destinationIssPageValue: issPageValues.TPA_SUBMIT
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_NEED_HELP,
|
||||
destinationIssPageValue: issPageValues.BAILOUT_PAGE
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import issPageValues from '@/router/router-constants/issPage-values';
|
|||
import { createApp } from 'vue';
|
||||
import { createPinia } from 'pinia';
|
||||
import App from '@/App.vue';
|
||||
import bailoutMessage from '@/constants/bailoutMessage';
|
||||
import { useMainStore } from '@/store';
|
||||
|
||||
describe('Router', () => {
|
||||
beforeAll(() => {
|
||||
|
|
@ -37,18 +39,15 @@ describe('Router', () => {
|
|||
expect(router.push.mock.calls[0][0].state).toBe(parameters);
|
||||
});
|
||||
|
||||
it('Should route from CAPABILITY_QUESTIONS to BAILOUT_PAGE on CLICKED_NEED_HELP_WITH_BAILOUT', () => {
|
||||
const scenario = navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT;
|
||||
const currentRoute = { query: { issPage: issPageValues.CAPABILITY_QUESTIONS } };
|
||||
|
||||
it('Should set bailout and navigate to bailout page when calling navigateBailout', () => {
|
||||
// Arrange
|
||||
router.push = jest.fn();
|
||||
|
||||
// Act
|
||||
router.navigate(scenario, currentRoute);
|
||||
router.navigateBailout(bailoutMessage.unknown({}));
|
||||
|
||||
// Assert
|
||||
expect(router.push).toHaveBeenCalled();
|
||||
|
||||
expect(router.push.mock.calls[0][0].query.issPage).toBe(issPageValues.BAILOUT_PAGE);
|
||||
expect(useMainStore().isBailout).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { defineStore } from 'pinia';
|
||||
import applicationConfig from '@/constants/application-config';
|
||||
import bailoutCode from '@/constants/bailoutCode';
|
||||
import bailoutMessage from '@/constants/bailoutMessage';
|
||||
import coverageStatuses from '@/constants/coverage-statuses';
|
||||
import coverageType from '@/constants/coverage-type';
|
||||
import damageLocationsSelected from '@/constants/damage-locations-selected';
|
||||
|
|
@ -22,14 +21,11 @@ import {
|
|||
noCoverageForSelectedVehicle,
|
||||
repairWaivedForSelectedVehicle
|
||||
} from '@/helpers/policy-vehicle-helper';
|
||||
import {
|
||||
buildURLSearchParams,
|
||||
getPartNumbersListForQueryString,
|
||||
getTaxLineItemQueryString
|
||||
} from '@/helpers/querystring-helper';
|
||||
import { buildURLSearchParams, getPartNumbersListForQueryString } from '@/helpers/querystring-helper';
|
||||
import { getTopLevelGlassPartsWithRecal } from '@/helpers/recal-helper';
|
||||
import { getDateForSavedSessionTimeout } from '@/helpers/session-helper';
|
||||
import issPageValues from '@/router/router-constants/issPage-values';
|
||||
import CoverageStatuses from '@/constants/coverage-statuses';
|
||||
|
||||
const storeId = 'main';
|
||||
|
||||
|
|
@ -533,20 +529,15 @@ export const useMainStore = defineStore({
|
|||
payload: {}
|
||||
});
|
||||
},
|
||||
getIsVinbyAddressPermissible() {
|
||||
async getIsVinbyAddressPermissible() {
|
||||
try {
|
||||
const response = globalMethods.callHttpClient({
|
||||
return globalMethods.callHttpClient({
|
||||
method: endpoints.IsVinbyAddressPermissible.method,
|
||||
endpoint: `${endpoints.IsVinbyAddressPermissible.url}?state=${this.order.customer.address.state}`,
|
||||
payload: {}
|
||||
});
|
||||
return response;
|
||||
} catch (responseError) {
|
||||
return {
|
||||
error: {
|
||||
status: responseError.status
|
||||
}
|
||||
};
|
||||
});
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
async getCoveragePolicyInfo() {
|
||||
|
|
@ -555,7 +546,7 @@ export const useMainStore = defineStore({
|
|||
|
||||
if (!issConfig.isCoverageEnabled || applicationUser.coverageLookupAttempts > 10) {
|
||||
this.updateCoverageType(coverageType.NONE);
|
||||
return Promise.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
this.applicationUser.coverageAttempts += 1;
|
||||
|
|
@ -571,7 +562,8 @@ export const useMainStore = defineStore({
|
|||
dateOfLoss: policy.dateOfLoss,
|
||||
zipCode: policy.policyZipCode,
|
||||
referralCorrelationId: order.referralCorrelationId
|
||||
}
|
||||
},
|
||||
bailoutOnError: false
|
||||
});
|
||||
|
||||
const responsePolicy = response?.data?.policies?.[0];
|
||||
|
|
@ -600,10 +592,8 @@ export const useMainStore = defineStore({
|
|||
} else {
|
||||
this.updateCoverageType(coverageType.NONE);
|
||||
}
|
||||
return Promise.resolve();
|
||||
} catch (e) {
|
||||
this.updateCoverageType(coverageType.NONE);
|
||||
return Promise.reject(e);
|
||||
}
|
||||
},
|
||||
clearDuplicateOrders() {
|
||||
|
|
@ -621,82 +611,81 @@ export const useMainStore = defineStore({
|
|||
updateIsItacOptimized(isItacOptimized) {
|
||||
this.order.insuranceCoverage.isItacOptimized = isItacOptimized || false;
|
||||
},
|
||||
registerClaim() {
|
||||
async registerClaim() {
|
||||
const nonNumberCharRegex = /[^0-9]/g;
|
||||
const { order, isITAC } = this;
|
||||
return new Promise((resolve, reject) => {
|
||||
globalMethods.callHttpClient({
|
||||
const { isITAC } = this;
|
||||
|
||||
try {
|
||||
const response = await globalMethods.callHttpClient({
|
||||
method: endpoints.RegisterClaim.method,
|
||||
endpoint: endpoints.RegisterClaim.url,
|
||||
payload:
|
||||
{
|
||||
referralCorrelationId: this.order.referralCorrelationId,
|
||||
accountNumber: this.order.parentAccountNumber?.toString() ?? '',
|
||||
policyData: this.order.policy.policyData,
|
||||
isItac: isITAC,
|
||||
insured: {
|
||||
firstName: this.order.customer.firstName,
|
||||
lastName: this.order.customer.lastName,
|
||||
address: {
|
||||
addressLine1: this.order.customer.address.streetAddress,
|
||||
addressLine2: this.order.customer.address.streetAddress2,
|
||||
city: this.order.customer.address.city,
|
||||
state: this.order.customer.address.state,
|
||||
zipCode: this.order.customer.address.zipCode,
|
||||
country: 'US' // TODO set from store
|
||||
{
|
||||
referralCorrelationId: this.order.referralCorrelationId,
|
||||
accountNumber: this.order.parentAccountNumber?.toString() ?? '',
|
||||
policyData: this.order.policy.policyData,
|
||||
isItac: isITAC,
|
||||
insured: {
|
||||
firstName: this.order.customer.firstName,
|
||||
lastName: this.order.customer.lastName,
|
||||
address: {
|
||||
addressLine1: this.order.customer.address.streetAddress,
|
||||
addressLine2: this.order.customer.address.streetAddress2,
|
||||
city: this.order.customer.address.city,
|
||||
state: this.order.customer.address.state,
|
||||
zipCode: this.order.customer.address.zipCode,
|
||||
country: 'US' // TODO set from store
|
||||
},
|
||||
homePhone: {
|
||||
number: this.contactInfo.homePhone?.replaceAll(nonNumberCharRegex, '') ?? ''
|
||||
}
|
||||
},
|
||||
homePhone: {
|
||||
number: this.contactInfo.homePhone?.replaceAll(nonNumberCharRegex, '') ?? ''
|
||||
driver: {
|
||||
firstName: this.order.customer.firstName,
|
||||
lastName: this.order.customer.lastName
|
||||
},
|
||||
caller: {
|
||||
homePhone: {}
|
||||
},
|
||||
policyInfo: {
|
||||
policyNumber: this.order.policy.policyNumber,
|
||||
safelitePolicy: {
|
||||
policies: []
|
||||
},
|
||||
actualDeductible: this.currentDeductible.toString() ?? ''
|
||||
},
|
||||
lossInfo: {
|
||||
dateOfLoss: this.order.policy.dateOfLoss,
|
||||
location: {
|
||||
city: this.order.policy.damageCity,
|
||||
state: this.order.policy.damageState,
|
||||
country: 'US' // TODO set from store
|
||||
},
|
||||
vehicle: {
|
||||
id: this.order.vehicle.policyVehicleId?.toString() ?? '',
|
||||
year: this.order.vehicle.year?.toString() ?? '',
|
||||
make: this.order.vehicle.make,
|
||||
model: this.order.vehicle.model,
|
||||
vin: this.order.vehicle.vin
|
||||
},
|
||||
cause: this.order.policy.damageCause,
|
||||
damageDescription: this.order.policy.damageCause
|
||||
}
|
||||
},
|
||||
driver: {
|
||||
firstName: this.order.customer.firstName,
|
||||
lastName: this.order.customer.lastName
|
||||
},
|
||||
caller: {
|
||||
homePhone: {}
|
||||
},
|
||||
policyInfo: {
|
||||
policyNumber: this.order.policy.policyNumber,
|
||||
safelitePolicy: {
|
||||
policies: []
|
||||
},
|
||||
actualDeductible: this.currentDeductible.toString() ?? ''
|
||||
},
|
||||
lossInfo: {
|
||||
dateOfLoss: this.order.policy.dateOfLoss,
|
||||
location: {
|
||||
city: this.order.policy.damageCity,
|
||||
state: this.order.policy.damageState,
|
||||
country: 'US' // TODO set from store
|
||||
},
|
||||
vehicle: {
|
||||
id: this.order.vehicle.policyVehicleId?.toString() ?? '',
|
||||
year: this.order.vehicle.year?.toString() ?? '',
|
||||
make: this.order.vehicle.make,
|
||||
model: this.order.vehicle.model,
|
||||
vin: this.order.vehicle.vin
|
||||
},
|
||||
cause: this.order.policy.damageCause,
|
||||
damageDescription: this.order.policy.damageCause
|
||||
}
|
||||
}
|
||||
}).then((response) => {
|
||||
this.order.insuranceCoverage.claimNumber = response.data.claimNumber;
|
||||
if (response.data.isSuccess) {
|
||||
this.updateCoverageStatus(coverageStatuses.VERIFIED);
|
||||
} else {
|
||||
this.updateCoverageStatus(coverageStatuses.NO_COVERAGE);
|
||||
}
|
||||
return resolve(response);
|
||||
}, (error) => {
|
||||
this.updateCoverageStatus(coverageStatuses.NO_COVERAGE);
|
||||
this.order.insuranceCoverage.claimNumber = null;
|
||||
return reject(error);
|
||||
bailoutOnError: false
|
||||
});
|
||||
});
|
||||
this.order.insuranceCoverage.claimNumber = response.data.claimNumber;
|
||||
if (response.data.isSuccess) {
|
||||
this.updateCoverageStatus(coverageStatuses.VERIFIED);
|
||||
} else {
|
||||
this.updateCoverageStatus(coverageStatuses.NO_COVERAGE);
|
||||
}
|
||||
} catch (e) {
|
||||
this.updateCoverageStatus(coverageStatuses.NO_COVERAGE);
|
||||
this.order.insuranceCoverage.claimNumber = null;
|
||||
}
|
||||
},
|
||||
getFinalDeductible() {
|
||||
async getFinalDeductible() {
|
||||
const endorsementAnswersForPayload = [];
|
||||
const endorsementAnswers = this.order.policy.endorsementQuestionAnswers;
|
||||
if (endorsementAnswers) {
|
||||
|
|
@ -712,9 +701,9 @@ export const useMainStore = defineStore({
|
|||
glassInformation?.forEach((glassPiece) => {
|
||||
manualGlassNamesArray.push(glassPiece?.glassLocation?.toUpperCase());
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
globalMethods.callHttpClient({
|
||||
|
||||
try {
|
||||
const r = await globalMethods.callHttpClient({
|
||||
method: endpoints.FinalDeductible.method,
|
||||
endpoint: endpoints.FinalDeductible.url,
|
||||
payload: {
|
||||
|
|
@ -736,16 +725,18 @@ export const useMainStore = defineStore({
|
|||
vehicleVin: this.order.vehicle.vin,
|
||||
policyData: this.order.policy.policyData,
|
||||
isItac: this.isITAC
|
||||
}
|
||||
}).then((r) => {
|
||||
this.order.policy.policyData = r.data.policyData;
|
||||
this.updateDeductible(r.data);
|
||||
return resolve(r);
|
||||
}).catch((error) => reject(error));
|
||||
});
|
||||
},
|
||||
bailoutOnError: false
|
||||
});
|
||||
this.order.policy.policyData = r.data.policyData;
|
||||
this.updateDeductible(r.data);
|
||||
return r;
|
||||
} catch (e) {
|
||||
this.updateCoverageStatus(CoverageStatuses.PENDING);
|
||||
}
|
||||
},
|
||||
|
||||
getDuplicateReferrals() {
|
||||
async getDuplicateReferrals() {
|
||||
const params = new URLSearchParams({
|
||||
parentAccountNumber: this.order.parentAccountNumber,
|
||||
customerPhoneNumber: this.order.contactInfo.servicePhone,
|
||||
|
|
@ -754,18 +745,16 @@ export const useMainStore = defineStore({
|
|||
dateOfLoss: this.order.policy.dateOfLoss
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
globalMethods.callHttpClient({
|
||||
try {
|
||||
const duplicates = await globalMethods.callHttpClient({
|
||||
method: endpoints.DuplicateSearch.method,
|
||||
endpoint: `${endpoints.DuplicateSearch.url}?${params.toString()}`
|
||||
}).then((r) => {
|
||||
this.applicationUser.duplicateOrders = r.data ?? [];
|
||||
return resolve(r.data);
|
||||
}).catch((error) => {
|
||||
this.applicationUser.duplicateOrders = [];
|
||||
return reject(error);
|
||||
endpoint: `${endpoints.DuplicateSearch.url}?${params.toString()}`,
|
||||
bailoutOnError: false
|
||||
});
|
||||
});
|
||||
this.applicationUser.duplicateOrders = duplicates.data ?? [];
|
||||
} catch (e) {
|
||||
this.applicationUser.duplicateOrders = [];
|
||||
}
|
||||
},
|
||||
async lookupVinByPlate(licensePlate, licenseState) {
|
||||
try {
|
||||
|
|
@ -884,6 +873,10 @@ export const useMainStore = defineStore({
|
|||
},
|
||||
getMobileTimeSlots(startDate, endDate, zipCodeOverride = null) {
|
||||
const { order } = this;
|
||||
if (!this.billToAccountNumber) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { vehicle } = this.order;
|
||||
let lineItems = [
|
||||
...(order.lineItems.supportingItems ?? []),
|
||||
|
|
@ -939,6 +932,9 @@ export const useMainStore = defineStore({
|
|||
},
|
||||
getShopTimeSlots(startDate, endDate, shopAppointmentType, providerNumber) {
|
||||
const { order } = this;
|
||||
if (!this.billToAccountNumber || !providerNumber) {
|
||||
return null;
|
||||
}
|
||||
const { vehicle } = this.order;
|
||||
|
||||
let lineItems = [
|
||||
|
|
@ -990,7 +986,8 @@ export const useMainStore = defineStore({
|
|||
return globalMethods.callHttpClient({
|
||||
method: endpoints.GetShopTimeSlots.method,
|
||||
endpoint: endpoints.GetShopTimeSlots.url,
|
||||
payload
|
||||
payload,
|
||||
bailoutOnError: false
|
||||
});
|
||||
},
|
||||
async getWipers() {
|
||||
|
|
@ -1062,7 +1059,7 @@ export const useMainStore = defineStore({
|
|||
safeliteOnly,
|
||||
carId,
|
||||
windshieldPartWithRecal?.partNumber,
|
||||
isBigTruck
|
||||
!!isBigTruck
|
||||
)
|
||||
});
|
||||
},
|
||||
|
|
@ -1135,6 +1132,10 @@ export const useMainStore = defineStore({
|
|||
const providerToUse = provider?.providerNumber ?? defaultProviderNumber;
|
||||
const partNumberListQueryString = getPartNumbersListForQueryString(glassParts, 'PartNumbers');
|
||||
|
||||
if (!this.billToAccountNumber || !providerToUse) {
|
||||
return null
|
||||
}
|
||||
|
||||
const params = new URLSearchParams({
|
||||
serviceType: damageType,
|
||||
facilityType,
|
||||
|
|
@ -1218,9 +1219,6 @@ export const useMainStore = defineStore({
|
|||
IsOEMRequest: this.hasOemEndorsement
|
||||
}
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
throw error;
|
||||
});
|
||||
|
||||
const { lineItems, serverData, isItac, primaryBillToNumber, partsWerePriced, isItacOptimized } = response.data;
|
||||
|
|
@ -1255,62 +1253,74 @@ export const useMainStore = defineStore({
|
|||
},
|
||||
|
||||
async getCombinedQuote(availableLineItems) {
|
||||
if (availableLineItems && availableLineItems.length > 0) {
|
||||
const { vehicle, serviceLocation, policy, eon, referralNumber, referralDate, referralSequenceNumber, lineItems } = this.order;
|
||||
|
||||
const availableLineItemsFiltered = availableLineItems.filter((lineItem) => !!(lineItem.partNumber));
|
||||
|
||||
const lineItemsWithOnlyPartNumbers = availableLineItemsFiltered.map((lineItem) => ({ partNumber: lineItem.partNumber }));
|
||||
|
||||
const payload = {
|
||||
ParentAccountNumber: this.order.parentAccountNumber,
|
||||
BillToAccountNumber: this.billToAccountNumber,
|
||||
ProviderNumber: this.providerNumber,
|
||||
CarId: vehicle.carId,
|
||||
Make: vehicle.make,
|
||||
Model: vehicle.model,
|
||||
Year: vehicle.year,
|
||||
EON: eon,
|
||||
ZipCode: policy.policyZipCode,
|
||||
State: this.order.customer.address.state,
|
||||
ReferralNumber: referralNumber,
|
||||
ReferralDate: referralDate,
|
||||
ReferralSequenceNumber: referralSequenceNumber,
|
||||
ServiceZipCode: serviceLocation.zipCode,
|
||||
IsReplacement: !this.isRepair,
|
||||
ServerData: lineItems.serverData,
|
||||
LineItems: lineItemsWithOnlyPartNumbers,
|
||||
FeatureToggles: getFeatureTogglesPayloadObject(this.experimentSettings)
|
||||
};
|
||||
|
||||
const response = await globalMethods
|
||||
.callHttpClient({
|
||||
method: endpoints.GetCombinedQuote.method,
|
||||
endpoint: endpoints.GetCombinedQuote.url,
|
||||
payload
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
return [];
|
||||
});
|
||||
|
||||
const { lineItems: pricedLinedItems, serverData } = response.data;
|
||||
|
||||
if (serverData) {
|
||||
this.order.lineItems.serverData = serverData;
|
||||
}
|
||||
|
||||
if (pricedLinedItems) {
|
||||
return addPricesToLineItems(availableLineItems, pricedLinedItems);
|
||||
}
|
||||
return availableLineItems;
|
||||
if (!availableLineItems || availableLineItems.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [];
|
||||
const {
|
||||
vehicle,
|
||||
serviceLocation,
|
||||
policy,
|
||||
eon,
|
||||
referralNumber,
|
||||
referralDate,
|
||||
referralSequenceNumber,
|
||||
lineItems
|
||||
} = this.order;
|
||||
|
||||
const availableLineItemsFiltered = availableLineItems.filter((lineItem) => !!(lineItem.partNumber));
|
||||
|
||||
const lineItemsWithOnlyPartNumbers = availableLineItemsFiltered.map((lineItem) => ({ partNumber: lineItem.partNumber }));
|
||||
|
||||
const payload = {
|
||||
ParentAccountNumber: this.order.parentAccountNumber,
|
||||
BillToAccountNumber: this.billToAccountNumber,
|
||||
ProviderNumber: this.providerNumber,
|
||||
CarId: vehicle.carId,
|
||||
Make: vehicle.make,
|
||||
Model: vehicle.model,
|
||||
Year: vehicle.year,
|
||||
EON: eon,
|
||||
ZipCode: policy.policyZipCode,
|
||||
State: this.order.customer.address.state,
|
||||
ReferralNumber: referralNumber,
|
||||
ReferralDate: referralDate,
|
||||
ReferralSequenceNumber: referralSequenceNumber,
|
||||
ServiceZipCode: serviceLocation.zipCode,
|
||||
IsReplacement: !this.isRepair,
|
||||
ServerData: lineItems.serverData,
|
||||
LineItems: lineItemsWithOnlyPartNumbers,
|
||||
FeatureToggles: getFeatureTogglesPayloadObject(this.experimentSettings)
|
||||
};
|
||||
|
||||
const response = await globalMethods
|
||||
.callHttpClient({
|
||||
method: endpoints.GetCombinedQuote.method,
|
||||
endpoint: endpoints.GetCombinedQuote.url,
|
||||
payload
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
return [];
|
||||
});
|
||||
|
||||
const { lineItems: pricedLinedItems, serverData } = response.data;
|
||||
|
||||
if (serverData) {
|
||||
this.order.lineItems.serverData = serverData;
|
||||
}
|
||||
|
||||
if (pricedLinedItems) {
|
||||
return addPricesToLineItems(availableLineItems, pricedLinedItems);
|
||||
}
|
||||
return availableLineItems;
|
||||
},
|
||||
|
||||
getMobileFeePart() {
|
||||
const { vehicle, insuranceCoverage } = this.order;
|
||||
if (!this.billToAccountNumber || !this.providerNumber) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const params = new URLSearchParams({
|
||||
serviceType: this.damage.isRepair ? 'Repair' : 'Install',
|
||||
|
|
@ -1330,7 +1340,7 @@ export const useMainStore = defineStore({
|
|||
});
|
||||
},
|
||||
|
||||
getServiceabilityDetails({ serviceZipCode }) {
|
||||
getServiceabilityDetails(serviceZipCode) {
|
||||
const { vehicle, damage, parentAccountNumber, referralSequenceNumber, lineItems } = this.order;
|
||||
const { carId } = vehicle;
|
||||
const glassArray = convertGlassPieceNamingForApi(damage.glassToReplace);
|
||||
|
|
@ -1360,7 +1370,8 @@ export const useMainStore = defineStore({
|
|||
endpoint: endpoints.LookupVehicleByVin.url,
|
||||
payload: {
|
||||
vin
|
||||
}
|
||||
},
|
||||
bailoutOnError: false
|
||||
});
|
||||
},
|
||||
|
||||
|
|
@ -1392,7 +1403,7 @@ export const useMainStore = defineStore({
|
|||
this.applicationUser.crmCustomerId = response.crmCustomerId.toString();
|
||||
},
|
||||
|
||||
saveSession({ submitAfterSave, createWorkOrderNumberForPIA }) {
|
||||
saveSession({ submitAfterSave, createWorkOrderNumberForPIA, bailoutOnError }) {
|
||||
const { vehicle, damage, policy, customer, contactInfo, payment,
|
||||
lineItems, serviceLocation, schedule, insuranceCoverage } = this.order;
|
||||
|
||||
|
|
@ -1544,7 +1555,7 @@ export const useMainStore = defineStore({
|
|||
method: endpoints.SaveSession.method,
|
||||
endpoint: endpoints.SaveSession.url,
|
||||
payload,
|
||||
bailoutOnError: false
|
||||
bailoutOnError
|
||||
}).then((response) => {
|
||||
if (loadedFromDupeCheck) {
|
||||
this.order.loadedSessionClearedPreviousData = true;
|
||||
|
|
@ -1831,23 +1842,12 @@ export const useMainStore = defineStore({
|
|||
this.order.serviceLocation.zipCodeCtu = serviceLocationInfo.zipCodeCtu ?? this.order.serviceLocation.zipCodeCtu;
|
||||
this.order.serviceLocation.appointmentType = serviceLocationInfo.appointmentType ?? this.order.serviceLocation.appointmentType;
|
||||
this.order.serviceLocation.isVehicleProtected = serviceLocationInfo.isVehicleProtected ?? this.order.serviceLocation.isVehicleProtected;
|
||||
|
||||
if (serviceLocationInfo.provider) {
|
||||
this.order.serviceLocation.provider = {
|
||||
providerNumber: serviceLocationInfo.provider?.providerNumber,
|
||||
address: {
|
||||
streetAddress: serviceLocationInfo.provider?.address?.streetAddress,
|
||||
city: serviceLocationInfo.provider?.address?.city,
|
||||
state: serviceLocationInfo.provider?.address?.state,
|
||||
zipCode: serviceLocationInfo.provider?.address?.zipCode,
|
||||
zipCodeCtu: serviceLocationInfo.provider?.address?.zipCodeCtu
|
||||
},
|
||||
companyName: serviceLocationInfo.provider?.companyName,
|
||||
phoneNumber: serviceLocationInfo.provider?.phoneNumber
|
||||
};
|
||||
}
|
||||
this.updateServiceLocationProvider(serviceLocationInfo.provider)
|
||||
this.order.serviceLocation.tpaSearchRadius = serviceLocationInfo.tpaSearchRadius;
|
||||
},
|
||||
updateAppointmentType(appointmentType) {
|
||||
this.order.serviceLocation.appointmentType = appointmentType;
|
||||
},
|
||||
updateServiceLocationProvider(providerInfo) {
|
||||
this.order.serviceLocation.provider = {
|
||||
providerNumber: providerInfo?.providerNumber,
|
||||
|
|
@ -2461,6 +2461,7 @@ export const useMainStore = defineStore({
|
|||
populateInitialState(forceReset) {
|
||||
if (!sessionStorage.getItem(storeId) || forceReset) {
|
||||
this.$state = getDefaultState();
|
||||
this.resetSubmittedOrder();
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -2697,36 +2698,33 @@ export const useMainStore = defineStore({
|
|||
}
|
||||
},
|
||||
|
||||
async getBillToInfo(componentProviderNumber = null) {
|
||||
const { order, issConfig } = this;
|
||||
try {
|
||||
const params = new URLSearchParams({
|
||||
parentAccountNumber: order.parentAccountNumber.toString(),
|
||||
providerNumber: componentProviderNumber || this.providerNumber,
|
||||
typeOfClaim: 'GLASS ONLY',
|
||||
lineOfBusiness: 'PERSONAL',
|
||||
isItac: this.isITAC
|
||||
});
|
||||
|
||||
const response = await globalMethods.callHttpClient({
|
||||
method: endpoints.GetBillToInfo.method,
|
||||
endpoint: `${endpoints.GetBillToInfo.url}?${params.toString()}`
|
||||
});
|
||||
|
||||
const billToInfo = response.data;
|
||||
if (billToInfo != null) {
|
||||
issConfig.billToAccountNumber = billToInfo.toString();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return Promise.reject(new Error('Invalid billToInfo'));
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
async getBillToInfo(componentProviderNumber = null) {
|
||||
const providerNumber = componentProviderNumber || this.providerNumber;
|
||||
if (!providerNumber) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { order, issConfig } = this;
|
||||
const params = new URLSearchParams({
|
||||
parentAccountNumber: order.parentAccountNumber.toString(),
|
||||
providerNumber,
|
||||
typeOfClaim: 'GLASS ONLY',
|
||||
lineOfBusiness: 'PERSONAL',
|
||||
isItac: this.isITAC
|
||||
});
|
||||
|
||||
const response = await globalMethods.callHttpClient({
|
||||
method: endpoints.GetBillToInfo.method,
|
||||
endpoint: `${endpoints.GetBillToInfo.url}?${params.toString()}`,
|
||||
bailoutOnError: false
|
||||
});
|
||||
|
||||
const billToInfo = response.data;
|
||||
issConfig.billToAccountNumber = billToInfo.toString();
|
||||
},
|
||||
|
||||
async validateClientTag(clientTag) {
|
||||
return await globalMethods.callHttpClient({
|
||||
return globalMethods.callHttpClient({
|
||||
method: endpoints.ValidateClientTag.method,
|
||||
endpoint: `${endpoints.ValidateClientTag.url}/${clientTag}`
|
||||
});
|
||||
|
|
@ -2746,10 +2744,6 @@ export const useMainStore = defineStore({
|
|||
});
|
||||
},
|
||||
|
||||
saveServiceLocation(serviceLocationInfo) {
|
||||
this.updateServiceLocation(serviceLocationInfo);
|
||||
},
|
||||
|
||||
saveRegistrationAddressLookup({ isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo }) {
|
||||
// Reset dependent state when changing
|
||||
if
|
||||
|
|
|
|||
|
|
@ -459,9 +459,9 @@ describe('Store', () => {
|
|||
expect(store.order.insuranceCoverage.claimNumber).not.toBeNull();
|
||||
});
|
||||
|
||||
it('Call to client returns exception, resulting in object with error property being returned', async () => {
|
||||
it('Call to client returns exception, and no error is returned from registerClaim method', async () => {
|
||||
// Arrange
|
||||
expect.assertions(4);
|
||||
expect.assertions(3);
|
||||
const error = 'register claim error';
|
||||
globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.reject(error));
|
||||
store.order.insuranceCoverage.coverageStatus = coverageStatuses.PENDING;
|
||||
|
|
@ -1494,8 +1494,8 @@ describe('Store', () => {
|
|||
// Assert
|
||||
expect(globalMethods.callHttpClient).toHaveBeenCalled();
|
||||
});
|
||||
it('api call throws exception => insuranceCoverage.coverageType is NONE', async () => {
|
||||
expect.assertions(3);
|
||||
it('api call throws exception => no error is thrown from getCoveragePolicyInfo and insuranceCoverage.coverageType is NONE', async () => {
|
||||
expect.assertions(2);
|
||||
const error = 'get coverage policy info error';
|
||||
store.issConfig.isCoverageEnabled = true;
|
||||
store.applicationUser.coverageLookupAttempts = 0;
|
||||
|
|
@ -1567,9 +1567,9 @@ describe('Store', () => {
|
|||
expect(globalMethods.callHttpClient).toHaveBeenCalled();
|
||||
expect(store.applicationUser.duplicateOrders).toEqual(expected);
|
||||
});
|
||||
it('Call to client returns exception => object with error property returned and duplicateReferrals set to []', async () => {
|
||||
it('Call to client returns exception => object with error property returned and duplicateReferrals set to [] and no error is returned from getDuplicateReferrals', async () => {
|
||||
// Arrange
|
||||
expect.assertions(3);
|
||||
expect.assertions(2);
|
||||
const error = 'get duplicate referrals error';
|
||||
globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.reject(error));
|
||||
|
||||
|
|
@ -1807,7 +1807,8 @@ describe('Store', () => {
|
|||
vehicleVin,
|
||||
policyData,
|
||||
isItac: store.isITAC
|
||||
})
|
||||
}),
|
||||
bailoutOnError: false
|
||||
}));
|
||||
});
|
||||
it('only "Yes" endorsement answers are added to payload', () => {
|
||||
|
|
@ -1912,14 +1913,15 @@ describe('Store', () => {
|
|||
vehicleVin,
|
||||
policyData,
|
||||
isItac: store.isITAC
|
||||
})
|
||||
}),
|
||||
bailoutOnError: false
|
||||
}));
|
||||
});
|
||||
});
|
||||
describe('unsuccessful api call', () => {
|
||||
it('api call throws exception', async () => {
|
||||
it('api call throws exception. no error from getFinalDeductible', async () => {
|
||||
// Arrange
|
||||
expect.assertions(2);
|
||||
expect.assertions(1);
|
||||
const error = 'final deductible error';
|
||||
globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.reject(error));
|
||||
|
||||
|
|
@ -2078,8 +2080,8 @@ describe('Store', () => {
|
|||
expect(result.data.shopProviders[1]).toBe(provider2);
|
||||
});
|
||||
});
|
||||
it('api call throws exception => coverageType none', async () => {
|
||||
expect.assertions(3);
|
||||
it('api call throws exception => coverageType none and no error from getCoveragePolicyInfo', async () => {
|
||||
expect.assertions(2);
|
||||
const error = 'get coverage policy info error';
|
||||
|
||||
store.issConfig.isCoverageEnabled = true;
|
||||
|
|
|
|||
|
|
@ -7,11 +7,6 @@ html {
|
|||
&.list-button,
|
||||
&.list-button.list-group,
|
||||
&.list-card &.option-label {
|
||||
border: 1px solid $red;
|
||||
position: relative;
|
||||
z-index: 4;
|
||||
border-radius: 60px;
|
||||
|
||||
.button-content {
|
||||
border: 1px solid $red;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ $svg-loading-modal-image: "data:image/svg+xml;charset=UTF-8,%3csvg fill='none' x
|
|||
$svg-modal-header-button-close: "data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M15.6874 1.82179L9.50889 8L15.6874 14.1782C16.1042 14.595 16.1042 15.2707 15.6874 15.6875C15.4843 15.8907 15.2146 16 14.9328 16C14.6514 16 14.3818 15.8906 14.1787 15.6875L8.00017 9.50934L1.82171 15.6875C1.6182 15.8907 1.34876 16 1.06708 16C0.785733 16 0.516056 15.8906 0.312965 15.6875C-0.10429 15.2706 -0.10429 14.5952 0.312797 14.1784L6.03276 8.45867L6.49146 8L0.312945 1.82177C-0.10429 1.40483 -0.10429 0.729418 0.312797 0.31262C0.728936 -0.104145 1.40489 -0.104051 1.82185 0.31262L7.54152 6.03202L8.00017 6.49065L14.1786 0.312472C14.5955 -0.104107 15.2707 -0.104201 15.6874 0.312452C16.1042 0.729289 16.1042 1.40496 15.6874 1.82179Z' fill='%231574A1'/%3E%3C/svg%3E%0A";
|
||||
$svg-payment-method-review-toggle: "data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 8.9' xml:space='preserve'%3e%3cpath d='M8 8.9c-.2 0-.5-.1-.6-.3L.3 1.5C.1 1.4 0 1.1 0 .9 0 .7.1.4.3.3.4.1.7 0 .9 0c.2 0 .5.1.6.3L8 6.7 14.5.2c.1-.1.4-.2.6-.2.2 0 .5.1.6.3s.3.4.3.6c0 .2-.1.5-.3.6L8.6 8.6c-.1.2-.4.3-.6.3z' fill='%231474a2'/%3e%3c/svg%3e";
|
||||
$svg-search-icon: "data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M15.7817 14.7328L11.8252 10.7762C12.8833 9.45005 13.3936 7.76911 13.2513 6.07849C13.1091 4.38788 12.325 2.81587 11.0601 1.6852C9.79515 0.554524 8.14538 -0.0490261 6.44946 -0.00154744C4.75353 0.0459312 3.14012 0.740836 1.94045 1.94051C0.740775 3.14018 0.0458701 4.75359 -0.00160848 6.44952C-0.0490871 8.14545 0.554463 9.79521 1.68514 11.0601C2.81581 12.325 4.38782 13.1091 6.07843 13.2514C7.76905 13.3937 9.44999 12.8834 10.7762 11.8252L14.7349 15.7839C14.8044 15.8527 14.8869 15.907 14.9774 15.9439C15.068 15.9808 15.165 15.9995 15.2628 15.9989C15.3606 15.9983 15.4573 15.9784 15.5475 15.9405C15.6376 15.9025 15.7194 15.8471 15.7881 15.7776C15.8568 15.708 15.9112 15.6256 15.9481 15.535C15.985 15.4444 16.0036 15.3474 16.0031 15.2496C16.0025 15.1518 15.9826 15.0551 15.9446 14.965C15.9067 14.8748 15.8513 14.7931 15.7817 14.7243V14.7328ZM6.63737 11.7913C5.61803 11.7913 4.62157 11.4891 3.77402 10.9228C2.92646 10.3564 2.26587 9.5515 1.87578 8.60975C1.4857 7.668 1.38363 6.63172 1.5825 5.63196C1.78136 4.6322 2.27222 3.71386 2.99301 2.99307C3.7138 2.27229 4.63214 1.78142 5.6319 1.58256C6.63166 1.38369 7.66793 1.48576 8.60969 1.87585C9.55144 2.26593 10.3564 2.92652 10.9227 3.77408C11.489 4.62163 11.7913 5.61809 11.7913 6.63743C11.7896 8.00382 11.2461 9.31376 10.2799 10.2799C9.3137 11.2461 8.00376 11.7897 6.63737 11.7913Z' fill='%231574A1'/%3E%3C/svg%3E%0A";
|
||||
$svg-error-search-icon: "data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M15.7817 14.7328L11.8252 10.7762C12.8833 9.45005 13.3936 7.76911 13.2513 6.07849C13.1091 4.38788 12.325 2.81587 11.0601 1.6852C9.79515 0.554524 8.14538 -0.0490261 6.44946 -0.00154744C4.75353 0.0459312 3.14012 0.740836 1.94045 1.94051C0.740775 3.14018 0.0458701 4.75359 -0.00160848 6.44952C-0.0490871 8.14545 0.554463 9.79521 1.68514 11.0601C2.81581 12.325 4.38782 13.1091 6.07843 13.2514C7.76905 13.3937 9.44999 12.8834 10.7762 11.8252L14.7349 15.7839C14.8044 15.8527 14.8869 15.907 14.9774 15.9439C15.068 15.9808 15.165 15.9995 15.2628 15.9989C15.3606 15.9983 15.4573 15.9784 15.5475 15.9405C15.6376 15.9025 15.7194 15.8471 15.7881 15.7776C15.8568 15.708 15.9112 15.6256 15.9481 15.535C15.985 15.4444 16.0036 15.3474 16.0031 15.2496C16.0025 15.1518 15.9826 15.0551 15.9446 14.965C15.9067 14.8748 15.8513 14.7931 15.7817 14.7243V14.7328ZM6.63737 11.7913C5.61803 11.7913 4.62157 11.4891 3.77402 10.9228C2.92646 10.3564 2.26587 9.5515 1.87578 8.60975C1.4857 7.668 1.38363 6.63172 1.5825 5.63196C1.78136 4.6322 2.27222 3.71386 2.99301 2.99307C3.7138 2.27229 4.63214 1.78142 5.6319 1.58256C6.63166 1.38369 7.66793 1.48576 8.60969 1.87585C9.55144 2.26593 10.3564 2.92652 10.9227 3.77408C11.489 4.62163 11.7913 5.61809 11.7913 6.63743C11.7896 8.00382 11.2461 9.31376 10.2799 10.2799C9.3137 11.2461 8.00376 11.7897 6.63737 11.7913Z' fill='" + $svg-calendar-error-stroke-color + "'/%3E%3C/svg%3E%0A";
|
||||
$svg-select-icon: "data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 8.89' xml:space='preserve'%3e%3cpath d='M8 8.89c-.24 0-.46-.09-.63-.26L.26 1.53a.901.901 0 0 1 0-1.27C.43.1.66 0 .9 0s.47.1.64.26L8 6.74 14.47.27c.17-.17.4-.27.64-.27s.47.1.63.27c.17.17.26.4.26.64s-.1.47-.27.63l-7.1 7.09a.86.86 0 0 1-.63.26z' fill='%231474a2'/%3e%3c/svg%3e";
|
||||
$svg-shop-list-button-green-availability: "data:image/svg+xml,%3Csvg viewBox='0 0 13 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.5 12C3.19159 12 0.5 9.30841 0.5 6C0.5 2.69159 3.19159 0 6.5 0C9.80841 0 12.5 2.69159 12.5 6C12.5 9.30841 9.80841 12 6.5 12ZM6.5 0.785047C3.62449 0.785047 1.28505 3.12449 1.28505 6C1.28505 8.87551 3.62449 11.215 6.5 11.215C9.37551 11.215 11.715 8.87551 11.715 6C11.715 3.12449 9.37551 0.785047 6.5 0.785047Z' fill='%23006A36'/%3E%3Cpath d='M5.697 7.95252C5.5927 7.95252 5.49289 7.91102 5.41999 7.837L3.90597 6.32299C3.75233 6.16934 3.75233 5.92149 3.90597 5.76785C4.05962 5.6142 4.30747 5.6142 4.46111 5.76785L5.69812 7.00373L8.53999 4.16186C8.69364 4.00822 8.94149 4.00822 9.09513 4.16186C9.24878 4.31551 9.24878 4.56336 9.09513 4.717L5.97626 7.83588C5.90224 7.9099 5.80242 7.9514 5.69925 7.9514L5.697 7.95252Z' fill='%23006A36'/%3E%3C/svg%3E%0A";
|
||||
$svg-shop-list-button-orange-availability: "data:image/svg+xml,%3Csvg viewBox='0 0 13 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.5 0C9.81368 0 12.5 2.68632 12.5 6C12.5 9.31368 9.81368 12 6.5 12C3.18632 12 0.5 9.31368 0.5 6C0.5 2.68632 3.18632 0 6.5 0ZM6.5 0.84C3.6548 0.84 1.34 3.1548 1.34 6C1.34 8.8452 3.6548 11.16 6.5 11.16C9.3452 11.16 11.66 8.8452 11.66 6C11.66 3.1548 9.3452 0.84 6.5 0.84ZM7.90018 4.00596C8.06422 3.84204 8.33002 3.84192 8.49406 4.00596C8.6581 4.17 8.6581 4.43592 8.49406 4.59996L7.0939 6L8.49406 7.40004C8.6581 7.56408 8.6581 7.83 8.49406 7.99404C8.4121 8.076 8.30458 8.11704 8.19706 8.11704C8.08966 8.11704 7.98214 8.076 7.90018 7.99404L6.50002 6.594L5.09986 7.99404C5.01778 8.076 4.91038 8.11704 4.80286 8.11704C4.69546 8.11704 4.58794 8.076 4.50598 7.99404C4.34182 7.83 4.34182 7.56408 4.50598 7.40004L5.90614 6L4.50598 4.59996C4.34182 4.43592 4.34182 4.17 4.50598 4.00596C4.66978 3.84192 4.93582 3.84204 5.09986 4.00596L6.50002 5.406L7.90018 4.00596Z' fill='%23E86421'/%3E%3C/svg%3E%0A";
|
||||
|
|
|
|||
|
|
@ -235,3 +235,6 @@ $border-input-focus: 2.5px solid $heritage-blue-primary;
|
|||
|
||||
$svg-calendar-error-fill-color: $alert-red-bg;
|
||||
$svg-calendar-error-stroke-color: '%23db0020'; // URL encoded #db0020
|
||||
|
||||
// Swing easing to replicate Heritage's slideVisible
|
||||
$transition-swing: all 400ms cubic-bezier(.02, .01, .47, 1);
|
||||
Loading…
Reference in a new issue