Merge branch 'develop' of https://github.com/Safelite/DigitalConsumer.ISS into feature/digital/SSR-791
This commit is contained in:
commit
d4cf2f3d51
38 changed files with 3458 additions and 147 deletions
7
src/constants/package-names.js
Normal file
7
src/constants/package-names.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
const packageNames = Object.freeze({
|
||||
TIER_ONE: 'TierOne',
|
||||
TIER_TWO: 'TierTwo',
|
||||
TIER_THREE: 'TierThree'
|
||||
});
|
||||
|
||||
export default packageNames;
|
||||
|
|
@ -2,7 +2,11 @@
|
|||
FRONT_WIPER: 'FRONT WIPER',
|
||||
REAR_WIPER: 'REAR WIPER',
|
||||
RAIN_DEFENSE: 'RAIN DEFENSE',
|
||||
RECALIBRATION: 'RECALIBRATION'
|
||||
RECALIBRATION: 'RECALIBRATION',
|
||||
REPLACE_FEE: 'REPLACE FEE',
|
||||
RECYCLE_FEE: 'RECYCLE FEE',
|
||||
MOBILE_FEE: 'MOBILE FEE',
|
||||
REPAIR_FEE: 'REPAIR FEE'
|
||||
});
|
||||
|
||||
export default partTypeStrings;
|
||||
|
|
|
|||
|
|
@ -106,10 +106,7 @@ import loader from '@/ux-components/loader/loader.vue';
|
|||
import { useMainStore } from '@/store';
|
||||
import { useField, ErrorMessage } from 'vee-validate';
|
||||
import { deepClone } from '@/helpers/object-helper';
|
||||
import {
|
||||
convertDateToDateString,
|
||||
convertDateStringToDate
|
||||
} from '@/layouts/schedule-page/helpers/schedule-helper';
|
||||
import { convertDateStringToDate, convertDateToDateString } from '@/helpers/date-helper';
|
||||
import { TIMINGFUNC_MAP, BUFFER_OFFSET, MONTHS_OF_YEAR } from './mixins/constants';
|
||||
import { selectableDaysOptions, requiredParameter } from './mixins/helpers';
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@
|
|||
loaderColor="white"
|
||||
:buttonText="footerButtonText"
|
||||
:class="(isButtonDisabled || isFooterButtonDisabled) && 'form-test-invalid'"
|
||||
:isDisabled="(isButtonDisabled || isFooterButtonDisabled)"
|
||||
@clickEvent="validateAndEmit" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
/* eslint-disable no-use-before-define */
|
||||
/* eslint-disable no-param-reassign */
|
||||
/* eslint-disable jsdoc/require-param-type */
|
||||
/* eslint-disable jsdoc/require-param-description */
|
||||
/* eslint-disable jsdoc/require-returns */
|
||||
import dynamicStrings from '@/constants/dynamic-strings';
|
||||
import { useMainStore } from '@/store';
|
||||
|
||||
|
|
@ -191,6 +196,45 @@ export function fetchCmsContentForPage(issPage) {
|
|||
);
|
||||
}
|
||||
|
||||
export function fetchGlobalCmsContent(issComponent) {
|
||||
const store = useMainStore();
|
||||
|
||||
return (
|
||||
store.getPageData(issComponent)
|
||||
.then((baseResponse) => processPageData(baseResponse, null)));
|
||||
}
|
||||
|
||||
export function updateCmsSiteHeader(cmsResponse) {
|
||||
const clientCmsSiteHeader = getCmsSiteHeader(cmsResponse);
|
||||
useMainStore().issConfig.clientHeader = clientCmsSiteHeader;
|
||||
}
|
||||
|
||||
function getCmsSiteHeader(cmsResponse) {
|
||||
if (cmsResponse?.SiteHeaderWidget) {
|
||||
const siteHeader = cmsResponse?.SiteHeaderWidget;
|
||||
if (siteHeader.Answers) {
|
||||
const { parentAccountNumber } = useMainStore().issConfig;
|
||||
let headerAnswersToUse = siteHeader.Answers
|
||||
.find((answer) => answer.AccountNumber === parentAccountNumber.toString());
|
||||
if (headerAnswersToUse == null) {
|
||||
headerAnswersToUse = siteHeader.Answers
|
||||
.find((answer) => answer.AccountNumber === '0');
|
||||
}
|
||||
|
||||
if (headerAnswersToUse) {
|
||||
return {
|
||||
altText: headerAnswersToUse.AltText,
|
||||
clientName: headerAnswersToUse.ClientName,
|
||||
hexCode: headerAnswersToUse.HexCode,
|
||||
imageId: headerAnswersToUse.ImageId,
|
||||
imageUrl: headerAnswersToUse.AnswerImageUrl
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @function mapStringToModal
|
||||
* @param str
|
||||
|
|
@ -493,6 +537,7 @@ export function doesCopyContainTextLink(copy) {
|
|||
export function setupModalLinks(context) {
|
||||
context.$nextTick(() => {
|
||||
const elements = document.getElementsByClassName('modal-text');
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const element of elements) {
|
||||
const target = element.getAttribute('modalTarget');
|
||||
if (target) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,30 @@
|
|||
const getDateDifferenceInDays = (startDate, endDate) => {
|
||||
export function calcDaysBetweenDates(dateString1, dateString2) {
|
||||
const date1 = new Date(dateString1);
|
||||
const date2 = new Date(dateString2);
|
||||
const timeDifference = Math.abs(date2 - date1); // Calculate the time difference in milliseconds
|
||||
return Math.ceil(timeDifference / (1000 * 60 * 60 * 24)); // Convert milliseconds to days
|
||||
}
|
||||
|
||||
export function convertDateToDateString(date) {
|
||||
// returns YYYY-MM-DD format
|
||||
if (date instanceof Date !== true) return null;
|
||||
return (
|
||||
`${date.getFullYear()
|
||||
}-${
|
||||
(`0${date.getMonth() + 1}`).slice(-2)
|
||||
}-${
|
||||
(`0${date.getDate()}`).slice(-2)}`
|
||||
);
|
||||
}
|
||||
|
||||
export function convertDateStringToDate(dateString) {
|
||||
// dateString must be YYYY-MM-DD format
|
||||
if (typeof dateString !== 'string') return null;
|
||||
const dateParts = dateString.split('-');
|
||||
return new Date(dateParts[0], parseInt(dateParts[1], 10) - 1, dateParts[2]);
|
||||
}
|
||||
|
||||
export function getDateDifferenceInDays(startDate, endDate) {
|
||||
const date1 = new Date(endDate);
|
||||
date1.setHours(0, 0, 0, 0);
|
||||
const date2 = new Date(startDate);
|
||||
|
|
@ -7,6 +33,42 @@ const getDateDifferenceInDays = (startDate, endDate) => {
|
|||
const DifferenceInTime = date1.getTime() - date2.getTime();
|
||||
// To calculate the no. of days between two dates
|
||||
return DifferenceInTime / (1000 * 3600 * 24);
|
||||
};
|
||||
}
|
||||
|
||||
export default getDateDifferenceInDays;
|
||||
export function getDisplayTextForDurationLength(durationMinimum, durationMaximum) {
|
||||
const isLongAppointment = durationMaximum >= 120;
|
||||
const isDurationRange = durationMinimum !== durationMaximum;
|
||||
|
||||
const adjustedMinimum = isLongAppointment ? durationMinimum / 60 : durationMinimum;
|
||||
const adjustedMaximum = isLongAppointment ? durationMaximum / 60 : durationMaximum;
|
||||
|
||||
const durationText = isDurationRange
|
||||
? `${adjustedMinimum} - ${adjustedMaximum}`
|
||||
: adjustedMinimum;
|
||||
|
||||
const unitText = isLongAppointment ? 'hours' : 'minutes';
|
||||
|
||||
return `${durationText} ${unitText}`;
|
||||
}
|
||||
|
||||
export function militaryToTwelveHourTime(timeString) {
|
||||
// Expected input: "HH:MM"
|
||||
if (typeof timeString !== 'string') return null;
|
||||
let hours = parseInt(timeString.split(':')[0], 10);
|
||||
const minutes = timeString.split(':')[1];
|
||||
const meridianNotation = hours > 11 ? 'PM' : 'AM';
|
||||
|
||||
if (hours > 12) {
|
||||
hours -= 12;
|
||||
}
|
||||
|
||||
return `${hours}:${minutes} ${meridianNotation}`;
|
||||
}
|
||||
|
||||
export function sumDateString(dateString, daysToAdd) {
|
||||
// dateString must be YYYY-MM-DD format
|
||||
if (typeof dateString !== 'string') return null;
|
||||
const date = convertDateStringToDate(dateString);
|
||||
date.setDate(date.getDate() + daysToAdd);
|
||||
return convertDateToDateString(date);
|
||||
}
|
||||
|
|
|
|||
238
src/helpers/service-package-helper.js
Normal file
238
src/helpers/service-package-helper.js
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
import partTypeStrings from '@/constants/part-type-strings';
|
||||
import damageLocationsSelected from '@/constants/damage-locations-selected';
|
||||
import packageNames from '@/constants/package-names';
|
||||
|
||||
export function findLineItemsWithPartType(typeToFind, itemsToSearch) {
|
||||
const partTypeMatches = itemsToSearch?.filter((lineItem) => lineItem.partType.toUpperCase() === typeToFind.toUpperCase());
|
||||
return partTypeMatches;
|
||||
}
|
||||
|
||||
export function containsLineItemWithPartType(typeToFind, itemsToSearch) {
|
||||
const partTypeMatches = findLineItemsWithPartType(typeToFind, itemsToSearch);
|
||||
return !!partTypeMatches?.length;
|
||||
}
|
||||
|
||||
export function getVapsLineItems(availableLineItems, vapTypes) {
|
||||
const lineItems = [];
|
||||
|
||||
for (let i = 0; i < vapTypes.length; i++) {
|
||||
lineItems.push(...findLineItemsWithPartType(vapTypes[i], availableLineItems));
|
||||
}
|
||||
|
||||
return lineItems;
|
||||
}
|
||||
|
||||
export function containsGlassPieceWithLocation(locationToFind, glassPiecesToSearch) {
|
||||
const glassLocationMatches = glassPiecesToSearch?.filter((glassPiece) =>
|
||||
glassPiece.glassLocation.toUpperCase() === locationToFind.toUpperCase());
|
||||
|
||||
return !!glassLocationMatches?.length;
|
||||
}
|
||||
|
||||
export function shouldFrontWipersBeAvailable(
|
||||
glassToReplace,
|
||||
availableLineItems,
|
||||
isRepair,
|
||||
targetTier
|
||||
) {
|
||||
const frontWiperIsAvailable = containsLineItemWithPartType(
|
||||
partTypeStrings.FRONT_WIPER,
|
||||
availableLineItems
|
||||
);
|
||||
|
||||
const isFrontWindshieldTask =
|
||||
isRepair || containsGlassPieceWithLocation(damageLocationsSelected.WINDSHIELD, glassToReplace);
|
||||
switch (targetTier) {
|
||||
case packageNames.TIER_TWO:
|
||||
return frontWiperIsAvailable && isFrontWindshieldTask;
|
||||
case packageNames.TIER_THREE:
|
||||
return frontWiperIsAvailable;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function shouldRearWipersBeAvailable(
|
||||
glassToReplace,
|
||||
availableLineItems,
|
||||
isRepair,
|
||||
targetTier
|
||||
) {
|
||||
const rearWiperIsAvailable = containsLineItemWithPartType(
|
||||
partTypeStrings.REAR_WIPER,
|
||||
availableLineItems
|
||||
);
|
||||
const frontWiperIsAvailable = containsLineItemWithPartType(
|
||||
partTypeStrings.FRONT_WIPER,
|
||||
availableLineItems
|
||||
);
|
||||
const isRearWindshieldTask = containsGlassPieceWithLocation(
|
||||
damageLocationsSelected.REAR,
|
||||
glassToReplace
|
||||
);
|
||||
|
||||
switch (targetTier) {
|
||||
case packageNames.TIER_TWO:
|
||||
return rearWiperIsAvailable && isRearWindshieldTask;
|
||||
case packageNames.TIER_THREE:
|
||||
return rearWiperIsAvailable && (isRearWindshieldTask || !frontWiperIsAvailable);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function shouldRainDefenseBeAvailable(
|
||||
glassToReplace,
|
||||
availableLineItems,
|
||||
isRepair,
|
||||
targetTier
|
||||
) {
|
||||
const isTierThree = targetTier === packageNames.TIER_THREE;
|
||||
const frontWipersInTierTwo = shouldFrontWipersBeAvailable(
|
||||
glassToReplace,
|
||||
availableLineItems,
|
||||
isRepair,
|
||||
packageNames.TIER_TWO
|
||||
);
|
||||
const frontWipersInTierThree = shouldFrontWipersBeAvailable(
|
||||
glassToReplace,
|
||||
availableLineItems,
|
||||
isRepair,
|
||||
packageNames.TIER_THREE
|
||||
);
|
||||
const rearWipersInTierTwo = shouldRearWipersBeAvailable(
|
||||
glassToReplace,
|
||||
availableLineItems,
|
||||
isRepair,
|
||||
packageNames.TIER_TWO
|
||||
);
|
||||
|
||||
return isTierThree && !(rearWipersInTierTwo && !frontWipersInTierTwo && frontWipersInTierThree);
|
||||
}
|
||||
|
||||
export function getPackageContents(glassToReplace, availableLineItems, isRepair, targetTier) {
|
||||
const vaps = [];
|
||||
|
||||
if (shouldFrontWipersBeAvailable(glassToReplace, availableLineItems, isRepair, targetTier)) {
|
||||
vaps.push(partTypeStrings.FRONT_WIPER);
|
||||
}
|
||||
|
||||
if (shouldRearWipersBeAvailable(glassToReplace, availableLineItems, isRepair, targetTier)) {
|
||||
vaps.push(partTypeStrings.REAR_WIPER);
|
||||
}
|
||||
|
||||
if (shouldRainDefenseBeAvailable(glassToReplace, availableLineItems, isRepair, targetTier)) {
|
||||
vaps.push(partTypeStrings.RAIN_DEFENSE);
|
||||
}
|
||||
|
||||
return vaps;
|
||||
}
|
||||
|
||||
export function getAvailablePackages(glassToReplace, availableLineItems, isRepair) {
|
||||
const tierOneVaps = getPackageContents(
|
||||
glassToReplace,
|
||||
availableLineItems,
|
||||
isRepair,
|
||||
packageNames.TIER_ONE
|
||||
);
|
||||
|
||||
const tierTwoVaps = getPackageContents(
|
||||
glassToReplace,
|
||||
availableLineItems,
|
||||
isRepair,
|
||||
packageNames.TIER_TWO
|
||||
);
|
||||
|
||||
const tierThreeVaps = getPackageContents(
|
||||
glassToReplace,
|
||||
availableLineItems,
|
||||
isRepair,
|
||||
packageNames.TIER_THREE
|
||||
);
|
||||
|
||||
if (tierTwoVaps.length === 0) {
|
||||
return [
|
||||
{
|
||||
packageName: packageNames.TIER_ONE,
|
||||
vapTypes: tierOneVaps
|
||||
},
|
||||
{
|
||||
packageName: packageNames.TIER_THREE,
|
||||
vapTypes: tierThreeVaps
|
||||
}
|
||||
];
|
||||
}
|
||||
return [
|
||||
{
|
||||
packageName: packageNames.TIER_ONE,
|
||||
vapTypes: tierOneVaps
|
||||
},
|
||||
{
|
||||
packageName: packageNames.TIER_TWO,
|
||||
vapTypes: tierTwoVaps
|
||||
},
|
||||
{
|
||||
packageName: packageNames.TIER_THREE,
|
||||
vapTypes: tierThreeVaps
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
export function getLowestTierForType(glassToReplace, availableLineItems, isRepair, partType) {
|
||||
const packages = getAvailablePackages(glassToReplace, availableLineItems, isRepair);
|
||||
for (let i = 0; i < packages.length; i++) {
|
||||
for (let j = 0; j < packages[i].vapTypes.length; j++) {
|
||||
if (packages[i].vapTypes[j].toUpperCase() === partType.toUpperCase()) {
|
||||
return packages[i].packageName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return packageNames.TIER_ONE;
|
||||
}
|
||||
|
||||
function maxTier(tierA, tierB) {
|
||||
if (tierA === packageNames.TIER_THREE || tierB === packageNames.TIER_THREE) {
|
||||
return packageNames.TIER_THREE;
|
||||
}
|
||||
|
||||
if (tierA === packageNames.TIER_TWO || tierB === packageNames.TIER_TWO) {
|
||||
return packageNames.TIER_TWO;
|
||||
}
|
||||
|
||||
return packageNames.TIER_ONE;
|
||||
}
|
||||
|
||||
export function getHighestRequiredTier(glassToReplace, availableLineItems, isRepair, vaps) {
|
||||
let currentHighestTier = packageNames.TIER_ONE;
|
||||
for (let i = 0; i < vaps.length; i++) {
|
||||
const lowestTierForItem = getLowestTierForType(
|
||||
glassToReplace,
|
||||
availableLineItems,
|
||||
isRepair,
|
||||
vaps[i].partType
|
||||
);
|
||||
|
||||
currentHighestTier = maxTier(currentHighestTier, lowestTierForItem);
|
||||
}
|
||||
|
||||
return currentHighestTier;
|
||||
}
|
||||
|
||||
export function getHighestFullySatisfiedTier(glassToReplace, availableLineItems, isRepair, vaps) {
|
||||
const packages = getAvailablePackages(glassToReplace, availableLineItems, isRepair);
|
||||
let highestSatisfiedPackage = packageNames.TIER_ONE;
|
||||
|
||||
for (let i = 0; i < packages.length; i++) {
|
||||
let isPackageSatisfied = true;
|
||||
for (let j = 0; j < packages[i].vapTypes.length; j++) {
|
||||
isPackageSatisfied =
|
||||
isPackageSatisfied && containsLineItemWithPartType(packages[i].vapTypes[j], vaps);
|
||||
}
|
||||
if (isPackageSatisfied) {
|
||||
highestSatisfiedPackage = packages[i].packageName;
|
||||
}
|
||||
}
|
||||
|
||||
return highestSatisfiedPackage;
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@
|
|||
<script>
|
||||
import menuModal from '@/iss-components/site-header/menu-modal/menu-modal.vue';
|
||||
import alert from '@/ux-components/alert/alert.vue';
|
||||
import { useMainStore } from '@/store';
|
||||
import eventBus from '@/helpers/event-bus/event-bus';
|
||||
import { globalEvents } from '@/constants/events';
|
||||
|
||||
|
|
@ -45,19 +46,19 @@ export default ({
|
|||
messageHeadline: '',
|
||||
type: ''
|
||||
},
|
||||
headerAnswers: null
|
||||
cmsClientHeader: null
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
imageSrc() {
|
||||
return this.headerAnswers ? this.headerAnswers.AnswerImageUrl : '';
|
||||
return this.cmsClientHeader ? this.cmsClientHeader.imageUrl : '';
|
||||
},
|
||||
altText() {
|
||||
return this.headerAnswers ? this.headerAnswers.AltText : '';
|
||||
return this.cmsClientHeader ? this.cmsClientHeader.altText : '';
|
||||
},
|
||||
logoBackgroundStyle() {
|
||||
return {
|
||||
'background-color': this.headerAnswers ? this.headerAnswers.HexCode : '#FFFFFF'
|
||||
'background-color': this.cmsClientHeader ? this.cmsClientHeader.hexCode : '#FFFFFF'
|
||||
};
|
||||
}
|
||||
},
|
||||
|
|
@ -85,10 +86,31 @@ export default ({
|
|||
},
|
||||
methods: {
|
||||
setupHeader() {
|
||||
const answers = this.getCmsContent(this.cmsWidgetName, 'Answers');
|
||||
if (Array.isArray(answers)) {
|
||||
this.headerAnswers = answers.filter((x) => x.AccountNumber == this.mainStore.issConfig.parentAccountNumber)[0];
|
||||
this.headerAnswers = !this.headerAnswers ? answers.filter((x) => x.AccountNumber === '0')[0] : this.headerAnswers;
|
||||
const overrideCmsHeaderAnswers = this.getCmsContent(this.cmsWidgetName, 'Answers');
|
||||
if (overrideCmsHeaderAnswers && Array.isArray(overrideCmsHeaderAnswers)) {
|
||||
const { parentAccountNumber } = useMainStore().issConfig;
|
||||
let headerOverrideToUse = overrideCmsHeaderAnswers
|
||||
.find((answer) => answer.AccountNumber === parentAccountNumber.toString());
|
||||
if (headerOverrideToUse == null) {
|
||||
headerOverrideToUse = overrideCmsHeaderAnswers
|
||||
.find((answer) => answer.AccountNumber === '0');
|
||||
}
|
||||
|
||||
if (headerOverrideToUse) {
|
||||
this.cmsClientHeader = {
|
||||
altText: headerOverrideToUse.AltText,
|
||||
clientName: headerOverrideToUse.ClientName,
|
||||
hexCode: headerOverrideToUse.HexCode,
|
||||
imageId: headerOverrideToUse.ImageId,
|
||||
imageUrl: headerOverrideToUse.AnswerImageUrl
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
const { clientHeader } = useMainStore().issConfig;
|
||||
if (clientHeader) {
|
||||
this.cmsClientHeader = clientHeader;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,7 +104,9 @@ export default {
|
|||
const pageData = useMainStore().pageData(to.query.issPage);
|
||||
next((vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
vm.setNotSeeingPreferShop(pageData[routerParams.NOT_SEEING_PREFERRED_SHOP]);
|
||||
if (pageData && pageData[routerParams.NOT_SEEING_PREFERRED_SHOP]) {
|
||||
vm.setNotSeeingPreferShop(pageData[routerParams.NOT_SEEING_PREFERRED_SHOP]);
|
||||
}
|
||||
});
|
||||
},
|
||||
data() {
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ import globalRules from '@/constants/global-rules.js';
|
|||
import baseFormMixin from '@/mixins/base-form-mixin.js';
|
||||
import navigationScenarios from '@/router/router-constants/navigation-scenarios.js';
|
||||
import routerParams from '@/router/router-constants/router-params';
|
||||
import issPageValues from '@/router/router-constants/issPage-values';
|
||||
|
||||
export default {
|
||||
name: 'coverage-statement',
|
||||
|
|
@ -170,9 +171,23 @@ export default {
|
|||
];
|
||||
|
||||
let pricingResults = [];
|
||||
if (useMainStore().order.policy.policyLookupSuccessful) {
|
||||
pricingResults = await useMainStore().getPriceOrderItems(availableLineItems);
|
||||
if (useMainStore().order.policy.policyLookupSuccessful && useMainStore().order.vehicle.policyVehicleId >= 0) {
|
||||
await useMainStore().getFinalDeductible();
|
||||
pricingResults = await useMainStore().getPriceOrderItems(availableLineItems)
|
||||
.catch((err) => {
|
||||
const errorPageData = {
|
||||
errorData: err.data,
|
||||
functionLocation: 'beforeRouteEnter',
|
||||
functionName: 'getPriceOrderItems',
|
||||
functionParameters: {
|
||||
availableLineItems
|
||||
},
|
||||
routeFrom: from,
|
||||
routeTo: to
|
||||
};
|
||||
useMainStore().updatePageData({ page: issPageValues.BAILOUT_PAGE, data: errorPageData });
|
||||
next(`/?issPage=${issPageValues.BAILOUT_PAGE}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Call the "next" function to complete the transition to this page.
|
||||
|
|
@ -342,6 +357,7 @@ export default {
|
|||
async initializeComponent() {
|
||||
useMainStore().updatePolicyITACFlag(this.verifiedITAC);
|
||||
if (this.policyLookupSuccessful
|
||||
&& useMainStore().order.vehicle.policyVehicleId >= 0
|
||||
&& useMainStore().isClaimRegistrationRequired
|
||||
&& (this.coveredAndServicePriceAboveOrEqualDeductible || this.verifiedITAC)) {
|
||||
await useMainStore().registerClaim()?.catch(() => {});
|
||||
|
|
|
|||
|
|
@ -64,12 +64,10 @@ export default {
|
|||
|
||||
if (resp?.data.active && resp?.data.accountName.length > 0) {
|
||||
if (resp.data.authentication === 'RSAToken') {
|
||||
const token = queryStringParams.token;
|
||||
const signature = queryStringParams.signature;
|
||||
|
||||
const { token, signature } = queryStringParams;
|
||||
const vsigResp = await validateISSClientSignature(clientTag, token, signature);
|
||||
this.mainStore.issConfig.isAuthenticated = vsigResp?.data?.valid ?? false;
|
||||
|
||||
this.mainStore.issConfig.isAuthenticated = vsigResp?.data?.valid ?? false;
|
||||
if (this.mainStore.issConfig.isAuthenticated) {
|
||||
authorized = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ export default {
|
|||
if (vehicleLookupResponse.status === 404) {
|
||||
this.mainStore.resetVehicleState();
|
||||
useMainStore().updateVehicle({
|
||||
policyVehicleId: vehicle.fleetUnitNumber,
|
||||
policyVehicleId: vehicle.id,
|
||||
carId: '0',
|
||||
category: '',
|
||||
year: vehicle.vehicleYear || '',
|
||||
|
|
@ -202,7 +202,7 @@ export default {
|
|||
}
|
||||
|
||||
this.vehicleFromLookup = Object.assign(vehicleLookupResponse.data, {
|
||||
policyVehicleId: vehicle.fleetUnitNumber,
|
||||
policyVehicleId: vehicle.id,
|
||||
vin: this.selectedVehicleVin,
|
||||
noCoverage: this.noCoverageForSelectedVehicle,
|
||||
deductible: this.deductibleForSelectedVehicle,
|
||||
|
|
|
|||
44
src/layouts/review-page/review-block/review-block.spec.js
Normal file
44
src/layouts/review-page/review-block/review-block.spec.js
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
// Components
|
||||
import reviewBlock from '@/layouts/review-page/review-block/review-block.vue';
|
||||
|
||||
// Supporting Files
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
||||
|
||||
// Mock fetchCmsContentForPage
|
||||
jest.mock('@/helpers/cms-content-helper', () => ({
|
||||
fetchCmsContentForPage: jest.fn()
|
||||
}));
|
||||
|
||||
function getShallowMountedComponent(initialData = {}, methodToRun = () => {}) {
|
||||
const mountOptions = getMountOptions({
|
||||
router: {
|
||||
navigate: jest.fn()
|
||||
}
|
||||
});
|
||||
|
||||
methodToRun();
|
||||
|
||||
mountOptions.data = () => (
|
||||
initialData
|
||||
);
|
||||
|
||||
const wrapper = shallowMount(reviewBlock, mountOptions);
|
||||
return { wrapper };
|
||||
}
|
||||
|
||||
describe('Review Content Block', () => {
|
||||
test('Displays one new line of content for each element in the content prop.', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getShallowMountedComponent({
|
||||
headerCmsWidgetName: 'TestWidget',
|
||||
content: ['a', 'b', 'c', 'd']
|
||||
});
|
||||
|
||||
// Act
|
||||
const contentLines = wrapper.findAll("[data-test='contentLine']");
|
||||
|
||||
// Assert
|
||||
expect(contentLines.length).toBe(4);
|
||||
});
|
||||
});
|
||||
73
src/layouts/review-page/review-block/review-block.vue
Normal file
73
src/layouts/review-page/review-block/review-block.vue
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<template>
|
||||
<div class="py-3">
|
||||
<div class="d-flex">
|
||||
<textBlock
|
||||
:cmsWidgetName="headerCmsWidgetName"
|
||||
:customText="customHeaderText"
|
||||
typeStyle="body small bold dark"
|
||||
:marginTopSizeOverride="0" />
|
||||
<textLink
|
||||
linkType="textSmall"
|
||||
text="Edit"
|
||||
class="ml-auto"
|
||||
useLoadingModal
|
||||
href="javascript:void(0)"
|
||||
@click-event="linkClicked">
|
||||
<template
|
||||
v-if="editScreenReaderTextCmsWidgetName"
|
||||
#after-text>
|
||||
<span class="sr-only"> {{ screenReaderOnlyText }} </span>
|
||||
</template>
|
||||
</textLink>
|
||||
</div>
|
||||
<div
|
||||
v-for="item in content"
|
||||
:key="item"
|
||||
class="small review-block-content"
|
||||
data-test="contentLine"
|
||||
v-html="item"></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,
|
||||
textLink
|
||||
},
|
||||
props: {
|
||||
headerCmsWidgetName: String,
|
||||
customHeaderText: String,
|
||||
// Specifically an array of strings.
|
||||
content: Array,
|
||||
editScreenReaderTextCmsWidgetName: String
|
||||
},
|
||||
emits: ['edit-clicked'],
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
computed: {
|
||||
screenReaderOnlyText() {
|
||||
return this.getCmsContent(this.editScreenReaderTextCmsWidgetName, 'Text');
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
linkClicked() {
|
||||
this.$emit('edit-clicked');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.review-block-content > ul) {
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
.review-block-content {
|
||||
width: 90%;
|
||||
}
|
||||
</style>
|
||||
386
src/layouts/review-page/review-page.spec.js
Normal file
386
src/layouts/review-page/review-page.spec.js
Normal file
|
|
@ -0,0 +1,386 @@
|
|||
// Components
|
||||
import review from '@/layouts/review-page/review-page.vue';
|
||||
|
||||
// Supporting Files
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
||||
import { useMainStore } from '@/store/index.js';
|
||||
|
||||
// Mock fetchCmsContentForPage
|
||||
jest.mock('@/helpers/cms-content-helper', () => ({
|
||||
fetchCmsContentForPage: jest.fn()
|
||||
}));
|
||||
|
||||
const mockMixin = {
|
||||
methods: {
|
||||
getCmsContent: jest.fn().mockImplementation(() => ''),
|
||||
setCmsContent: jest.fn()
|
||||
}
|
||||
};
|
||||
|
||||
const footerStub = {
|
||||
render: () => {},
|
||||
methods: {
|
||||
updateButtonText: jest.fn()
|
||||
}
|
||||
};
|
||||
|
||||
const loadingModalStub = {
|
||||
render: () => {},
|
||||
methods: {
|
||||
showModal: jest.fn(),
|
||||
hideModal: jest.fn()
|
||||
}
|
||||
};
|
||||
|
||||
function getShallowMountedComponent(initialData = {}, methodToRun = () => {}) {
|
||||
const mountOptions = getMountOptions({
|
||||
router: {
|
||||
navigate: jest.fn()
|
||||
}
|
||||
});
|
||||
|
||||
// mountOptions.global.mocks["$store"] = store;
|
||||
|
||||
mountOptions.global.stubs = {
|
||||
siteFooter: footerStub,
|
||||
loadingModal: loadingModalStub
|
||||
};
|
||||
|
||||
methodToRun();
|
||||
|
||||
mountOptions.mixins = [mockMixin];
|
||||
mountOptions.data = () => (
|
||||
initialData
|
||||
);
|
||||
|
||||
const wrapper = shallowMount(review, mountOptions);
|
||||
return { wrapper };
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
const testingPinia = createTestingPinia({
|
||||
initialState: {
|
||||
main: {
|
||||
order: {
|
||||
vehicle: {
|
||||
year: '2020',
|
||||
make: 'Acura',
|
||||
model: 'MDX',
|
||||
style: '4 door sedan'
|
||||
},
|
||||
damage: {
|
||||
isRepair: false,
|
||||
numberOfChips: 2,
|
||||
glassToReplace: ['dummy location value']
|
||||
},
|
||||
lineItems: {
|
||||
glassParts: ['dummy part value'],
|
||||
supportingItems: ['dummy supporting item'],
|
||||
vaps: ['dummy vap']
|
||||
},
|
||||
serviceLocation: {
|
||||
address: 'address 1',
|
||||
address2: 'address 2',
|
||||
city: 'city',
|
||||
state: 'state',
|
||||
zipCode: 'zip code',
|
||||
appointmentType: 'Mobile',
|
||||
provider: {
|
||||
providerNumber: 1,
|
||||
address: {
|
||||
streetAddress: 'provider address 1',
|
||||
city: 'provider city',
|
||||
state: 'provider state',
|
||||
zipCode: 'provider zip code'
|
||||
}
|
||||
}
|
||||
},
|
||||
schedule: {
|
||||
date: 'date',
|
||||
startTime: 'start',
|
||||
endTime: 'end',
|
||||
jobMinMinutes: '30',
|
||||
jobMaxMinutes: '45'
|
||||
},
|
||||
customer: {
|
||||
firstName: 'first name',
|
||||
lastName: 'last name',
|
||||
emailAddress: 'builddigitaltest@safelite.com',
|
||||
phoneNumber: '555-555-5555',
|
||||
isSmsOptIn: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
useMainStore(testingPinia);
|
||||
});
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('Review Page', () => {
|
||||
describe('arePagePrerequisitesValid', () => {
|
||||
test('Returns true for baseline valid state', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getShallowMountedComponent();
|
||||
|
||||
// Act
|
||||
const isValid = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
// Assert
|
||||
expect(isValid).toBe(true);
|
||||
});
|
||||
test('Returns false for empty state', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getShallowMountedComponent();
|
||||
wrapper.vm.mainStore.order = {
|
||||
vehicle: {
|
||||
year: null,
|
||||
make: null,
|
||||
model: null,
|
||||
style: null,
|
||||
carId: null,
|
||||
category: null,
|
||||
vin: null,
|
||||
imageUrl: null,
|
||||
imageVifNumber: null,
|
||||
imageColor: null,
|
||||
registration: {
|
||||
licensePlate: null
|
||||
}
|
||||
},
|
||||
serviceLocation: {
|
||||
address: null,
|
||||
address2: null,
|
||||
city: null,
|
||||
state: null,
|
||||
zipCode: null,
|
||||
zipCodeCtu: null,
|
||||
appointmentType: null,
|
||||
isVehicleProtected: null,
|
||||
provider: {
|
||||
providerNumber: null,
|
||||
address: {
|
||||
streetAddress: null,
|
||||
city: null,
|
||||
state: null,
|
||||
zipCode: null,
|
||||
zipCodeCtu: null
|
||||
}
|
||||
},
|
||||
techNotes: null
|
||||
},
|
||||
customer: {
|
||||
firstName: null,
|
||||
lastName: null,
|
||||
emailAddress: null,
|
||||
phoneNumber: null,
|
||||
isSmsOptIn: null
|
||||
},
|
||||
damage: {
|
||||
isRepair: null,
|
||||
numberOfChips: null,
|
||||
glassToReplace: null,
|
||||
partQuestionAnswers: null,
|
||||
moldingQuestionAnswers: null,
|
||||
capabilityQuestionAnswers: null
|
||||
},
|
||||
lineItems: {
|
||||
glassParts: null,
|
||||
supportingItems: null,
|
||||
vaps: null,
|
||||
serverData: null
|
||||
},
|
||||
payment: {
|
||||
isInsurance: null,
|
||||
insuranceCoverage: {
|
||||
isVerified: null,
|
||||
coverageStatus: null
|
||||
},
|
||||
parentAccountNumber: 0
|
||||
},
|
||||
schedule: {
|
||||
date: null,
|
||||
startTime: null,
|
||||
endTime: null,
|
||||
routeCode: null,
|
||||
jobMaxMinutes: null,
|
||||
jobMinMinutes: null
|
||||
},
|
||||
referralNumber: null,
|
||||
referralSequenceNumber: null,
|
||||
referralDate: null,
|
||||
referralCorrelationId: null,
|
||||
eon: null
|
||||
};
|
||||
|
||||
// Act
|
||||
const isValid = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
// Assert
|
||||
expect(isValid).toBe(false);
|
||||
});
|
||||
describe('Damage requirements', () => {
|
||||
test('Accepts null glassToReplace when is repair', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getShallowMountedComponent();
|
||||
wrapper.vm.mainStore.order.damage.isRepair = true;
|
||||
wrapper.vm.mainStore.order.damage.glassToReplace = null;
|
||||
wrapper.vm.mainStore.order.damage.numberOfChips = 1;
|
||||
|
||||
// Act
|
||||
const isValid = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
// Assert
|
||||
expect(isValid).toBe(true);
|
||||
});
|
||||
test('Rejects 0 chips when repair', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getShallowMountedComponent();
|
||||
wrapper.vm.mainStore.order.damage.isRepair = true;
|
||||
wrapper.vm.mainStore.order.damage.numberOfChips = 0;
|
||||
|
||||
// Act
|
||||
const isValid = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
// Assert
|
||||
expect(isValid).toBe(false);
|
||||
});
|
||||
test('Rejects null chips when repair', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getShallowMountedComponent();
|
||||
wrapper.vm.mainStore.order.damage.isRepair = true;
|
||||
wrapper.vm.mainStore.order.damage.numberOfChips = null;
|
||||
|
||||
// Act
|
||||
const isValid = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
// Assert
|
||||
expect(isValid).toBe(false);
|
||||
});
|
||||
test('Accepts null chips when not repair', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getShallowMountedComponent();
|
||||
wrapper.vm.mainStore.order.damage.isRepair = false;
|
||||
wrapper.vm.mainStore.order.damage.numberOfChips = null;
|
||||
|
||||
// Act
|
||||
const isValid = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
// Assert
|
||||
expect(isValid).toBe(true);
|
||||
});
|
||||
test('Rejects empty glassToReplace when not repair', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getShallowMountedComponent();
|
||||
wrapper.vm.mainStore.order.damage.isRepair = false;
|
||||
wrapper.vm.mainStore.order.damage.glassToReplace = null;
|
||||
|
||||
// Act
|
||||
const isValid = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
// Assert
|
||||
expect(isValid).toBe(false);
|
||||
});
|
||||
test('Rejects null glassToReplace when not repair', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getShallowMountedComponent();
|
||||
wrapper.vm.mainStore.order.damage.isRepair = false;
|
||||
wrapper.vm.mainStore.order.damage.glassToReplace = [];
|
||||
|
||||
// Act
|
||||
const isValid = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
// Assert
|
||||
expect(isValid).toBe(false);
|
||||
});
|
||||
});
|
||||
describe('Package requirements', () => {
|
||||
test('Accepts null glassParts when is repair', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getShallowMountedComponent();
|
||||
wrapper.vm.mainStore.order.damage.isRepair = true;
|
||||
wrapper.vm.mainStore.order.lineItems.glassParts = null;
|
||||
|
||||
// Act
|
||||
const isValid = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
// Assert
|
||||
expect(isValid).toBe(true);
|
||||
});
|
||||
test('Rejects null glassParts when not repair', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getShallowMountedComponent();
|
||||
wrapper.vm.mainStore.order.damage.isRepair = false;
|
||||
wrapper.vm.mainStore.order.lineItems.glassParts = null;
|
||||
|
||||
// Act
|
||||
const isValid = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
// Assert
|
||||
expect(isValid).toBe(false);
|
||||
});
|
||||
});
|
||||
describe('Service Location requirements', () => {
|
||||
test('Accepts null provider address when mobile appointment', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getShallowMountedComponent();
|
||||
wrapper.vm.mainStore.order.serviceLocation.appointmentType = 'Mobile';
|
||||
wrapper.vm.mainStore.order.serviceLocation.provider.address = {};
|
||||
|
||||
// Act
|
||||
const isValid = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
// Assert
|
||||
expect(isValid).toBe(true);
|
||||
});
|
||||
test('Reject null provider address when non-mobile appointment', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getShallowMountedComponent();
|
||||
wrapper.vm.mainStore.order.serviceLocation.appointmentType = 'Inshop';
|
||||
wrapper.vm.mainStore.order.serviceLocation.provider.address = {};
|
||||
|
||||
// Act
|
||||
const isValid = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
// Assert
|
||||
expect(isValid).toBe(false);
|
||||
});
|
||||
test('Accepts null service location address when non-mobile appointment', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getShallowMountedComponent();
|
||||
wrapper.vm.mainStore.order.serviceLocation.appointmentType = 'Inshop';
|
||||
wrapper.vm.mainStore.order.serviceLocation.address = null;
|
||||
wrapper.vm.mainStore.order.serviceLocation.address2 = null;
|
||||
wrapper.vm.mainStore.order.serviceLocation.zipCode = null;
|
||||
wrapper.vm.mainStore.order.serviceLocation.city = null;
|
||||
wrapper.vm.mainStore.order.serviceLocation.state = null;
|
||||
|
||||
// Act
|
||||
const isValid = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
// Assert
|
||||
expect(isValid).toBe(true);
|
||||
});
|
||||
test('Rejects null service location address when mobile appointment', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getShallowMountedComponent();
|
||||
wrapper.vm.mainStore.order.serviceLocation.appointmentType = 'Mobile';
|
||||
wrapper.vm.mainStore.order.serviceLocation.address = null;
|
||||
wrapper.vm.mainStore.order.serviceLocation.address2 = null;
|
||||
wrapper.vm.mainStore.order.serviceLocation.zipCode = null;
|
||||
wrapper.vm.mainStore.order.serviceLocation.city = null;
|
||||
wrapper.vm.mainStore.order.serviceLocation.state = null;
|
||||
|
||||
// Act
|
||||
const isValid = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
// Assert
|
||||
expect(isValid).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,41 +1,134 @@
|
|||
<template>
|
||||
<Form
|
||||
ref="theForm"
|
||||
v-slot="{ meta }"
|
||||
@submit="onSubmit"
|
||||
@invalidSubmit="onInvalidSubmit">
|
||||
<!-- When customer-details is added: v-slot="{ meta }" -->
|
||||
<div class="page-container-grouped-styles">
|
||||
<div class="fade-on-route-transition position-relative">
|
||||
<siteHeader cmsWidgetName="SiteHeaderWidget" />
|
||||
<div class="container-fluid pb-2 review">
|
||||
<p>Placeholder for review page</p>
|
||||
<siteFooter
|
||||
ref="siteFooter"
|
||||
cmsWidgetName="SiteFooterWidget"
|
||||
:isForwardActionDisabled="!meta.valid"
|
||||
@ForwardClicked="navigateForward"
|
||||
@backClicked="backButtonAction" />
|
||||
<siteHeader cmsWidgetName="SiteHeaderWidget" />
|
||||
<div class="main-content-container">
|
||||
<vehicleBanner
|
||||
cmsWidgetName="VehicleBannerWidget"
|
||||
class="mb-4"
|
||||
:displayGenericVehicleImage="false" />
|
||||
<textBlock
|
||||
:customText="subHeaderTitle"
|
||||
typeStyle="h5"
|
||||
justifyText="center"
|
||||
margin="mt-1"
|
||||
class="dark-header" />
|
||||
<textBlock
|
||||
:customText="subHeaderBody"
|
||||
typeStyle="body"
|
||||
justifyText="left"
|
||||
class="mb-4"
|
||||
margin="mt-0 mb-2" />
|
||||
<buttonMain
|
||||
ref="buttonMain"
|
||||
:buttonText="forwardButtonText"
|
||||
isPrimary
|
||||
loaderColor="white"
|
||||
class="mb-2 w-100"
|
||||
@clickEvent="forwardButtonAction" />
|
||||
<div>
|
||||
<hr />
|
||||
</div>
|
||||
<textBlock
|
||||
customText="Appointment Details"
|
||||
typeStyle="label bold"
|
||||
justifyText="justify-text-left"
|
||||
margin="mt-0"
|
||||
class="dark-header" />
|
||||
<div class="px-4">
|
||||
<vehicleReview
|
||||
cmsWidgetName="VehicleReviewWidget"
|
||||
:vehicle="vehicleInfo"
|
||||
@editClicked="editVehicle" />
|
||||
<hr class="my-0" />
|
||||
<damageReview
|
||||
cmsWidgetName="DamageReviewWidget"
|
||||
damageLocationsWidgetName="DamageLocationsWidget"
|
||||
:damage="damageInfo"
|
||||
@editClicked="editDamage" />
|
||||
<hr class="my-0" />
|
||||
<servicePackageReview
|
||||
ref="servicePackageReview"
|
||||
servicePackageOptionsCmsName="ServicePackageTitle"
|
||||
defaultPackageItemsCmsName="DefaultPackageItemDescriptions"
|
||||
vapsItemsCmsName="VapsItemDescriptions"
|
||||
:damage="damageInfo"
|
||||
:lineItems="lineItems"
|
||||
@editClicked="editServicePackage" />
|
||||
<hr class="my-0" />
|
||||
<serviceLocationReview
|
||||
cmsWidgetName="ServiceLocationTitleWidget"
|
||||
:serviceLocation="serviceLocationInfo"
|
||||
@editClicked="editServiceLocation" />
|
||||
<hr class="my-0" />
|
||||
<scheduleReview
|
||||
cmsWidgetName="ScheduleWidget"
|
||||
:appointmentType="appointmentType"
|
||||
@editClicked="editSchedule" />
|
||||
<hr class="my-0" />
|
||||
<customerReview
|
||||
cmsWidgetName="CustomerReviewWidget"
|
||||
:customer="customerInfo"
|
||||
@editClicked="editCustomerDetails" />
|
||||
</div>
|
||||
<div>
|
||||
<hr class="my-0" />
|
||||
</div>
|
||||
<siteFooter
|
||||
ref="siteFooter"
|
||||
class="mt-5"
|
||||
cmsWidgetName="SiteFooterWidget"
|
||||
@ForwardClicked="forwardButtonAction"
|
||||
@backClicked="backButtonAction" />
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// Components
|
||||
import siteHeader from '@/iss-components/site-header/site-header.vue';
|
||||
import siteFooter from '@/iss-components/site-footer/site-footer.vue';
|
||||
import vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner.vue';
|
||||
import buttonMain from '@/ux-components/button-main/button-main.vue';
|
||||
import textBlock from '@/digital-components/text-block/text-block.vue';
|
||||
|
||||
import customerReview from '@/layouts/review-page/review-sections/customer-review/customer-review.vue';
|
||||
import damageReview from '@/layouts/review-page/review-sections/damage-review/damage-review.vue';
|
||||
import scheduleReview from '@/layouts/review-page/review-sections/schedule-review/schedule-review.vue';
|
||||
import serviceLocationReview from '@/layouts/review-page/review-sections/service-location-review/service-location-review.vue';
|
||||
import servicePackageReview from '@/layouts/review-page/review-sections/service-package-review/service-package-review.vue';
|
||||
import vehicleReview from '@/layouts/review-page/review-sections/vehicle-review/vehicle-review.vue';
|
||||
|
||||
// Supporting files
|
||||
import { AppointmentTypeStrings } from '@/constants/schedule-constants';
|
||||
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
|
||||
import navigationScenarios from '@/router/router-constants/navigation-scenarios.js';
|
||||
import settleAllPromises from '@/helpers/layout-helper';
|
||||
import { Form } from 'vee-validate';
|
||||
import BaseFormMixin from '@/mixins/base-form-mixin.js';
|
||||
import { useMainStore } from '@/store';
|
||||
|
||||
// Validation
|
||||
import { Form } from 'vee-validate';
|
||||
|
||||
export default {
|
||||
name: 'review-page',
|
||||
components: {
|
||||
buttonMain,
|
||||
customerReview,
|
||||
damageReview,
|
||||
scheduleReview,
|
||||
servicePackageReview,
|
||||
serviceLocationReview,
|
||||
siteHeader,
|
||||
siteFooter,
|
||||
textBlock,
|
||||
vehicleBanner,
|
||||
vehicleReview,
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form
|
||||
},
|
||||
|
|
@ -43,13 +136,13 @@ export default {
|
|||
async beforeRouteEnter(to, from, next) {
|
||||
// Call APIs
|
||||
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
|
||||
// Settle promises and get results
|
||||
|
||||
const promiseResultMap = [
|
||||
{
|
||||
resultKey: 'cmsContent',
|
||||
promise: cmsContentPromise
|
||||
}];
|
||||
// use resultMap to populate layout content.
|
||||
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
next((vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
|
|
@ -61,23 +154,170 @@ export default {
|
|||
},
|
||||
data() {
|
||||
},
|
||||
computed: {
|
||||
subHeaderTitle() {
|
||||
return this.getCmsContent('SiteSubHeaderWidget', 'HeaderText');
|
||||
},
|
||||
subHeaderBody() {
|
||||
return this.getCmsContent('SiteSubHeaderWidget', 'BodyText');
|
||||
},
|
||||
forwardButtonText() {
|
||||
return this.getCmsContent('SiteFooterWidget', 'ForwardButtonText');
|
||||
},
|
||||
vehicleInfo() {
|
||||
return useMainStore().vehicle;
|
||||
},
|
||||
damageInfo() {
|
||||
return useMainStore().damage;
|
||||
},
|
||||
lineItems() {
|
||||
return useMainStore().lineItems;
|
||||
},
|
||||
serviceLocationInfo() {
|
||||
return useMainStore().order.serviceLocation;
|
||||
},
|
||||
appointmentType() {
|
||||
return useMainStore().order.serviceLocation.appointmentType;
|
||||
},
|
||||
customerInfo() {
|
||||
return useMainStore().order.customer;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
arePagePrerequisitesValid() {
|
||||
// Vehicle
|
||||
const { vehicle } = useMainStore().order;
|
||||
const vehicleReqs = !!(vehicle.year && vehicle.make && vehicle.model && vehicle.style);
|
||||
|
||||
// Damage
|
||||
const { damage } = useMainStore().order;
|
||||
const damageReqs = !!(
|
||||
(damage.isRepair && damage.numberOfChips)
|
||||
|| (!damage.isRepair && damage.glassToReplace?.length)
|
||||
);
|
||||
|
||||
// Service Package
|
||||
const { lineItems } = useMainStore().order;
|
||||
// damageReqs handles checking for damage, even though it is also required for this section.
|
||||
const packageReqs = !!(
|
||||
(damage.isRepair || lineItems.glassParts)
|
||||
&& lineItems.supportingItems
|
||||
);
|
||||
|
||||
// Service Location
|
||||
const { serviceLocation } = useMainStore().order;
|
||||
const mobileReqs = !!(
|
||||
serviceLocation.address
|
||||
&& serviceLocation.city
|
||||
&& serviceLocation.state
|
||||
&& serviceLocation.zipCode
|
||||
);
|
||||
|
||||
const providerLocation = serviceLocation.provider.address;
|
||||
const dropOffInshopReqs = !!(
|
||||
providerLocation.streetAddress
|
||||
&& providerLocation.city
|
||||
&& providerLocation.state
|
||||
&& providerLocation.zipCode
|
||||
);
|
||||
|
||||
const isMobile = serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE;
|
||||
const serviceLocationReqs =
|
||||
(isMobile && mobileReqs) || (!isMobile && dropOffInshopReqs);
|
||||
|
||||
// Schedule
|
||||
const { schedule } = useMainStore().order;
|
||||
const scheduleReqs = !!(
|
||||
schedule.date
|
||||
&& schedule.startTime
|
||||
&& schedule.endTime
|
||||
&& schedule.jobMaxMinutes
|
||||
&& schedule.jobMinMinutes
|
||||
);
|
||||
|
||||
// Customer
|
||||
const { customer } = useMainStore().order;
|
||||
const customerReqs = !!(
|
||||
customer.firstName
|
||||
&& customer.lastName
|
||||
&& customer.phoneNumber
|
||||
&& customer.emailAddress
|
||||
);
|
||||
|
||||
return (
|
||||
vehicleReqs
|
||||
&& damageReqs
|
||||
&& packageReqs
|
||||
&& serviceLocationReqs
|
||||
&& scheduleReqs
|
||||
&& customerReqs
|
||||
);
|
||||
},
|
||||
backButtonAction() {
|
||||
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||
this.$router.navigate(navigationScenarios.CLICKED_BACK, this.$route);
|
||||
},
|
||||
editVehicle() {
|
||||
this.$router.navigateWithoutSaving(
|
||||
navigationScenarios.CLICKED_VEHICLE_EDIT,
|
||||
this.$route
|
||||
);
|
||||
},
|
||||
editDamage() {
|
||||
this.$router.navigateWithoutSaving(
|
||||
navigationScenarios.CLICKED_DAMAGE_EDIT,
|
||||
this.$route
|
||||
);
|
||||
},
|
||||
editServicePackage() {
|
||||
this.$router.navigateWithoutSaving(
|
||||
navigationScenarios.CLICKED_SERVICE_PACKAGE_EDIT,
|
||||
this.$route
|
||||
);
|
||||
},
|
||||
editServiceLocation() {
|
||||
this.$router.navigateWithoutSaving(
|
||||
navigationScenarios.CLICKED_SERVICE_LOCATION_EDIT,
|
||||
this.$route
|
||||
);
|
||||
},
|
||||
editSchedule() {
|
||||
this.$router.navigateWithoutSaving(
|
||||
navigationScenarios.CLICKED_SCHEDULE_EDIT,
|
||||
this.$route
|
||||
);
|
||||
},
|
||||
editCustomerDetails() {
|
||||
this.$router.navigateWithoutSaving(
|
||||
navigationScenarios.CLICKED_CUSTOMER_EDIT,
|
||||
this.$route
|
||||
);
|
||||
},
|
||||
forwardButtonAction() {
|
||||
this.navigateForward();
|
||||
},
|
||||
navigateForward() {
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD,
|
||||
this.$router.navigateWithoutSaving(
|
||||
navigationScenarios.CLICKED_FORWARD,
|
||||
this.$route
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.review p{
|
||||
margin-bottom:0.75rem;
|
||||
$page-side-padding: 1.5rem;
|
||||
|
||||
.page-container-grouped-styles {
|
||||
overflow: auto;
|
||||
|
||||
.main-content-container {
|
||||
padding: 0 1.5rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
.dark-header {
|
||||
color: $black;
|
||||
line-height: 1.6;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,129 @@
|
|||
// Components
|
||||
import customerReview from '@/layouts/review-page/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'
|
||||
}
|
||||
},
|
||||
customer: {
|
||||
firstName: 'First',
|
||||
lastName: 'Last',
|
||||
phoneNumber: '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.customer.firstName,
|
||||
lastName: testConstants.customer.lastName,
|
||||
phoneNumber: testConstants.customer.phoneNumber,
|
||||
emailAddress: testConstants.customer.emailAddress,
|
||||
isSmsOptIn: testConstants.customer.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
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
<template>
|
||||
<reviewBlock
|
||||
editScreenReaderTextCmsWidgetName="EditContactDetailsScreenReader"
|
||||
:customHeaderText="header"
|
||||
:content="displayContent"
|
||||
@editClicked="editClicked" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import reviewBlock from '@/layouts/review-page/review-block/review-block.vue';
|
||||
|
||||
export default {
|
||||
name: 'customer-review',
|
||||
components: {
|
||||
reviewBlock
|
||||
},
|
||||
props: {
|
||||
cmsWidgetName: String,
|
||||
customer: Object
|
||||
},
|
||||
emits: ['edit-clicked'],
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
computed: {
|
||||
displayContent() {
|
||||
return [this.fullName, this.email, this.phoneNumber, this.smsOptIn];
|
||||
},
|
||||
header() {
|
||||
return this.getCmsContent(this.cmsWidgetName, 'HeaderText');
|
||||
},
|
||||
fullName() {
|
||||
return `${this.customer?.firstName} ${this.customer?.lastName}`;
|
||||
},
|
||||
email() {
|
||||
return this.customer?.emailAddress;
|
||||
},
|
||||
phoneNumber() {
|
||||
return this.customer?.phoneNumber;
|
||||
},
|
||||
smsOptIn() {
|
||||
return this.getCmsContent(this.cmsWidgetName, 'SubheaderText');
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
editClicked() {
|
||||
this.$emit('edit-clicked');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,436 @@
|
|||
// Components
|
||||
import damageReview from '@/layouts/review-page/review-sections/damage-review/damage-review.vue';
|
||||
|
||||
// Supporting Files
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
||||
import damageLocationsSelected from '@/constants/damage-locations-selected';
|
||||
|
||||
const testConstants = {
|
||||
cmsConstants: {
|
||||
widgetNames: {
|
||||
header: 'DamageReviewWidget',
|
||||
locations: 'DamageLocationsWidget',
|
||||
driverDamages: 'DriverDamagesWidget',
|
||||
passengerDamages: 'PassengerDamagesWidget'
|
||||
},
|
||||
header: {
|
||||
text: 'Damage'
|
||||
},
|
||||
damageLocations: {
|
||||
windshield: damageLocationsSelected.WINDSHIELD,
|
||||
driver: damageLocationsSelected.DRIVER,
|
||||
passenger: damageLocationsSelected.PASSENGER,
|
||||
rear: damageLocationsSelected.REAR
|
||||
},
|
||||
damageNames: {
|
||||
vent: damageLocationsSelected.VENT,
|
||||
front: damageLocationsSelected.FRONT,
|
||||
back: damageLocationsSelected.BACK,
|
||||
quarter: damageLocationsSelected.QUARTER,
|
||||
side: damageLocationsSelected.SIDEDOOR
|
||||
},
|
||||
locationCopy: {
|
||||
windshield: 'Windshield copy',
|
||||
driver: 'Driver copy',
|
||||
passenger: 'Passenger copy',
|
||||
rear: 'Rear copy'
|
||||
},
|
||||
damageCopy: {
|
||||
vent: 'Vent copy',
|
||||
front: 'Front copy',
|
||||
back: 'Back copy',
|
||||
quarter: 'Quarter copy',
|
||||
side: 'Side copy'
|
||||
},
|
||||
imageId: '00000000-0000-0000-0000-000000000000'
|
||||
},
|
||||
makeBulletedList: (items) => {
|
||||
let list = '<ul>';
|
||||
items.forEach((item) => {
|
||||
list += `<li>${item}</li>`;
|
||||
});
|
||||
list += '</ul>';
|
||||
|
||||
return list;
|
||||
},
|
||||
glassItems: {
|
||||
windshield: {
|
||||
glassLocation: damageLocationsSelected.WINDSHIELD,
|
||||
glassName: damageLocationsSelected.SINGLE
|
||||
},
|
||||
rear: {
|
||||
glassLocation: damageLocationsSelected.REAR,
|
||||
glassName: damageLocationsSelected.STATIONARY
|
||||
},
|
||||
passengerItems: {
|
||||
vent: {
|
||||
glassLocation: damageLocationsSelected.PASSENGER,
|
||||
glassName: damageLocationsSelected.VENT
|
||||
},
|
||||
front: {
|
||||
glassLocation: damageLocationsSelected.PASSENGER,
|
||||
glassName: damageLocationsSelected.FRONT
|
||||
},
|
||||
back: {
|
||||
glassLocation: damageLocationsSelected.PASSENGER,
|
||||
glassName: damageLocationsSelected.BACK
|
||||
},
|
||||
quarter: {
|
||||
glassLocation: damageLocationsSelected.PASSENGER,
|
||||
glassName: damageLocationsSelected.QUARTER
|
||||
},
|
||||
side: {
|
||||
glassLocation: damageLocationsSelected.PASSENGER,
|
||||
glassName: damageLocationsSelected.SIDEDOOR
|
||||
}
|
||||
},
|
||||
driverItems: {
|
||||
vent: {
|
||||
glassLocation: damageLocationsSelected.DRIVER,
|
||||
glassName: damageLocationsSelected.VENT
|
||||
},
|
||||
front: {
|
||||
glassLocation: damageLocationsSelected.DRIVER,
|
||||
glassName: damageLocationsSelected.FRONT
|
||||
},
|
||||
back: {
|
||||
glassLocation: damageLocationsSelected.DRIVER,
|
||||
glassName: damageLocationsSelected.BACK
|
||||
},
|
||||
quarter: {
|
||||
glassLocation: damageLocationsSelected.DRIVER,
|
||||
glassName: damageLocationsSelected.QUARTER
|
||||
},
|
||||
side: {
|
||||
glassLocation: damageLocationsSelected.DRIVER,
|
||||
glassName: damageLocationsSelected.SIDEDOOR
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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(damageReview, mountOptions);
|
||||
return { wrapper };
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
cmsContent = {
|
||||
DamageReviewWidget: {
|
||||
Text: testConstants.cmsConstants.header.text
|
||||
},
|
||||
DamageLocationsWidget: {
|
||||
Answers: [
|
||||
{
|
||||
Name: testConstants.cmsConstants.damageLocations.windshield,
|
||||
Text: testConstants.cmsConstants.locationCopy.windshield,
|
||||
SubText: '',
|
||||
ImageId: testConstants.cmsConstants.imageId,
|
||||
Image: '',
|
||||
SubWidgetName: ''
|
||||
},
|
||||
{
|
||||
Name: testConstants.cmsConstants.damageLocations.driver,
|
||||
Text: testConstants.cmsConstants.locationCopy.driver,
|
||||
SubText: '',
|
||||
ImageId: testConstants.cmsConstants.imageId,
|
||||
Image: '',
|
||||
SubWidgetName: testConstants.cmsConstants.widgetNames.driverDamages
|
||||
},
|
||||
{
|
||||
Name: testConstants.cmsConstants.damageLocations.passenger,
|
||||
Text: testConstants.cmsConstants.locationCopy.passenger,
|
||||
SubText: '',
|
||||
ImageId: testConstants.cmsConstants.imageId,
|
||||
Image: '',
|
||||
SubWidgetName: testConstants.cmsConstants.widgetNames.passengerDamages
|
||||
},
|
||||
{
|
||||
Name: testConstants.cmsConstants.damageLocations.rear,
|
||||
Text: testConstants.cmsConstants.locationCopy.rear,
|
||||
SubText: '',
|
||||
ImageId: testConstants.cmsConstants.imageId,
|
||||
Image: '',
|
||||
SubWidgetName: ''
|
||||
}
|
||||
]
|
||||
},
|
||||
DriverDamagesWidget: {
|
||||
Answers: [
|
||||
{
|
||||
Name: testConstants.cmsConstants.damageNames.vent,
|
||||
Text: testConstants.cmsConstants.damageCopy.vent,
|
||||
SubText: '',
|
||||
ImageId: testConstants.cmsConstants.imageId,
|
||||
Image: '',
|
||||
SubWidgetName: ''
|
||||
},
|
||||
{
|
||||
Name: testConstants.cmsConstants.damageNames.front,
|
||||
Text: testConstants.cmsConstants.damageCopy.front,
|
||||
SubText: '',
|
||||
ImageId: testConstants.cmsConstants.imageId,
|
||||
Image: '',
|
||||
SubWidgetName: ''
|
||||
},
|
||||
{
|
||||
Name: testConstants.cmsConstants.damageNames.back,
|
||||
Text: testConstants.cmsConstants.damageCopy.back,
|
||||
SubText: '',
|
||||
ImageId: testConstants.cmsConstants.imageId,
|
||||
Image: '',
|
||||
SubWidgetName: ''
|
||||
},
|
||||
{
|
||||
Name: testConstants.cmsConstants.damageNames.quarter,
|
||||
Text: testConstants.cmsConstants.damageCopy.quarter,
|
||||
SubText: '',
|
||||
ImageId: testConstants.cmsConstants.imageId,
|
||||
Image: '',
|
||||
SubWidgetName: ''
|
||||
},
|
||||
{
|
||||
Name: testConstants.cmsConstants.damageNames.side,
|
||||
Text: testConstants.cmsConstants.damageCopy.side,
|
||||
SubText: '',
|
||||
ImageId: testConstants.cmsConstants.imageId,
|
||||
Image: '',
|
||||
SubWidgetName: ''
|
||||
}
|
||||
]
|
||||
},
|
||||
PassengerDamagesWidget: {
|
||||
Answers: [
|
||||
{
|
||||
Name: testConstants.cmsConstants.damageNames.vent,
|
||||
Text: testConstants.cmsConstants.damageCopy.vent,
|
||||
SubText: '',
|
||||
ImageId: testConstants.cmsConstants.imageId,
|
||||
Image: '',
|
||||
SubWidgetName: ''
|
||||
},
|
||||
{
|
||||
Name: testConstants.cmsConstants.damageNames.front,
|
||||
Text: testConstants.cmsConstants.damageCopy.front,
|
||||
SubText: '',
|
||||
ImageId: testConstants.cmsConstants.imageId,
|
||||
Image: '',
|
||||
SubWidgetName: ''
|
||||
},
|
||||
{
|
||||
Name: testConstants.cmsConstants.damageNames.back,
|
||||
Text: testConstants.cmsConstants.damageCopy.back,
|
||||
SubText: '',
|
||||
ImageId: testConstants.cmsConstants.imageId,
|
||||
Image: '',
|
||||
SubWidgetName: ''
|
||||
},
|
||||
{
|
||||
Name: testConstants.cmsConstants.damageNames.quarter,
|
||||
Text: testConstants.cmsConstants.damageCopy.quarter,
|
||||
SubText: '',
|
||||
ImageId: testConstants.cmsConstants.imageId,
|
||||
Image: '',
|
||||
SubWidgetName: ''
|
||||
},
|
||||
{
|
||||
Name: testConstants.cmsConstants.damageNames.side,
|
||||
Text: testConstants.cmsConstants.damageCopy.side,
|
||||
SubText: '',
|
||||
ImageId: testConstants.cmsConstants.imageId,
|
||||
Image: '',
|
||||
SubWidgetName: ''
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
describe('Damage Review Block', () => {
|
||||
describe('Correctly assembles damage info into a display string', () => {
|
||||
test('Shows windshield copy when windshield damage is included', async () => {
|
||||
// Arrange
|
||||
const { wrapper } = getShallowMountedComponent({
|
||||
cmsWidgetName: testConstants.cmsConstants.widgetNames.header,
|
||||
damageLocationsWidgetName: testConstants.cmsConstants.widgetNames.locations,
|
||||
damage: {
|
||||
isRepair: false,
|
||||
glassToReplace: [testConstants.glassItems.windshield]
|
||||
}
|
||||
});
|
||||
|
||||
// Act
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.displayContent).toStrictEqual([
|
||||
testConstants.cmsConstants.locationCopy.windshield
|
||||
]);
|
||||
});
|
||||
|
||||
test('Windshield copy is shown when order is a repair', async () => {
|
||||
// Arrange
|
||||
const { wrapper } = getShallowMountedComponent({
|
||||
cmsWidgetName: testConstants.cmsConstants.widgetNames.header,
|
||||
damageLocationsWidgetName: testConstants.cmsConstants.widgetNames.locations,
|
||||
damage: {
|
||||
isRepair: true,
|
||||
numberOfChips: 2,
|
||||
glassToReplace: []
|
||||
}
|
||||
});
|
||||
|
||||
// Act
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.displayContent).toStrictEqual([
|
||||
testConstants.cmsConstants.locationCopy.windshield
|
||||
]);
|
||||
});
|
||||
|
||||
test('Rear windshield copy shows when rear damage is present', async () => {
|
||||
// Arrange
|
||||
const { wrapper } = getShallowMountedComponent({
|
||||
cmsWidgetName: testConstants.cmsConstants.widgetNames.header,
|
||||
damageLocationsWidgetName: testConstants.cmsConstants.widgetNames.locations,
|
||||
damage: {
|
||||
isRepair: false,
|
||||
numberOfChips: null,
|
||||
glassToReplace: [testConstants.glassItems.rear]
|
||||
}
|
||||
});
|
||||
|
||||
// Act
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.displayContent).toStrictEqual([
|
||||
testConstants.cmsConstants.locationCopy.rear
|
||||
]);
|
||||
});
|
||||
|
||||
test('Driver side copy and items are shown when driver side damage is present', async () => {
|
||||
// Arrange
|
||||
const { wrapper } = getShallowMountedComponent({
|
||||
cmsWidgetName: testConstants.cmsConstants.widgetNames.header,
|
||||
damageLocationsWidgetName: testConstants.cmsConstants.widgetNames.locations,
|
||||
damage: {
|
||||
isRepair: false,
|
||||
numberOfChips: null,
|
||||
glassToReplace: [
|
||||
testConstants.glassItems.driverItems.back,
|
||||
testConstants.glassItems.driverItems.front
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
// Act
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.displayContent).toStrictEqual([
|
||||
testConstants.cmsConstants.locationCopy.driver,
|
||||
testConstants.makeBulletedList([
|
||||
testConstants.cmsConstants.damageCopy.front,
|
||||
testConstants.cmsConstants.damageCopy.back
|
||||
])
|
||||
]);
|
||||
});
|
||||
|
||||
test('Passenger side copy and items are shown when passenger side damage is present', async () => {
|
||||
// Arrange
|
||||
const { wrapper } = getShallowMountedComponent({
|
||||
cmsWidgetName: testConstants.cmsConstants.widgetNames.header,
|
||||
damageLocationsWidgetName: testConstants.cmsConstants.widgetNames.locations,
|
||||
damage: {
|
||||
isRepair: false,
|
||||
numberOfChips: null,
|
||||
glassToReplace: [
|
||||
testConstants.glassItems.passengerItems.quarter,
|
||||
testConstants.glassItems.passengerItems.vent,
|
||||
testConstants.glassItems.passengerItems.side
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
// Act
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.displayContent).toStrictEqual([
|
||||
testConstants.cmsConstants.locationCopy.passenger,
|
||||
testConstants.makeBulletedList([
|
||||
testConstants.cmsConstants.damageCopy.vent,
|
||||
testConstants.cmsConstants.damageCopy.quarter,
|
||||
testConstants.cmsConstants.damageCopy.side
|
||||
])
|
||||
]);
|
||||
});
|
||||
|
||||
test('All relevant sections are shown in order in multiglass scenario', async () => {
|
||||
// Arrange
|
||||
const { wrapper } = getShallowMountedComponent({
|
||||
cmsWidgetName: testConstants.cmsConstants.widgetNames.header,
|
||||
damageLocationsWidgetName: testConstants.cmsConstants.widgetNames.locations,
|
||||
damage: {
|
||||
isRepair: false,
|
||||
numberOfChips: null,
|
||||
glassToReplace: [
|
||||
testConstants.glassItems.windshield,
|
||||
testConstants.glassItems.rear,
|
||||
testConstants.glassItems.driverItems.vent,
|
||||
testConstants.glassItems.driverItems.front,
|
||||
testConstants.glassItems.driverItems.back,
|
||||
testConstants.glassItems.passengerItems.quarter,
|
||||
testConstants.glassItems.passengerItems.back,
|
||||
testConstants.glassItems.passengerItems.side
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
// Act
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.displayContent).toStrictEqual([
|
||||
testConstants.cmsConstants.locationCopy.windshield,
|
||||
testConstants.cmsConstants.locationCopy.driver,
|
||||
testConstants.makeBulletedList([
|
||||
testConstants.cmsConstants.damageCopy.vent,
|
||||
testConstants.cmsConstants.damageCopy.front,
|
||||
testConstants.cmsConstants.damageCopy.back
|
||||
]),
|
||||
testConstants.cmsConstants.locationCopy.passenger,
|
||||
testConstants.makeBulletedList([
|
||||
testConstants.cmsConstants.damageCopy.back,
|
||||
testConstants.cmsConstants.damageCopy.quarter,
|
||||
testConstants.cmsConstants.damageCopy.side
|
||||
]),
|
||||
testConstants.cmsConstants.locationCopy.rear
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
<template>
|
||||
<reviewBlock
|
||||
editScreenReaderTextCmsWidgetName="EditDamageScreenReader"
|
||||
:headerCmsWidgetName="cmsWidgetName"
|
||||
:content="displayContent"
|
||||
@editClicked="editClicked" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import damageLocationsSelected from '@/constants/damage-locations-selected.js';
|
||||
import reviewBlock from '@/layouts/review-page/review-block/review-block.vue';
|
||||
|
||||
export default {
|
||||
name: 'damage-review',
|
||||
components: {
|
||||
reviewBlock
|
||||
},
|
||||
props: {
|
||||
cmsWidgetName: String,
|
||||
damageLocationsWidgetName: String,
|
||||
damage: Object
|
||||
},
|
||||
emits: ['edit-clicked'],
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
computed: {
|
||||
displayContent() {
|
||||
return [
|
||||
...this.windshieldCopy,
|
||||
...this.driverSideCopy,
|
||||
...this.passengerSideCopy,
|
||||
...this.rearCopy
|
||||
];
|
||||
},
|
||||
hasWindshieldDamage() {
|
||||
const windshieldPieces = this.getGlassPieces(damageLocationsSelected.WINDSHIELD);
|
||||
|
||||
return this.damage.isRepair || !!windshieldPieces?.length;
|
||||
},
|
||||
hasDriverSideDamage() {
|
||||
const driverPieces = this.getGlassPieces(damageLocationsSelected.DRIVER);
|
||||
|
||||
return !!driverPieces?.length;
|
||||
},
|
||||
hasPassengerSideDamage() {
|
||||
const passengerPieces = this.getGlassPieces(damageLocationsSelected.PASSENGER);
|
||||
|
||||
return !!passengerPieces?.length;
|
||||
},
|
||||
hasRearDamage() {
|
||||
const rearPieces = this.getGlassPieces(damageLocationsSelected.REAR);
|
||||
|
||||
return !!rearPieces?.length;
|
||||
},
|
||||
windshieldCopy() {
|
||||
const answerContent = this.getLocationAnswer(damageLocationsSelected.WINDSHIELD);
|
||||
|
||||
if (this.hasWindshieldDamage) {
|
||||
return [answerContent?.Text];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
driverSideCopy() {
|
||||
const answerContent = this.getLocationAnswer(damageLocationsSelected.DRIVER);
|
||||
const damageAnswers = this.getAnswersNullSafe(answerContent?.SubWidgetName);
|
||||
const driverSideItems = this.getGlassPieces(damageLocationsSelected.DRIVER);
|
||||
const damageAnswersOnOrder = damageAnswers?.filter((answer) =>
|
||||
driverSideItems?.some((glassPiece) => answer.Name === glassPiece.glassName));
|
||||
|
||||
if (this.hasDriverSideDamage) {
|
||||
return [
|
||||
answerContent?.Text,
|
||||
this.generateBulletedListFromAnswers(damageAnswersOnOrder)
|
||||
];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
passengerSideCopy() {
|
||||
const answerContent = this.getLocationAnswer(damageLocationsSelected.PASSENGER);
|
||||
const damageAnswers = this.getAnswersNullSafe(answerContent?.SubWidgetName);
|
||||
const passengerSideItems = this.getGlassPieces(damageLocationsSelected.PASSENGER);
|
||||
const damageAnswersOnOrder = damageAnswers?.filter((answer) =>
|
||||
passengerSideItems?.some((glassPiece) => answer.Name === glassPiece.glassName));
|
||||
|
||||
if (this.hasPassengerSideDamage) {
|
||||
return [
|
||||
answerContent?.Text,
|
||||
this.generateBulletedListFromAnswers(damageAnswersOnOrder)
|
||||
];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
rearCopy() {
|
||||
const answerContent = this.getLocationAnswer(damageLocationsSelected.REAR);
|
||||
|
||||
if (this.hasRearDamage) {
|
||||
return [answerContent?.Text];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
locationAnswers() {
|
||||
return this.getAnswersNullSafe(this.damageLocationsWidgetName);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
editClicked() {
|
||||
this.$emit('edit-clicked');
|
||||
},
|
||||
getAnswersNullSafe(widgetName) {
|
||||
const rawAnswers = this.getCmsContent(widgetName, 'Answers');
|
||||
|
||||
return rawAnswers || [];
|
||||
},
|
||||
getLocationAnswer(damageLocation) {
|
||||
return this.locationAnswers?.find((answer) => answer.Name === damageLocation);
|
||||
},
|
||||
getGlassPieces(damageLocation) {
|
||||
return this.damage?.glassToReplace?.filter((item) => item.glassLocation === damageLocation);
|
||||
},
|
||||
generateBulletedListFromAnswers(answers) {
|
||||
let list = '<ul>';
|
||||
|
||||
answers.forEach((answer) => {
|
||||
list += `<li>${answer.Text}</li>`;
|
||||
});
|
||||
|
||||
list += '</ul>';
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<template>
|
||||
<reviewBlock
|
||||
editScreenReaderTextCmsWidgetName="EditAppointmentScreenReader"
|
||||
:customHeaderText="headerText"
|
||||
:content="displayContent"
|
||||
@editClicked="editClicked" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import reviewBlock from '@/layouts/review-page/review-block/review-block.vue';
|
||||
|
||||
export default {
|
||||
name: 'schedule-review',
|
||||
components: {
|
||||
reviewBlock
|
||||
},
|
||||
props: {
|
||||
cmsWidgetName: String,
|
||||
appointmentType: String
|
||||
},
|
||||
emits: ['edit-clicked'],
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
computed: {
|
||||
displayContent() {
|
||||
return [
|
||||
this.getCmsContent(this.cmsWidgetName, 'BodyText'),
|
||||
this.getCmsContent(this.cmsWidgetName, 'BodyText2')
|
||||
];
|
||||
},
|
||||
headerText() {
|
||||
return this.getCmsContent(this.cmsWidgetName, 'HeaderText');
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
editClicked() {
|
||||
this.$emit('edit-clicked');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
// Components
|
||||
import serviceLocationReview from '@/layouts/review-page/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']);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
<template>
|
||||
<reviewBlock
|
||||
editScreenReaderTextCmsWidgetName="EditLocationScreenReader"
|
||||
:headerCmsWidgetName="cmsWidgetName"
|
||||
:content="displayContent"
|
||||
@editClicked="editClicked" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { AppointmentTypeStrings } from '@/constants/schedule-constants';
|
||||
import reviewBlock from '@/layouts/review-page/review-block/review-block.vue';
|
||||
|
||||
export default {
|
||||
name: 'service-location-review',
|
||||
components: {
|
||||
reviewBlock
|
||||
},
|
||||
props: {
|
||||
cmsWidgetName: String,
|
||||
serviceLocation: Object
|
||||
},
|
||||
emits: ['edit-clicked'],
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
computed: {
|
||||
displayContent() {
|
||||
return [
|
||||
`${this.addressInfo.address}${this.addressInfo.address2 ? ', ' : ''}${
|
||||
this.addressInfo.address2
|
||||
}, ${this.addressInfo.city}, ${this.addressInfo.state} ${this.addressInfo.zipCode}`
|
||||
];
|
||||
},
|
||||
addressInfo() {
|
||||
if (this.serviceLocation?.appointmentType === AppointmentTypeStrings.MOBILE) {
|
||||
return {
|
||||
address: this.serviceLocation?.address,
|
||||
address2: this.serviceLocation?.address2 ?? '',
|
||||
city: this.serviceLocation?.city,
|
||||
state: this.serviceLocation?.state,
|
||||
zipCode: this.serviceLocation?.zipCode
|
||||
};
|
||||
}
|
||||
return {
|
||||
address: this.serviceLocation?.provider?.address?.streetAddress,
|
||||
address2: '',
|
||||
city: this.serviceLocation?.provider?.address?.city,
|
||||
state: this.serviceLocation?.provider?.address?.state,
|
||||
zipCode: this.serviceLocation?.provider?.address?.zipCode
|
||||
};
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
editClicked() {
|
||||
this.$emit('edit-clicked');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,969 @@
|
|||
// Components
|
||||
import servicePackageReview from '@/layouts/review-page/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';
|
||||
|
||||
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 defense 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 DEFENSE',
|
||||
description: null,
|
||||
partType: 'RAIN DEFENSE',
|
||||
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(wrapper) {
|
||||
wrapper.vm.initializeComponent({
|
||||
wipers: [testConstants.parts.frontWiperPart],
|
||||
rainDefense: 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(wrapper);
|
||||
|
||||
// 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(wrapper);
|
||||
|
||||
// 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(wrapper);
|
||||
|
||||
// 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(wrapper);
|
||||
|
||||
// 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
|
||||
});
|
||||
|
||||
wrapper.vm.initializeComponent({
|
||||
wipers: scenario.params.wiperResponse,
|
||||
rainDefense: scenario.params.rainDefenseResponse
|
||||
});
|
||||
|
||||
// Act
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.packageNameWidget).toEqual(iteration.expected.packageNameWidget);
|
||||
expect(wrapper.vm.displayContent).toEqual(iteration.expected.displayContent);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
<template>
|
||||
<reviewBlock
|
||||
editScreenReaderTextCmsWidgetName="EditServiceTypeScreenReader"
|
||||
:headerCmsWidgetName="packageNameWidget"
|
||||
:content="displayContent"
|
||||
@editClicked="editClicked" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import settleAllPromises from '@/helpers/layout-helper';
|
||||
// import baseMixin from '@/mixins/base-mixin.js';
|
||||
// import store from '@/store';
|
||||
import reviewBlock from '@/layouts/review-page/review-block/review-block.vue';
|
||||
import {
|
||||
getHighestFullySatisfiedTier,
|
||||
containsLineItemWithPartType
|
||||
} from '@/helpers/service-package-helper.js';
|
||||
// import { storeActions } from '@/constants/store-actions.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 ?? []),
|
||||
...this.availableVaps
|
||||
];
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadInitialData() {
|
||||
/* const wipersPromise = baseMixin.methods.dispatchStoreActionWithLogging(
|
||||
storeActions.GET_WIPERS,
|
||||
{
|
||||
serviceZipCode: store.getters.order.serviceLocation.zipCode,
|
||||
carId: store.getters.vehicle.carId
|
||||
},
|
||||
'review'
|
||||
);
|
||||
const rainDefensePromise = baseMixin.methods.dispatchStoreActionWithLogging(
|
||||
storeActions.GET_RAIN_DEFENSE,
|
||||
null,
|
||||
'review'
|
||||
);
|
||||
|
||||
const promiseResultMap = [
|
||||
{
|
||||
resultKey: 'wipers',
|
||||
promise: wipersPromise
|
||||
},
|
||||
{
|
||||
resultKey: 'rainDefense',
|
||||
promise: rainDefensePromise
|
||||
}
|
||||
];
|
||||
|
||||
return settleAllPromises(promiseResultMap); */
|
||||
},
|
||||
initializeComponent(apiResponses) {
|
||||
const vapsFromApi = [...apiResponses.wipers, apiResponses.rainDefense];
|
||||
|
||||
this.availableVaps = vapsFromApi;
|
||||
},
|
||||
editClicked() {
|
||||
this.$emit('edit-clicked');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// Components
|
||||
import vehicleReview from '@/layouts/review-page/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']);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<template>
|
||||
<reviewBlock
|
||||
editScreenReaderTextCmsWidgetName="EditVehicleScreenReader"
|
||||
:headerCmsWidgetName="cmsWidgetName"
|
||||
:content="displayContent"
|
||||
@editClicked="editClicked" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import reviewBlock from '@/layouts/review-page/review-block/review-block.vue';
|
||||
|
||||
export default {
|
||||
name: 'vehicle-review',
|
||||
components: {
|
||||
reviewBlock
|
||||
},
|
||||
props: {
|
||||
cmsWidgetName: String,
|
||||
vehicle: Object
|
||||
},
|
||||
emits: ['edit-clicked'],
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
computed: {
|
||||
displayContent() {
|
||||
return [`${this.vehicle.year} ${this.vehicle.make} ${this.vehicle.model}`];
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
editClicked() {
|
||||
this.$emit('edit-clicked');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -1,72 +1,8 @@
|
|||
import { useMainStore } from '@/store';
|
||||
|
||||
export async function getAlertReasons(ctu) {
|
||||
export default async function getAlertReasons(ctu) {
|
||||
const store = useMainStore();
|
||||
const alertReasons = await store.getAlertReasonsByCtu(ctu);
|
||||
|
||||
return Promise.resolve(alertReasons);
|
||||
}
|
||||
|
||||
export function calcDaysBetweenDates(dateString1, dateString2) {
|
||||
const date1 = new Date(dateString1);
|
||||
const date2 = new Date(dateString2);
|
||||
const timeDifference = Math.abs(date2 - date1); // Calculate the time difference in milliseconds
|
||||
return Math.ceil(timeDifference / (1000 * 60 * 60 * 24)); // Convert milliseconds to days
|
||||
}
|
||||
|
||||
export function convertDateToDateString(date) {
|
||||
// returns YYYY-MM-DD format
|
||||
if (date instanceof Date !== true) return null;
|
||||
return (
|
||||
`${date.getFullYear()
|
||||
}-${
|
||||
(`0${date.getMonth() + 1}`).slice(-2)
|
||||
}-${
|
||||
(`0${date.getDate()}`).slice(-2)}`
|
||||
);
|
||||
}
|
||||
|
||||
export function convertDateStringToDate(dateString) {
|
||||
// dateString must be YYYY-MM-DD format
|
||||
if (typeof dateString !== 'string') return null;
|
||||
const dateParts = dateString.split('-');
|
||||
return new Date(dateParts[0], parseInt(dateParts[1], 10) - 1, dateParts[2]);
|
||||
}
|
||||
|
||||
export function sumDateString(dateString, daysToAdd) {
|
||||
// dateString must be YYYY-MM-DD format
|
||||
if (typeof dateString !== 'string') return null;
|
||||
const date = convertDateStringToDate(dateString);
|
||||
date.setDate(date.getDate() + daysToAdd);
|
||||
return convertDateToDateString(date);
|
||||
}
|
||||
|
||||
export function militaryToTwelveHourTime(timeString) {
|
||||
// Expected input: "HH:MM"
|
||||
if (typeof timeString !== 'string') return null;
|
||||
let hours = parseInt(timeString.split(':')[0], 10);
|
||||
const minutes = timeString.split(':')[1];
|
||||
const meridianNotation = hours > 11 ? 'PM' : 'AM';
|
||||
|
||||
if (hours > 12) {
|
||||
hours -= 12;
|
||||
}
|
||||
|
||||
return `${hours}:${minutes} ${meridianNotation}`;
|
||||
}
|
||||
|
||||
export function getDisplayTextForDurationLength(durationMinimum, durationMaximum) {
|
||||
const isLongAppointment = durationMaximum >= 120;
|
||||
const isDurationRange = durationMinimum !== durationMaximum;
|
||||
|
||||
const adjustedMinimum = isLongAppointment ? durationMinimum / 60 : durationMinimum;
|
||||
const adjustedMaximum = isLongAppointment ? durationMaximum / 60 : durationMaximum;
|
||||
|
||||
const durationText = isDurationRange
|
||||
? `${adjustedMinimum} - ${adjustedMaximum}`
|
||||
: adjustedMinimum;
|
||||
|
||||
const unitText = isLongAppointment ? 'hours' : 'minutes';
|
||||
|
||||
return `${durationText} ${unitText}`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
</template>
|
||||
<script>
|
||||
import alert from '@/ux-components/alert/alert.vue';
|
||||
import { getAlertReasons } from '@/layouts/schedule-page/helpers/schedule-helper';
|
||||
import getAlertReasons from '@/layouts/schedule-page/helpers/schedule-helper';
|
||||
|
||||
export default {
|
||||
name: 'location-alerts',
|
||||
|
|
|
|||
|
|
@ -70,11 +70,6 @@ import siteFooter from '@/iss-components/site-footer/site-footer.vue';
|
|||
import textBlock from '@/digital-components/text-block/text-block.vue';
|
||||
|
||||
// Supporting files
|
||||
import {
|
||||
calcDaysBetweenDates,
|
||||
convertDateStringToDate,
|
||||
sumDateString
|
||||
} from '@/layouts/schedule-page/helpers/schedule-helper';
|
||||
import {
|
||||
AppointmentTypeStrings,
|
||||
GET_MOBILE_TIME_SLOTS,
|
||||
|
|
@ -82,6 +77,7 @@ import {
|
|||
PREMIUM_FEE_PART_TYPE
|
||||
} from '@/constants/schedule-constants.js';
|
||||
import { fetchCmsContentForPage, splitCopyOnCMSPlaceHolder } from '@/helpers/cms-content-helper';
|
||||
import { calcDaysBetweenDates, convertDateStringToDate, sumDateString } from '@/helpers/date-helper';
|
||||
import settleAllPromises from '@/helpers/layout-helper';
|
||||
import { Form, defineRule } from 'vee-validate';
|
||||
import BaseFormMixin from '@/mixins/base-form-mixin.js';
|
||||
|
|
|
|||
|
|
@ -60,11 +60,7 @@ import {
|
|||
PREMIUM_TIME_SLOT_ID_FLAG,
|
||||
PREMIUM_FEE_PART_TYPE
|
||||
} from '@/constants/schedule-constants';
|
||||
import {
|
||||
convertDateStringToDate,
|
||||
militaryToTwelveHourTime,
|
||||
getDisplayTextForDurationLength
|
||||
} from '@/layouts/schedule-page/helpers/schedule-helper';
|
||||
import { convertDateStringToDate, getDisplayTextForDurationLength, militaryToTwelveHourTime } from '@/helpers/date-helper';
|
||||
import timeSlotModalListButton from './time-slot-modal-list-button/time-slot-modal-list-button.vue';
|
||||
|
||||
const cmsWidgetFieldMappings = {
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ import dropdownQuestion from '@/digital-components/dropdown-question/dropdown-qu
|
|||
import textBlock from '@/digital-components/text-block/text-block.vue';
|
||||
|
||||
// Supporting files
|
||||
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
|
||||
import { fetchCmsContentForPage, fetchGlobalCmsContent, updateCmsSiteHeader } from '@/helpers/cms-content-helper';
|
||||
import settleAllPromises from '@/helpers/layout-helper';
|
||||
import { required, regex } from '@/helpers/validation-rules';
|
||||
import errorMessages from '@/constants/error-messages';
|
||||
|
|
@ -226,19 +226,24 @@ export default {
|
|||
async beforeRouteEnter(to, from, next) {
|
||||
// Call APIs
|
||||
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
|
||||
const cmsGlobalSiteHeaderContentPromise = fetchGlobalCmsContent('iss-siteheader');
|
||||
|
||||
// Settle promises and get results
|
||||
const promiseResultMap = [
|
||||
{
|
||||
resultKey: 'cmsContent',
|
||||
promise: cmsContentPromise
|
||||
},
|
||||
{
|
||||
resultKey: 'globalSiteHeaderCmsContent',
|
||||
promise: cmsGlobalSiteHeaderContentPromise
|
||||
}
|
||||
];
|
||||
|
||||
// use resultMap to populate layout content.
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
|
||||
next((vm) => {
|
||||
updateCmsSiteHeader(resultMap.globalSiteHeaderCmsContent);
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
});
|
||||
},
|
||||
|
|
@ -317,7 +322,6 @@ export default {
|
|||
this.navigateForward();
|
||||
});
|
||||
},
|
||||
|
||||
navigateForward() {
|
||||
if (this.mainStore.applicationUser.duplicateOrders?.length > 0 ?? false) {
|
||||
this.$router.navigate(
|
||||
|
|
@ -354,7 +358,6 @@ export default {
|
|||
);
|
||||
}
|
||||
},
|
||||
|
||||
getWelcomePageModelFromStore() {
|
||||
return {
|
||||
policyNumber: this.mainStore.order.policy.policyNumber,
|
||||
|
|
|
|||
|
|
@ -217,7 +217,6 @@ function navigate(
|
|||
optionalParams = {},
|
||||
optionalPageData = {}
|
||||
) {
|
||||
/*eslint-disable-line*/
|
||||
if (!scenario) {
|
||||
window.console.error('No scenario provided. Please review the routing table.');
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -80,6 +80,14 @@ const navigationScenarios = Object.freeze({
|
|||
CLICKED_FORWARD_WITH_TPA_DISABLED: 'CLICKED_FORWARD_WITH_TPA_DISABLED',
|
||||
CLICKED_FORWARD_WITH_POLICY_AND_VEHICLES: 'CLICKED_FORWARD_WITH_POLICY_AND_VEHICLES',
|
||||
|
||||
// Review Page
|
||||
CLICKED_CUSTOMER_EDIT: 'CLICKED_CUSTOMER_EDIT',
|
||||
CLICKED_DAMAGE_EDIT: 'CLICKED_DAMAGE_EDIT',
|
||||
CLICKED_SCHEDULE_EDIT: 'CLICKED_SCHEDULE_EDIT',
|
||||
CLICKED_SERVICE_LOCATION_EDIT: 'CLICKED_SERVICE_LOCATION_EDIT',
|
||||
CLICKED_SERVICE_PACKAGE_EDIT: 'CLICKED_SERVICE_PACKAGE_EDIT',
|
||||
CLICKED_VEHICLE_EDIT: 'CLICKED_VEHICLE_EDIT',
|
||||
|
||||
// Bailout
|
||||
CLICKED_FORWARD_WITH_BAILOUT: 'CLICKED_FORWARD_WITH_BAILOUT'
|
||||
});
|
||||
|
|
|
|||
|
|
@ -609,6 +609,30 @@ const routingTable = () => [
|
|||
{
|
||||
scenario: navigationScenarios.CLICKED_FORWARD,
|
||||
destinationIssPageValue: issPageValues.PAYMENT_PAGE
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_CUSTOMER_EDIT,
|
||||
destinationIssPageValue: issPageValues.CONTACT_DETAILS
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_DAMAGE_EDIT,
|
||||
destinationIssPageValue: issPageValues.VEHICLE_DAMAGE
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_SCHEDULE_EDIT,
|
||||
destinationIssPageValue: issPageValues.SCHEDULE_PAGE
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_SERVICE_LOCATION_EDIT,
|
||||
destinationIssPageValue: issPageValues.SERVICE_LOCATION
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_SERVICE_PACKAGE_EDIT,
|
||||
destinationIssPageValue: issPageValues.SERVICE_PACKAGES
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_VEHICLE_EDIT,
|
||||
destinationIssPageValue: issPageValues.VEHICLE_SELECTION
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -11,13 +11,28 @@ import applicationConfig from '@/constants/application-config';
|
|||
import issPageValues from '@/router/router-constants/issPage-values';
|
||||
import damageLocationsSelected from '@/constants/damage-locations-selected';
|
||||
import coverageStatuses from '@/constants/coverage-statuses';
|
||||
import { deepEqual } from '@/helpers/object-helper';
|
||||
import { AppointmentTypeStrings, PREMIUM_FEE_PART_TYPE } from '@/constants/schedule-constants';
|
||||
import getDateDifferenceInDays from '@/helpers/date-helper';
|
||||
import endorsementOptions from '@/constants/endorsement-options';
|
||||
import { convertDateStringToDate, getDateDifferenceInDays, militaryToTwelveHourTime } from '@/helpers/date-helper';
|
||||
// import endorsementOptions from '@/constants/endorsement-options';
|
||||
|
||||
const storeId = 'main';
|
||||
|
||||
function getDisplayTextForDurationLength(durationMinimum, durationMaximum) {
|
||||
const isLongAppointment = durationMaximum >= 120;
|
||||
const isDurationRange = durationMinimum !== durationMaximum;
|
||||
|
||||
const adjustedMinimum = isLongAppointment ? durationMinimum / 60 : durationMinimum;
|
||||
const adjustedMaximum = isLongAppointment ? durationMaximum / 60 : durationMaximum;
|
||||
|
||||
const durationText = isDurationRange
|
||||
? `${adjustedMinimum} - ${adjustedMaximum}`
|
||||
: adjustedMinimum;
|
||||
|
||||
const unitText = isLongAppointment ? 'hours' : 'minutes';
|
||||
|
||||
return `${durationText} ${unitText}`;
|
||||
}
|
||||
|
||||
function getTimeSlotsAdditionalEventData(
|
||||
provisionalTriggers,
|
||||
zipCode,
|
||||
|
|
@ -175,6 +190,7 @@ const getDefaultState = () => ({
|
|||
issConfig: {
|
||||
clientName: 'Generic Insurance', // this is the default and will be overriden by the client's name
|
||||
clientDisplayName: 'Generic Insurance', // this is the default and will be overridden by the client's name or client display name.
|
||||
clientHeader: {},
|
||||
styleSheet: '', // Stylesheet used by the client.
|
||||
parentAccountNumber: 0, // Parent account number used by the client.
|
||||
isCoverageEnabled: false, // Indicates if we should be calling coverage on this flow.
|
||||
|
|
@ -202,7 +218,7 @@ export const useMainStore = defineStore({
|
|||
lineItems: (state) => state.order.lineItems,
|
||||
payment: (state) => state.order.payment,
|
||||
policy: (state) => state.order.policy,
|
||||
hasAnyNonWindshieldGlassParts: (state) => !state.order.policy.isDamageGlassOnly,
|
||||
hasAnyNonWindshieldGlassParts: (s) => !s.order.policy.isDamageGlassOnly,
|
||||
isMobileAppointment: (state) => state.order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE
|
||||
|| state.order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP,
|
||||
isDropOffAppointment: (state) => state.order.serviceLocation.appointmentType === AppointmentTypeStrings.DROP_OFF,
|
||||
|
|
@ -251,6 +267,30 @@ export const useMainStore = defineStore({
|
|||
requestTextUpdates: s.order.contactInfo.requestTextUpdates ?? false,
|
||||
notesForTechnician: s.order.contactInfo.notesForTechnician
|
||||
}),
|
||||
scheduleDisplayText: (s) => {
|
||||
const dateModel = convertDateStringToDate(s.order.schedule.date);
|
||||
const readableDate = dateModel?.toLocaleDateString('en-us', {
|
||||
weekday: 'long',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
year: 'numeric'
|
||||
});
|
||||
|
||||
const readableStartTime = militaryToTwelveHourTime(s.order.schedule.startTime);
|
||||
const readableEndTime = militaryToTwelveHourTime(s.order.schedule.endTime);
|
||||
|
||||
const readableDuration = getDisplayTextForDurationLength(
|
||||
s.order.schedule.jobMinMinutes,
|
||||
s.order.schedule.jobMaxMinutes
|
||||
);
|
||||
|
||||
return {
|
||||
date: readableDate,
|
||||
startTime: readableStartTime,
|
||||
endTime: readableEndTime,
|
||||
duration: readableDuration
|
||||
};
|
||||
},
|
||||
experimentOrder: (state) => ({
|
||||
issVehicleYear: state.order.vehicle.year,
|
||||
issVehicleMake: state.order.vehicle.make,
|
||||
|
|
@ -546,6 +586,7 @@ export const useMainStore = defineStore({
|
|||
manualGlassNames: manualGlassNamesArray,
|
||||
policyState: this.order.customer.address.state,
|
||||
status: this.order.policy.status,
|
||||
originalDeductible: this.order.originalDeductible,
|
||||
currentDeductible: this.order.currentDeductible,
|
||||
noCoverage: this.order.policy.noCoverage,
|
||||
isRepair: this.order.damage.isRepair,
|
||||
|
|
@ -899,8 +940,7 @@ export const useMainStore = defineStore({
|
|||
}
|
||||
const ctuToUse = this.order.serviceLocation.zipCodeCtu;
|
||||
const availableLineItemsFormattedForRequest = getLineItemQueryStringForPricing(availableLineItems);
|
||||
const { policy } = this.order;
|
||||
const deductibleToUse = this.damage.isRepair ? policy.deductible.repair : policy.deductible.replace;
|
||||
const deductibleToUse = this.order.currentDeductible;
|
||||
|
||||
let queryString =
|
||||
`ParentAccountNumber=${this.order.accountNumber}`
|
||||
|
|
@ -920,7 +960,7 @@ export const useMainStore = defineStore({
|
|||
endpoint: `${endpoints.GetInsurancePriceOrderItems.url}?${queryString}`
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
return [];
|
||||
throw error;
|
||||
});
|
||||
|
||||
if (response.data?.lineItems) {
|
||||
|
|
@ -1400,8 +1440,8 @@ export const useMainStore = defineStore({
|
|||
this.order.policy.endorsements = vehicle?.endorsements;
|
||||
|
||||
// TODO logic should be more complicated later on
|
||||
this.order.originalDeductible = parseFloat(vehicle.deductible);
|
||||
this.order.currentDeductible = parseFloat(vehicle.deductible);
|
||||
this.order.originalDeductible = vehicle.deductible;
|
||||
this.order.currentDeductible = vehicle.deductible;
|
||||
|
||||
this.resetSupportingItemsState();
|
||||
this.resetVapsState();
|
||||
|
|
@ -1467,6 +1507,7 @@ export const useMainStore = defineStore({
|
|||
resetISSConfigState() {
|
||||
this.issConfig.clientName = 'Generic Insurance';
|
||||
this.issConfig.clientDisplayName = 'Generic Insurance';
|
||||
this.issConfig.clientHeader = {};
|
||||
this.issConfig.parentAccountNumber = 0;
|
||||
this.issConfig.styleSheet = '';
|
||||
this.issConfig.isCoverageEnabled = false;
|
||||
|
|
@ -1592,11 +1633,9 @@ export const useMainStore = defineStore({
|
|||
updateIsSafeliteProvider(isSafelite) {
|
||||
this.order.serviceLocation.IsSafeliteProvider = isSafelite;
|
||||
},
|
||||
|
||||
updateDeductible(finalDeductible) {
|
||||
this.order.currentDeductible = finalDeductible;
|
||||
},
|
||||
|
||||
savePartQuestionAnswers(partQuestionAnswersArray) {
|
||||
// if part question answers have changed, reset subsequent question answers
|
||||
const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(this.order.damage.partQuestionAnswers, 'result');
|
||||
|
|
@ -1639,7 +1678,6 @@ export const useMainStore = defineStore({
|
|||
// Save new values
|
||||
this.updateMoldingQuestionAnswers(moldingQuestionAnswersArray);
|
||||
},
|
||||
|
||||
saveCapabilityQuestionAnswers(capabilityQuestionAnswersArray) {
|
||||
const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(this.order.damage.capabilityQuestionAnswers, 'result');
|
||||
const sortedCapabilityQuestionAnswersArray = sortArrayOfObjectsByPropertyValue(capabilityQuestionAnswersArray, 'result');
|
||||
|
|
@ -1657,16 +1695,9 @@ export const useMainStore = defineStore({
|
|||
this.updateCapabilityQuestionAnswers(capabilityQuestionAnswersArray);
|
||||
},
|
||||
saveGlassParts(glassParts) {
|
||||
if (!deepEqual(glassParts, this.order.lineItems.glassParts)) {
|
||||
this.resetServiceLocationAndDependencies();
|
||||
}
|
||||
this.order.lineItems.glassParts = glassParts;
|
||||
},
|
||||
saveSupportingItems(supportingItems) {
|
||||
if (!deepEqual(supportingItems, this.order.lineItems.supportingItems)) {
|
||||
this.resetServiceLocationAndDependencies();
|
||||
}
|
||||
|
||||
this.order.lineItems.supportingItems = supportingItems;
|
||||
},
|
||||
saveSupportingItemsSuppressingStateResetting(supportingItems) {
|
||||
|
|
|
|||
|
|
@ -1758,6 +1758,7 @@ describe('Store', () => {
|
|||
const policyState = getRandomString(2, 2);
|
||||
const status = getRandomString(6, 8);
|
||||
const currentDeductible = getRandomInt(0, 5000);
|
||||
const originalDeductible = getRandomInt(0, 5000);
|
||||
const noCoverage = getRandomBoolean();
|
||||
const isRepair = getRandomBoolean();
|
||||
const policyNumber = getRandomString(10, 20);
|
||||
|
|
@ -1772,6 +1773,7 @@ describe('Store', () => {
|
|||
store.order.referralCorrelationId = referralCorrelationId;
|
||||
store.issConfig.parentAccountNumber = accountNumber;
|
||||
store.order.currentDeductible = currentDeductible;
|
||||
store.order.originalDeductible = originalDeductible;
|
||||
store.order.policy.noCoverage = noCoverage;
|
||||
store.order.policy.status = status;
|
||||
store.order.customer.address.state = policyState;
|
||||
|
|
@ -1803,6 +1805,7 @@ describe('Store', () => {
|
|||
policyState,
|
||||
status,
|
||||
currentDeductible,
|
||||
originalDeductible,
|
||||
noCoverage,
|
||||
isRepair,
|
||||
policyNumber,
|
||||
|
|
@ -1837,6 +1840,7 @@ describe('Store', () => {
|
|||
const policyState = getRandomString(2, 2);
|
||||
const status = getRandomString(6, 8);
|
||||
const currentDeductible = getRandomInt(0, 5000);
|
||||
const originalDeductible = getRandomInt(0, 5000);
|
||||
const noCoverage = getRandomBoolean();
|
||||
const isRepair = getRandomBoolean();
|
||||
const endorsementAnswers = [
|
||||
|
|
@ -1871,6 +1875,7 @@ describe('Store', () => {
|
|||
store.order.referralCorrelationId = referralCorrelationId;
|
||||
store.issConfig.parentAccountNumber = accountNumber;
|
||||
store.order.currentDeductible = currentDeductible;
|
||||
store.order.originalDeductible = originalDeductible;
|
||||
store.order.policy.noCoverage = noCoverage;
|
||||
store.order.policy.status = status;
|
||||
store.order.customer.address.state = policyState;
|
||||
|
|
@ -1904,6 +1909,7 @@ describe('Store', () => {
|
|||
policyState,
|
||||
status,
|
||||
currentDeductible,
|
||||
originalDeductible,
|
||||
noCoverage,
|
||||
isRepair,
|
||||
policyNumber,
|
||||
|
|
|
|||
|
|
@ -27,17 +27,18 @@ $limu-button-color: #A9E3E9;
|
|||
}
|
||||
|
||||
.btn {
|
||||
&.btn-override[aria-disabled="false"]{
|
||||
&.btn-override[aria-disabled="false"]:not(.form-test-invalid) {
|
||||
&.btn-primary {
|
||||
background: $limu-button-color !important;
|
||||
color: $limu-button-text-color !important;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
background: $limu-button-color !important;
|
||||
color: $limu-button-text-color !important;
|
||||
box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $limu-button-color !important;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue