Merge branch 'develop' into feature/SSR-795

This commit is contained in:
Katie Kroell 2024-01-24 14:46:58 -05:00
commit e2e9efb471
33 changed files with 1129 additions and 1516 deletions

View file

@ -0,0 +1,71 @@
import damageLocationsSelected from '@/constants/damage-locations-selected.js';
function getLocationAnswer(damageLocation, locationAnswers) {
return locationAnswers?.find((answer) => answer.Name === damageLocation);
}
function getGlassPieces(damageLocation, glassToReplace) {
return glassToReplace?.filter((item) => item.glassLocation === damageLocation);
}
function getWindshieldCopy(answerContent, glassToReplace, isRepair) {
const windshieldPieces = getGlassPieces(damageLocationsSelected.WINDSHIELD, glassToReplace);
return isRepair || !!windshieldPieces?.length ? [answerContent?.Text] : [];
}
function getRearCopy(answerContent, glassToReplace) {
const rearPieces = getGlassPieces(damageLocationsSelected.REAR, glassToReplace);
return rearPieces?.length ? [answerContent?.Text] : [];
}
function generateBulletedListFromAnswers(answers) {
const items = answers.map((answer) => `<li>${answer.Text}</li>`);
return `<ul>${items.join('')}</ul>`;
}
function getSideCopy(location, locationAnswers, damageAnswers, glassToReplace) {
const sideItems = getGlassPieces(location, glassToReplace);
const damageAnswersOnOrder = damageAnswers?.filter((answer) =>
sideItems?.some((glassPiece) => answer.Name === glassPiece.glassName));
return sideItems?.length
? [
locationAnswers?.Text,
generateBulletedListFromAnswers(damageAnswersOnOrder)
]
: [];
}
function getDamageDisplayContent(
locationAnswers,
driverSideDamageAnswers,
passengerSideDamageAnswers,
glassToReplace,
isRepair
) {
return [
...getWindshieldCopy(
getLocationAnswer(damageLocationsSelected.WINDSHIELD, locationAnswers),
glassToReplace,
isRepair
),
...getSideCopy(
damageLocationsSelected.DRIVER,
getLocationAnswer(damageLocationsSelected.DRIVER, locationAnswers),
driverSideDamageAnswers,
glassToReplace
),
...getSideCopy(
damageLocationsSelected.PASSENGER,
getLocationAnswer(damageLocationsSelected.PASSENGER, locationAnswers),
passengerSideDamageAnswers,
glassToReplace
),
...getRearCopy(
getLocationAnswer(damageLocationsSelected.REAR, locationAnswers),
glassToReplace
)
];
}
export { getDamageDisplayContent, getLocationAnswer };

View file

@ -0,0 +1,411 @@
import { getDamageDisplayContent, getLocationAnswer } from '@/helpers/damage-review-content-generator.js';
import damageLocationsSelected from '@/constants/damage-locations-selected';
const testConstants = {
cmsConstants: {
widgetNames: {
locations: 'DamageLocationsWidget',
driverDamages: 'DriverDamagesWidget',
passengerDamages: 'PassengerDamagesWidget'
},
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'
},
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
}
}
}
};
const cmsContent = {
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-content-generator', () => {
describe('getLocationAnswer', () => {
test('answer with damage location name match', () => {
// Arrange
const damageLocation = 'matching name';
const expected = { Name: damageLocation };
const locationAnswers = [{ Name: 'non matching name' }, expected];
// Act
const result = getLocationAnswer(damageLocation, locationAnswers);
// Assert
expect(result).toEqual(expected);
});
test('no damage location name match', () => {
// Arrange
const damageLocation = 'random string';
const locationAnswers = [{ Name: 'non matching name' }];
// Act
const result = getLocationAnswer(damageLocation, locationAnswers);
// Assert
expect(result).toBe(undefined);
});
test.each([
[[]],
[null],
[undefined]
])('location answers %p', (locationAnswers) => {
// Arrange
const damageLocation = 'some string';
// Act
const result = getLocationAnswer(damageLocation, locationAnswers);
// Assert
expect(result).toBe(undefined);
});
});
describe('Correctly assembles damage info into a display string', () => {
const locationAnswers = cmsContent.DamageLocationsWidget.Answers;
test('Shows windshield copy when windshield damage is included', async () => {
// Arrange
const glassToReplace = [testConstants.glassItems.windshield];
const isRepair = false;
// Act
const result = getDamageDisplayContent(locationAnswers, null, null, glassToReplace, isRepair);
// Assert
expect(result).toStrictEqual([
testConstants.cmsConstants.locationCopy.windshield
]);
});
test('Windshield copy is shown when order is a repair', async () => {
// Arrange
const glassToReplace = [];
const isRepair = true;
// Act
const result = getDamageDisplayContent(locationAnswers, null, null, glassToReplace, isRepair);
// Assert
expect(result).toStrictEqual([
testConstants.cmsConstants.locationCopy.windshield
]);
});
test('Rear windshield copy shows when rear damage is present', async () => {
// Arrange
const glassToReplace = [testConstants.glassItems.rear];
const isRepair = false;
// Act
const result = getDamageDisplayContent(locationAnswers, null, null, glassToReplace, isRepair);
// Assert
expect(result).toStrictEqual([
testConstants.cmsConstants.locationCopy.rear
]);
});
test('Driver side copy and items are shown when driver side damage is present', async () => {
// Arrange
const driverSideDamageAnswers = cmsContent.DriverDamagesWidget.Answers;
const glassToReplace = [
testConstants.glassItems.driverItems.back,
testConstants.glassItems.driverItems.front
];
const isRepair = false;
const expectedList = '<ul>'
+ `<li>${testConstants.cmsConstants.damageCopy.front}</li>`
+ `<li>${testConstants.cmsConstants.damageCopy.back}</li>`
+ '</ul>';
// Act
const result = getDamageDisplayContent(
locationAnswers,
driverSideDamageAnswers,
null,
glassToReplace,
isRepair
);
// Assert
expect(result).toStrictEqual([
testConstants.cmsConstants.locationCopy.driver,
expectedList
]);
});
test('Passenger side copy and items are shown when passenger side damage is present', async () => {
// Arrange
const passengerSideDamageAnswers = cmsContent.PassengerDamagesWidget.Answers;
const glassToReplace = [
testConstants.glassItems.passengerItems.quarter,
testConstants.glassItems.passengerItems.vent,
testConstants.glassItems.passengerItems.side
];
const isRepair = false;
const expectedList = '<ul>'
+ `<li>${testConstants.cmsConstants.damageCopy.vent}</li>`
+ `<li>${testConstants.cmsConstants.damageCopy.quarter}</li>`
+ `<li>${testConstants.cmsConstants.damageCopy.side}</li>`
+ '</ul>';
// Act
const result = getDamageDisplayContent(
locationAnswers,
null,
passengerSideDamageAnswers,
glassToReplace,
isRepair
);
// Assert
expect(result).toStrictEqual([
testConstants.cmsConstants.locationCopy.passenger,
expectedList
]);
});
test('All relevant sections are shown in order in multiglass scenario', async () => {
// Arrange
const driverSideDamageAnswers = cmsContent.DriverDamagesWidget.Answers;
const passengerSideDamageAnswers = cmsContent.PassengerDamagesWidget.Answers;
const 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
];
const isRepair = false;
const expectedDriverDamageList = '<ul>'
+ `<li>${testConstants.cmsConstants.damageCopy.vent}</li>`
+ `<li>${testConstants.cmsConstants.damageCopy.front}</li>`
+ `<li>${testConstants.cmsConstants.damageCopy.back}</li>`
+ '</ul>';
const expectedPassengerDamageList = '<ul>'
+ `<li>${testConstants.cmsConstants.damageCopy.back}</li>`
+ `<li>${testConstants.cmsConstants.damageCopy.quarter}</li>`
+ `<li>${testConstants.cmsConstants.damageCopy.side}</li>`
+ '</ul>';
// Act
const result = getDamageDisplayContent(
locationAnswers,
driverSideDamageAnswers,
passengerSideDamageAnswers,
glassToReplace,
isRepair
);
// Assert
expect(result).toStrictEqual([
testConstants.cmsConstants.locationCopy.windshield,
testConstants.cmsConstants.locationCopy.driver,
expectedDriverDamageList,
testConstants.cmsConstants.locationCopy.passenger,
expectedPassengerDamageList,
testConstants.cmsConstants.locationCopy.rear
]);
});
});
});

View file

@ -0,0 +1,188 @@
<template>
<Form
ref="theForm"
v-slot="{ meta }"
@submit="onSubmit"
@invalidSubmit="onInvalidSubmit">
<div class="page-container-grouped-styles">
<siteHeader
ref="siteHeader"
cmsWidgetName="SiteHeaderWidget" />
<vehicleBanner
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="false" />
<siteSubHeader
class="mb-5 mt-4 hello"
cmsWidgetName="SiteSubHeaderWidget" />
<div class="main-content-container">
<hr class="my-0" />
<reviewDropdown ref="reviewDropdown" />
<hr class="my-0" />
<div>Cart Placeholder</div>
<hr class="my-5" />
<div>Pia Alert Placeholder</div>
<div>Payment Method Question</div>
<div>Pia Disabled Placeholder</div>
<siteFooter
ref="siteFooter"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid"
:isBackButtonHidden="shouldHideBackButton"
buttonSize
@backClicked="navigateBack"
@ForwardClicked="forwardButtonAction" />
</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 siteSubHeader from '@/iss-components/site-sub-header/site-sub-header.vue';
import reviewDropdown from '@/layouts/payment-method/review-dropdown/review-dropdown.vue';
// Supporting Items
import settleAllPromises from '@/helpers/layout-helper';
import { useMainStore } from '@/store';
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import { Form, defineRule } from 'vee-validate';
import { required } from '@/helpers/validation-rules';
import errorMessages from '@/constants/error-messages';
import { AppointmentTypeStrings } from '@/constants/schedule-constants';
defineRule('option-required', required(errorMessages.OPTION_REQUIRED));
export default {
name: 'payment-method',
components: {
// eslint-disable-next-line vue/no-reserved-component-names
Form,
siteHeader,
siteSubHeader,
siteFooter,
reviewDropdown
},
async beforeRouteEnter(to, from, next) {
// Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
const reviewDropdownPromise = reviewDropdown.methods.loadInitialData();
const promiseResultMap = [
{
resultKey: 'cmsContent',
promise: cmsContentPromise
},
{
resultKey: 'reviewDropdownData',
promise: reviewDropdownPromise
}
];
const resultMap = await settleAllPromises(promiseResultMap);
next((vm) => {
vm.setCmsContent(resultMap.cmsContent);
vm.$refs.reviewDropdown.initializeComponent(resultMap.reviewDropdownData);
});
},
data() {
return { };
},
computed: {
damageInfo() {
return useMainStore().order.damage;
}
},
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;
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
);
},
async forwardButtonAction() {
// Multiple paths based on payment
console.log('forward button hit...');
}
}
};
</script>
<style lang="scss" scoped>
$page-side-padding: 1.5rem;
.page-container-grouped-styles {
overflow: auto;
.main-content-container {
padding: 0 1.5rem !important;
}
}
</style>

View file

@ -6,19 +6,6 @@
: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"
@ -31,20 +18,16 @@
<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
textBlock
},
props: {
headerCmsWidgetName: String,
customHeaderText: String,
// Specifically an array of strings.
content: Array,
editScreenReaderTextCmsWidgetName: String
content: Array // Specifically an array of strings.
},
emits: ['edit-clicked'],
data() {

View file

@ -0,0 +1,175 @@
<template>
<div>
<div
class="row review-toggle flex align-items-center pt-4"
:class="[isExpanded ? 'expanded' : '']"
@click="toggleIsExpanded">
<a
aria-label="expand appointment details"
href="javascript:void(0)"
class="col d-flex justify-content-between py-0">
<span class="label">Appointment details</span>
</a>
</div>
<div class="review-table px-4">
<vehicleReview
cmsWidgetName="VehicleReviewWidget"
:vehicle="vehicleInfo" />
<hr class="my-0" />
<damageReview
cmsWidgetName="DamageReviewWidget"
damageLocationsWidgetName="DamageLocationsWidget"
:damage="damageInfo" />
<hr class="my-0" />
<servicePackageReview
ref="servicePackageReview"
servicePackageOptionsCmsName="ServicePackageTitle"
defaultPackageItemsCmsName="DefaultPackageItemDescriptions"
vapsItemsCmsName="VapsItemDescriptions"
:damage="damageInfo"
:lineItems="lineItems" />
<hr class="my-0" />
<serviceLocationReview
cmsWidgetName="ServiceLocationTitleWidget"
:serviceLocation="serviceLocationInfo" />
<hr class="my-0" />
<scheduleReview
cmsWidgetName="ScheduleWidget"
:appointmentType="appointmentType" />
<hr class="my-0" />
<customerReview
cmsWidgetName="CustomerReviewWidget"
:customer="customerInfo" />
</div>
</div>
</template>
<script>
// import textBlock from '@/digital-components/text-block/text-block.vue';
import customerReview from '@/layouts/payment-method/review-dropdown/review-sections/customer-review/customer-review.vue';
import damageReview from '@/layouts/payment-method/review-dropdown/review-sections/damage-review/damage-review.vue';
import scheduleReview from '@/layouts/payment-method/review-dropdown/review-sections/schedule-review/schedule-review.vue';
import
serviceLocationReview from '@/layouts/payment-method/review-dropdown/review-sections/service-location-review/service-location-review.vue';
import
servicePackageReview
from '@/layouts/payment-method/review-dropdown/review-sections/service-package-review/service-package-review.vue';
import vehicleReview from '@/layouts/payment-method/review-dropdown/review-sections/vehicle-review/vehicle-review.vue';
import { useMainStore } from '@/store';
import settleAllPromises from '@/helpers/layout-helper';
export default {
name: 'review-dropdown',
components: {
customerReview,
damageReview,
scheduleReview,
serviceLocationReview,
servicePackageReview,
vehicleReview
},
props: {},
data() {
return {
isExpanded: false
};
},
computed: {
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: {
async loadInitialData() {
const servicePackageInitialDataPromise = servicePackageReview.methods.loadInitialData();
const promiseResultMap = [
{
resultKey: 'servicePackageData',
promise: servicePackageInitialDataPromise
}
];
return settleAllPromises(promiseResultMap);
},
initializeComponent(apiResponses) {
this.$refs.servicePackageReview.initializeComponent(apiResponses.servicePackageData);
},
toggleIsExpanded() {
this.isExpanded = !this.isExpanded;
}
}
};
</script>
<style lang="scss" scoped>
@import "@/styles/ux-variables-svg-strings.scss";
.dark-header {
color: $black;
line-height: 1.6;
}
.review-table {
max-height: 0;
transition: all 350ms ease-in;
overflow: hidden;
visibility: hidden;
}
.review-toggle {
margin-bottom: 1rem;
&.expanded {
margin-bottom: 0;
}
&:after {
content: "";
transition: all 0.5s ease;
background-image: url($svg-payment-method-review-toggle);
background-repeat: no-repeat;
background-position: right center;
width: 16px;
height: 9px;
display: inline-flex;
position: relative;
right: 0.75rem;
margin: 0.5rem 0 0.5rem 1rem;
cursor: pointer;
}
&.expanded:after {
transform: rotate(180deg);
}
&.expanded + .review-table {
max-height: 800px;
transition: all 150ms ease-in;
overflow: hidden;
visibility: visible;
}
a {
text-decoration: none;
}
.label {
color: $black;
font-weight: 500;
line-height: 1.625;
}
}
</style>

View file

@ -1,5 +1,5 @@
// Components
import customerReview from '@/layouts/review-page/review-sections/customer-review/customer-review.vue';
import customerReview from '@/layouts/payment-method/review-dropdown/review-sections/customer-review/customer-review.vue';
// Supporting Files
import { shallowMount } from '@vue/test-utils';

View file

@ -1,13 +1,11 @@
<template>
<reviewBlock
editScreenReaderTextCmsWidgetName="EditContactDetailsScreenReader"
:customHeaderText="header"
:content="displayContent"
@editClicked="editClicked" />
:content="displayContent" />
</template>
<script>
import reviewBlock from '@/layouts/review-page/review-block/review-block.vue';
import reviewBlock from '@/layouts/payment-method/review-dropdown/review-block/review-block.vue';
export default {
name: 'customer-review',
@ -18,7 +16,6 @@ export default {
cmsWidgetName: String,
customer: Object
},
emits: ['edit-clicked'],
data() {
return {};
},
@ -41,11 +38,6 @@ export default {
smsOptIn() {
return this.getCmsContent(this.cmsWidgetName, 'SubheaderText');
}
},
methods: {
editClicked() {
this.$emit('edit-clicked');
}
}
};
</script>

View file

@ -0,0 +1,78 @@
// Components
import damageReview from '@/layouts/payment-method/review-dropdown/review-sections/damage-review/damage-review.vue';
// Supporting Files
import { createTestingPinia } from '@pinia/testing';
import { useMainStore } from '@/store';
import { shallowMount } from '@vue/test-utils';
import { getMountOptions } from '@/helpers/unit-test-helper.js';
import { getDamageDisplayContent } from '@/helpers/damage-review-content-generator.js';
jest.mock('@/helpers/damage-review-content-generator.js', () => ({
getDamageDisplayContent: jest.fn(),
getLocationAnswer: jest.fn()
}));
let cmsContent;
const mockMixin = {
methods: {
getCmsContent: jest.fn((widgetName, cmsFieldName) => cmsContent?.[widgetName]?.[cmsFieldName] ?? '')
}
};
function getShallowMountedComponent(mainInitialState = {}, initialData = {}, methodToRun = () => {}) {
const mountOptions = getMountOptions({
router: {
navigate: jest.fn()
}
});
const testingPinia = createTestingPinia({
initialState: {
main: mainInitialState
}
});
useMainStore(testingPinia);
methodToRun();
mountOptions.global.plugins = [testingPinia];
mountOptions.data = () => (
initialData
);
mountOptions.mixins = [mockMixin];
const wrapper = shallowMount(damageReview, mountOptions);
return { wrapper };
}
beforeEach(() => {
getDamageDisplayContent.mockClear();
});
describe('Damage Review Block', () => {
test('computed displayContent calls getDamageDisplayContent with expected', () => {
// Arrange
const glassToReplace = ['front-window', 'side-window'];
const isRepair = false;
const initialStore = {
order: {
damage: {
glassToReplace,
isRepair
}
}
};
const expected = ['a', 'v', 'n'];
getDamageDisplayContent.mockImplementationOnce(() => expected);
const { wrapper } = getShallowMountedComponent(initialStore, {});
// Act
const result = wrapper.vm.displayContent;
// Assert
expect(result).toStrictEqual(expected);
expect(getDamageDisplayContent).toBeCalledTimes(1);
expect(getDamageDisplayContent).toBeCalledWith([], [], [], glassToReplace, isRepair);
});
});

View file

@ -0,0 +1,53 @@
<template>
<reviewBlock
:headerCmsWidgetName="cmsWidgetName"
:content="displayContent" />
</template>
<script>
import damageLocationsSelected from '@/constants/damage-locations-selected.js';
import { useMainStore } from '@/store';
import { getDamageDisplayContent, getLocationAnswer } from '@/helpers/damage-review-content-generator.js';
import reviewBlock from '@/layouts/payment-method/review-dropdown/review-block/review-block.vue';
export default {
name: 'damage-review',
components: {
reviewBlock
},
props: {
cmsWidgetName: String,
damageLocationsWidgetName: String,
damage: Object
},
computed: {
displayContent() {
const { glassToReplace, isRepair } = useMainStore().order.damage;
return getDamageDisplayContent(
this.locationAnswers,
this.driverSideDamageAnswers,
this.passengerSideDamageAnswers,
glassToReplace,
isRepair
);
},
driverSideDamageAnswers() {
const answerContent = getLocationAnswer(damageLocationsSelected.DRIVER, this.locationAnswers);
return this.getAnswersNullSafe(answerContent?.SubWidgetName);
},
passengerSideDamageAnswers() {
const answerContent = getLocationAnswer(damageLocationsSelected.PASSENGER, this.locationAnswers);
return this.getAnswersNullSafe(answerContent?.SubWidgetName);
},
locationAnswers() {
return this.getAnswersNullSafe(this.damageLocationsWidgetName);
}
},
methods: {
getAnswersNullSafe(widgetName) {
const rawAnswers = this.getCmsContent(widgetName, 'Answers');
return rawAnswers || [];
}
}
};
</script>

View file

@ -1,13 +1,11 @@
<template>
<reviewBlock
editScreenReaderTextCmsWidgetName="EditAppointmentScreenReader"
:customHeaderText="headerText"
:content="displayContent"
@editClicked="editClicked" />
:content="displayContent" />
</template>
<script>
import reviewBlock from '@/layouts/review-page/review-block/review-block.vue';
import reviewBlock from '@/layouts/payment-method/review-dropdown/review-block/review-block.vue';
export default {
name: 'schedule-review',
@ -18,7 +16,6 @@ export default {
cmsWidgetName: String,
appointmentType: String
},
emits: ['edit-clicked'],
data() {
return {};
},
@ -32,11 +29,6 @@ export default {
headerText() {
return this.getCmsContent(this.cmsWidgetName, 'HeaderText');
}
},
methods: {
editClicked() {
this.$emit('edit-clicked');
}
}
};
</script>

View file

@ -1,5 +1,6 @@
// Components
import serviceLocationReview from '@/layouts/review-page/review-sections/service-location-review/service-location-review.vue';
import
serviceLocationReview from '@/layouts/payment-method/review-dropdown/review-sections/service-location-review/service-location-review.vue';
// Supporting Files
import { shallowMount } from '@vue/test-utils';

View file

@ -1,14 +1,12 @@
<template>
<reviewBlock
editScreenReaderTextCmsWidgetName="EditLocationScreenReader"
:headerCmsWidgetName="cmsWidgetName"
:content="displayContent"
@editClicked="editClicked" />
:content="displayContent" />
</template>
<script>
import { AppointmentTypeStrings } from '@/constants/schedule-constants';
import reviewBlock from '@/layouts/review-page/review-block/review-block.vue';
import reviewBlock from '@/layouts/payment-method/review-dropdown/review-block/review-block.vue';
export default {
name: 'service-location-review',
@ -19,7 +17,6 @@ export default {
cmsWidgetName: String,
serviceLocation: Object
},
emits: ['edit-clicked'],
data() {
return {};
},
@ -49,11 +46,6 @@ export default {
zipCode: this.serviceLocation?.provider?.address?.zipCode
};
}
},
methods: {
editClicked() {
this.$emit('edit-clicked');
}
}
};
</script>

View file

@ -1,5 +1,6 @@
// Components
import servicePackageReview from '@/layouts/review-page/review-sections/service-package-review/service-package-review.vue';
import
servicePackageReview from '@/layouts/payment-method/review-dropdown/review-sections/service-package-review/service-package-review.vue';
// Supporting Files
import { shallowMount } from '@vue/test-utils';

View file

@ -1,21 +1,18 @@
<template>
<reviewBlock
editScreenReaderTextCmsWidgetName="EditServiceTypeScreenReader"
:headerCmsWidgetName="packageNameWidget"
:content="displayContent"
@editClicked="editClicked" />
:content="displayContent" />
</template>
<script>
// import settleAllPromises from '@/helpers/layout-helper';
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 { useMainStore } from '@/store';
import reviewBlock from '@/layouts/payment-method/review-dropdown/review-block/review-block.vue';
import {
getHighestFullySatisfiedTier,
containsLineItemWithPartType
} from '@/helpers/service-package-helper.js';
// import { storeActions } from '@/constants/store-actions.js';
export default {
name: 'service-package-review',
@ -101,19 +98,8 @@ export default {
},
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 wipersPromise = useMainStore().getWipers();
const rainDefensePromise = useMainStore().getRainDefense();
const promiseResultMap = [
{
@ -126,15 +112,12 @@ export default {
}
];
return settleAllPromises(promiseResultMap); */
return settleAllPromises(promiseResultMap);
},
initializeComponent(apiResponses) {
const vapsFromApi = [...apiResponses.wipers, apiResponses.rainDefense];
this.availableVaps = vapsFromApi;
},
editClicked() {
this.$emit('edit-clicked');
}
}
};

View file

@ -1,5 +1,5 @@
// Components
import vehicleReview from '@/layouts/review-page/review-sections/vehicle-review/vehicle-review.vue';
import vehicleReview from '@/layouts/payment-method/review-dropdown/review-sections/vehicle-review/vehicle-review.vue';
// Supporting Files
import { shallowMount } from '@vue/test-utils';

View file

@ -1,13 +1,11 @@
<template>
<reviewBlock
editScreenReaderTextCmsWidgetName="EditVehicleScreenReader"
:headerCmsWidgetName="cmsWidgetName"
:content="displayContent"
@editClicked="editClicked" />
:content="displayContent" />
</template>
<script>
import reviewBlock from '@/layouts/review-page/review-block/review-block.vue';
import reviewBlock from '@/layouts/payment-method/review-dropdown/review-block/review-block.vue';
export default {
name: 'vehicle-review',
@ -18,7 +16,6 @@ export default {
cmsWidgetName: String,
vehicle: Object
},
emits: ['edit-clicked'],
data() {
return {};
},
@ -26,11 +23,6 @@ export default {
displayContent() {
return [`${this.vehicle.year} ${this.vehicle.make} ${this.vehicle.model}`];
}
},
methods: {
editClicked() {
this.$emit('edit-clicked');
}
}
};
</script>

View file

@ -1,44 +0,0 @@
// 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);
});
});

View file

@ -1,386 +0,0 @@
// 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);
});
});
});
});

View file

@ -1,320 +0,0 @@
<template>
<Form
ref="theForm"
@submit="onSubmit"
@invalidSubmit="onInvalidSubmit">
<!-- When customer-details is added: v-slot="{ meta }" -->
<div class="page-container-grouped-styles">
<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="navigateBack" />
</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 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
},
mixins: [BaseFormMixin],
async beforeRouteEnter(to, from, next) {
// Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
const promiseResultMap = [
{
resultKey: 'cmsContent',
promise: cmsContentPromise
}];
const resultMap = await settleAllPromises(promiseResultMap);
next((vm) => {
vm.setCmsContent(resultMap.cmsContent);
});
},
setup() {
const mainStore = useMainStore();
return { mainStore };
},
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
);
},
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.navigateWithoutSaving(
navigationScenarios.CLICKED_FORWARD,
this.$route
);
}
}
};
</script>
<style lang="scss" scoped>
$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>

View file

@ -1,436 +0,0 @@
// 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
]);
});
});
});

View file

@ -1,134 +0,0 @@
<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>

View file

@ -9,7 +9,7 @@
<siteSubHeader
cmsWidgetName="ScheduleSubHeaderWidget"
subTextClasses="text-center small sub-text"
class="mt-5" />
class="mt-4" />
<template v-if="ChangeShopLink.length">
<textBlock
cmsWidgetName="ChangeShopLink"

View file

@ -66,8 +66,8 @@ import allGlassPartsAndItemsHavePrices from '@/layouts/service-packages/service-
import globalRules from '@/constants/global-rules';
import servicePackageQuestion from '@/layouts/service-packages/service-package-question/service-package-question.vue';
import issPageValues from '@/router/router-constants/issPage-values';
import bailoutCode from "@/constants/bailoutCode";
import bailoutMessage from "@/constants/bailoutMessage";
import bailoutCode from '@/constants/bailoutCode';
import bailoutMessage from '@/constants/bailoutMessage';
const store = useMainStore();

View file

@ -354,7 +354,7 @@ export default {
addressLine2 += zipCode;
}
const addressLine1 = this.toTitleCase(provider?.address?.streetAddress);
const addressLine1 = toTitleCase(provider?.address?.streetAddress);
const joinString = addressLine1.length > 0 && addressLine2.length > 0 ? ', ' : '';
return [addressLine1, addressLine2].join(joinString);
},
@ -376,6 +376,9 @@ export default {
this.mapZipCode = this.zipCode;
},
forwardButtonAction() {
if (this.selectedProviderIsSafeliteShop) {
useMainStore().updateIsSafeliteProvider(true);
}
const scenario = this.selectedProviderIsSafeliteShop
? this.navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE_SHOP
: this.navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP;
@ -390,7 +393,7 @@ export default {
}
},
getShopButtonDataFromProvider(provider) {
const cellNumber = this.toDisplayPhoneNumber(provider?.phoneNumber);
const cellNumber = toDisplayPhoneNumber(provider?.phoneNumber);
const distance = provider?.distanceInMiles !== null && !Number.isNaN(parseFloat(provider?.distanceInMiles))
? +provider.distanceInMiles.toFixed(1)
: null;
@ -403,9 +406,7 @@ export default {
buttonBodyCopy: `${this.getProviderAddress(provider)}<br>${cellNumber ?? ''}`,
value: provider?.providerNumber ?? ''
};
},
toDisplayPhoneNumber,
toTitleCase
}
}
};
</script>

View file

@ -8,6 +8,7 @@ Object {
},
"sections": Array [],
"widget": Object {
"damageLocations": "DamageLocationsWidget",
"footer": "SiteFooterWidget",
"orderDetails": "OrderDetailsContent",
"serviceSummary": "ServiceSummaryContent",

View file

@ -21,8 +21,8 @@
<p
v-for="line in lines"
:key="line"
class="small review-block__body--line">
{{ line }}
class="small review-block__body--line"
v-html="line">
</p>
</div>
</template>

View file

@ -9,6 +9,8 @@ import { useMainStore } from '@/store';
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import settleAllPromises from '@/helpers/layout-helper.js';
import widgetFields from '@/constants/cms-widget-fields.js';
import { toTitleCase, formatAddress, toDisplayPhoneNumber } from '@/helpers/text-helper.js';
import { getDamageDisplayContent } from '@/helpers/damage-review-content-generator.js';
// Mock fetchCmsContentForPage
jest.mock('@/helpers/cms-content-helper', () => ({
@ -25,6 +27,11 @@ jest.mock('@/helpers/text-helper.js', () => ({
toTitleCase: jest.fn()
}));
jest.mock('@/helpers/damage-review-content-generator.js', () => ({
getDamageDisplayContent: jest.fn(),
getLocationAnswer: jest.fn()
}));
// Mock our module for promises.
jest.mock('@/helpers/layout-helper.js', () => jest.fn());
@ -57,6 +64,12 @@ function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRu
return { wrapper };
}
beforeEach(() => {
formatAddress.mockClear();
toDisplayPhoneNumber.mockClear();
getDamageDisplayContent.mockClear();
});
describe('tpa-submit', () => {
test('returns the initial data', () => {
// Arrange
@ -337,37 +350,25 @@ describe('tpa-submit', () => {
expect(vehicleSection.lines).toStrictEqual(expectedLines);
}
);
test.each([
['Apple sauce', 'Apple sauce'],
['', ''],
['', undefined],
['', null]
])(
'damage line is %p when store damage is %p',
async (damageLine, storeDamage) => {
// Arrange
const initialStore = {
order: {
policy: { damageCause: storeDamage }
}
};
const { wrapper } = getMountedComponent(initialStore);
const damageSectionIndex = 1;
const expectedLines = [damageLine];
test('damage section lines equal result from getDamageDisplayContent', async () => {
// Arrange
const { wrapper } = getMountedComponent();
const damageSectionIndex = 1;
const expectedLines = ['hi', 'potato', 'vehicle 3'];
getDamageDisplayContent.mockImplementationOnce(() => expectedLines);
// Act
await tpaSubmit.beforeRouteEnter.call(
wrapper.vm,
{ query: { issPage: 'tpa-submit' } },
undefined,
(c) => c(wrapper.vm)
);
// Act
await tpaSubmit.beforeRouteEnter.call(
wrapper.vm,
{ query: { issPage: 'tpa-submit' } },
undefined,
(c) => c(wrapper.vm)
);
// Assert
const damageSection = wrapper.vm.sections[damageSectionIndex];
expect(damageSection.lines).toStrictEqual(expectedLines);
}
);
// Assert
const damageSection = wrapper.vm.sections[damageSectionIndex];
expect(damageSection.lines).toEqual(expectedLines);
});
describe('preferred shop section', () => {
test('has three lines', async () => {
// Arrange
@ -386,15 +387,12 @@ describe('tpa-submit', () => {
const preferredShopSection = wrapper.vm.sections[preferredShopSectionIndex];
expect(preferredShopSection.lines.length).toBe(3);
});
test.each([
['some value', 'some value'],
['', ''],
['', null],
['', undefined]
])('first line is %p when company name is %p', async (line, companyName) => {
test('first line is value returned from toTitleCase method', async () => {
// Arrange
const initialData = { companyName };
const initialData = { companyName: 'some value' };
const { wrapper } = getMountedComponent({}, initialData);
const expectedName = 'some expected name';
toTitleCase.mockImplementationOnce(() => expectedName);
const preferredShopSectionIndex = 2;
// Act
@ -407,7 +405,7 @@ describe('tpa-submit', () => {
// Assert
const preferredShopSection = wrapper.vm.sections[preferredShopSectionIndex];
expect(preferredShopSection.lines[0]).toBe(line);
expect(preferredShopSection.lines[0]).toBe(expectedName);
});
test('second line is expected and formatAddress called', async () => {
// Arrange
@ -426,7 +424,7 @@ describe('tpa-submit', () => {
};
const { wrapper } = getMountedComponent(initialStore);
const line = 'some returned line';
wrapper.vm.formatAddress = jest.fn().mockImplementationOnce(() => line);
formatAddress.mockImplementationOnce(() => line);
const preferredShopSectionIndex = 2;
// Act
@ -440,8 +438,8 @@ describe('tpa-submit', () => {
// Assert
const preferredShopSection = wrapper.vm.sections[preferredShopSectionIndex];
expect(preferredShopSection.lines[1]).toBe(line);
expect(wrapper.vm.formatAddress).toHaveBeenCalledTimes(1);
expect(wrapper.vm.formatAddress).toHaveBeenCalledWith(
expect(formatAddress).toHaveBeenCalledTimes(1);
expect(formatAddress).toHaveBeenCalledWith(
address.streetAddress,
null,
address.city,
@ -461,7 +459,7 @@ describe('tpa-submit', () => {
};
const { wrapper } = getMountedComponent(initialStore);
const expectedLine = 'returned from to display phone num';
wrapper.vm.toDisplayPhoneNumber = jest.fn().mockImplementationOnce(() => expectedLine);
toDisplayPhoneNumber.mockImplementationOnce(() => expectedLine);
const preferredShopSectionIndex = 2;
// Act
@ -475,7 +473,7 @@ describe('tpa-submit', () => {
// Assert
const preferredShopSection = wrapper.vm.sections[preferredShopSectionIndex];
expect(preferredShopSection.lines[2]).toBe(expectedLine);
expect(wrapper.vm.toDisplayPhoneNumber).toHaveBeenCalledWith(phoneNumber);
expect(toDisplayPhoneNumber).toHaveBeenCalledWith(phoneNumber);
});
});
test('contact info section has expected content', async () => {
@ -498,7 +496,7 @@ describe('tpa-submit', () => {
const expectedLine1 = 'Jones Eddison';
const expectedLine2 = emailAddress;
const expectedLine3 = 'some value returned';
wrapper.vm.toDisplayPhoneNumber = jest.fn().mockImplementation((number) => (number === phoneNumber ? expectedLine3 : ''));
toDisplayPhoneNumber.mockImplementation((number) => (number === phoneNumber ? expectedLine3 : ''));
const contactInfoSectionIndex = 3;
// Act
@ -514,7 +512,7 @@ describe('tpa-submit', () => {
expect(contactInfoSection.lines[0]).toBe(expectedLine1);
expect(contactInfoSection.lines[1]).toBe(expectedLine2);
expect(contactInfoSection.lines[2]).toBe(expectedLine3);
expect(wrapper.vm.toDisplayPhoneNumber).toHaveBeenCalledWith(phoneNumber);
expect(toDisplayPhoneNumber).toHaveBeenCalledWith(phoneNumber);
});
});
describe('computed', () => {
@ -762,5 +760,22 @@ describe('tpa-submit', () => {
expect(result).toBe(null);
});
});
test.each([
[[1, 5, 6], [1, 5, 6]],
[[], []],
[undefined, []],
[null, []]
])('getAnswersNullSafe', (rawAnswers, expected) => {
// Arrange
const widgetName = 'WidgetName';
const { wrapper } = getMountedComponent();
wrapper.vm.getCmsContent = jest.fn().mockImplementationOnce(() => rawAnswers);
// Act
const result = wrapper.vm.getAnswersNullSafe(widgetName);
// Assert
expect(result).toEqual(expected);
});
});
});

View file

@ -78,7 +78,9 @@
:customText="orderDetailsBody"
:marginTopSizeOverride="4"
class="mb-4 px-4 small text-color--darker-gray" />
<deductibleBox ref="deductibleBox" :value="deductibleBoxValue" />
<deductibleBox
ref="deductibleBox"
:value="deductibleBoxValue" />
</div>
<siteFooter
ref="siteFooter"
@ -109,6 +111,8 @@ import BaseFormMixin from '@/mixins/base-form-mixin.js';
import widgetFields from '@/constants/cms-widget-fields.js';
import { useMainStore } from '@/store';
import { toTitleCase, toDisplayPhoneNumber, formatAddress, formatAmountInDollars } from '@/helpers/text-helper.js';
import { getDamageDisplayContent, getLocationAnswer } from '@/helpers/damage-review-content-generator.js';
import damageLocationsSelected from '@/constants/damage-locations-selected.js';
const VERIFYING_COVERAGE = 'Verifying coverage';
@ -148,6 +152,7 @@ export default {
shop: 'PreferredShopSubTitle',
contactInfo: 'ContactDetailsSubTitle'
},
damageLocations: 'DamageLocationsWidget',
orderDetails: 'OrderDetailsContent',
footer: 'SiteFooterWidget'
},
@ -166,7 +171,7 @@ export default {
},
subHeaderBodyTwo() {
const cmsContent = this.getCmsContent(this.widget.siteSubHeader, widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT_2);
return this.getStringWithCustomValues(cmsContent, this.customValueMap);
return getStringWithCustomValues(cmsContent, this.customValueMap);
},
serviceSummaryText() {
return this.getCmsContent(this.widget.serviceSummary, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
@ -176,7 +181,7 @@ export default {
},
orderDetailsBody() {
const orderDetailsBodyText = this.getCmsContent(this.widget.orderDetails, widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT);
return this.processIfStatements(orderDetailsBodyText, 'custom', this.getCustomValueFromString);
return processIfStatements(orderDetailsBodyText, 'custom', this.getCustomValueFromString);
},
forwardButtonText() {
return this.getCmsContent(this.widget.footer, widgetFields.FOOTER_WIDGET.FORWARD_BUTTON_TEXT);
@ -188,7 +193,7 @@ export default {
return useMainStore().order.currentDeductible;
},
deductibleBoxValue() {
return this.isVerified ? this.formatAmountInDollars(this.currentDeductible) : VERIFYING_COVERAGE;
return this.isVerified ? formatAmountInDollars(this.currentDeductible) : VERIFYING_COVERAGE;
},
getVehicleLines() {
const { year, make, model } = useMainStore().order.vehicle;
@ -197,30 +202,51 @@ export default {
.join(' ');
return [line];
},
locationAnswers() {
return this.getAnswersNullSafe(this.widget.damageLocations);
},
driverSideDamageAnswers() {
const answerContent = getLocationAnswer(damageLocationsSelected.DRIVER, this.locationAnswers);
return this.getAnswersNullSafe(answerContent?.SubWidgetName);
},
passengerSideDamageAnswers() {
const answerContent = getLocationAnswer(damageLocationsSelected.PASSENGER, this.locationAnswers);
return this.getAnswersNullSafe(answerContent?.SubWidgetName);
},
getDamageLines() {
return [useMainStore().order.policy.damageCause ?? ''];
const { glassToReplace, isRepair } = useMainStore().order.damage;
return getDamageDisplayContent(
this.locationAnswers,
this.driverSideDamageAnswers,
this.passengerSideDamageAnswers,
glassToReplace,
isRepair
);
},
getPreferredShopLines() {
const { phoneNumber, address } = useMainStore().order.serviceLocation.provider;
const { streetAddress, city, state, zipCode } = address;
const displayAddress = this.formatAddress(streetAddress, null, city, state, zipCode);
const displayPhoneNumber = this.toDisplayPhoneNumber(phoneNumber);
return [this.companyName ?? '', displayAddress, displayPhoneNumber];
const displayAddress = formatAddress(streetAddress, null, city, state, zipCode);
const displayPhoneNumber = toDisplayPhoneNumber(phoneNumber);
return [toTitleCase(this.companyName ?? ''), displayAddress, displayPhoneNumber];
},
getContactInfoLines() {
const { firstName, lastName, emailAddress, phoneNumber } = useMainStore().contactInfo;
return [
`${firstName} ${lastName}`,
emailAddress ?? '',
this.toDisplayPhoneNumber(phoneNumber)
toDisplayPhoneNumber(phoneNumber)
];
}
},
methods:
{
setSections() {
const vehicleScenario = useMainStore().isPolicyVehicle
? this.navigationScenarios.EDIT_POLICY_VEHICLE
: this.navigationScenarios.EDIT_VEHICLE;
this.sections = [
this.getSection(this.widget.subheader.vehicle, this.getVehicleLines, this.navigationScenarios.EDIT_VEHICLE),
this.getSection(this.widget.subheader.vehicle, this.getVehicleLines, vehicleScenario),
this.getSection(this.widget.subheader.damage, this.getDamageLines, this.navigationScenarios.EDIT_DAMAGE),
this.getSection(this.widget.subheader.shop, this.getPreferredShopLines, this.navigationScenarios.EDIT_PREFERRED_SHOP),
// eslint-disable-next-line max-len
@ -231,15 +257,12 @@ export default {
return {
title: this.getCmsContent(widgetName, widgetFields.TEXT_BLOCK_WIDGET.TEXT),
lines,
onClickEdit: this.getNavigateByScenarioMethod(scenario)
onClickEdit: () => this.navigate(scenario)
};
},
forwardButtonAction() {
this.navigate(this.navigationScenarios.CLICKED_FORWARD);
},
getNavigateByScenarioMethod(scenario) {
return () => this.navigate(scenario);
},
navigate(scenario) {
this.$router.navigate(scenario, this.$route);
},
@ -255,12 +278,10 @@ export default {
return null;
}
},
processIfStatements,
getStringWithCustomValues,
toDisplayPhoneNumber,
toTitleCase,
formatAddress,
formatAmountInDollars
getAnswersNullSafe(widgetName) {
const rawAnswers = this.getCmsContent(widgetName, 'Answers');
return rawAnswers || [];
}
}
};
</script>

View file

@ -16,11 +16,11 @@ const issPageValues = Object.freeze({
LICENSE_PLATE_LOOKUP: 'license-plate-lookup',
MOLDING_QUESTIONS: 'molding-questions',
ORDER_CONFIRMATION: 'order-confirmation',
PAYMENT_METHOD: 'payment-method',
PAYMENT_PAGE: 'payment-page',
PART_QUESTIONS: 'part-questions',
POLICY_HOLDER_DETAILS: 'policy-holder-details',
PROVIDER_PREFERENCE: 'provider-preference',
REVIEW_PAGE: 'review-page',
REVEAL: 'reveal',
SERVICE_LOCATION: 'service-location',
TPA_CONFIRMATION: 'tpa-confirmation',

View file

@ -69,6 +69,7 @@ const navigationScenarios = Object.freeze({
CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP: 'CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP',
// TPA Submit
EDIT_POLICY_VEHICLE: 'EDIT_POLICY_VEHICLE',
EDIT_VEHICLE: 'EDIT_VEHICLE',
EDIT_DAMAGE: 'EDIT_DAMAGE',
EDIT_PREFERRED_SHOP: 'EDIT_PREFERRED_SHOP',

View file

@ -599,12 +599,12 @@ const routingTable = () => [
},
{
scenario: navigationScenarios.CLICKED_FORWARD,
destinationIssPageValue: issPageValues.REVIEW_PAGE
destinationIssPageValue: issPageValues.PAYMENT_METHOD
}
]
},
{
issPageValue: issPageValues.REVIEW_PAGE,
issPageValue: issPageValues.PAYMENT_METHOD,
maps: [
{
scenario: navigationScenarios.CLICKED_BACK,
@ -612,31 +612,7 @@ 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
destinationIssPageValue: issPageValues.ORDER_CONFIRMATION
}
]
},
@ -665,6 +641,10 @@ const routingTable = () => [
{
issPageValue: issPageValues.TPA_SUBMIT,
maps: [
{
scenario: navigationScenarios.EDIT_POLICY_VEHICLE,
destinationIssPageValue: issPageValues.POLICY_VEHICLES
},
{
scenario: navigationScenarios.EDIT_VEHICLE,
destinationIssPageValue: issPageValues.VEHICLE_SELECTION
@ -675,11 +655,11 @@ const routingTable = () => [
},
{
scenario: navigationScenarios.EDIT_PREFERRED_SHOP,
destinationIssPageValue: issPageValues.TPA_SEARCH
destinationIssPageValue: issPageValues.PROVIDER_PREFERENCE
},
{
scenario: navigationScenarios.EDIT_CONTACT_DETAILS,
destinationIssPageValue: issPageValues.CONTACT_DETAILS
destinationIssPageValue: issPageValues.POLICY_HOLDER_DETAILS
},
{
scenario: navigationScenarios.CLICKED_BACK,

View file

@ -13,7 +13,6 @@ import damageLocationsSelected from '@/constants/damage-locations-selected';
import coverageStatuses from '@/constants/coverage-statuses';
import { AppointmentTypeStrings, PREMIUM_FEE_PART_TYPE } from '@/constants/schedule-constants';
import { convertDateStringToDate, getDateDifferenceInDays, militaryToTwelveHourTime } from '@/helpers/date-helper';
// import endorsementOptions from '@/constants/endorsement-options';
const storeId = 'main';
@ -220,6 +219,8 @@ export const useMainStore = defineStore({
lineItems: (state) => state.order.lineItems,
payment: (state) => state.order.payment,
policy: (state) => state.order.policy,
hasExactlyOneChip: () => state.order.damage.numberOfChips === 1,
isPolicyVehicle: () => state.order.vehicle.policyVehicleId != null,
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,

View file

@ -1,10 +1,11 @@
$svg-calendar-picker: "data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M14.1762 1.52764H13.7656V1.50352C13.7656 1.10476 13.6076 0.722334 13.3263 0.44037C13.0451 0.158406 12.6636 0 12.2659 0C11.8681 0 11.4866 0.158406 11.2054 0.44037C10.9241 0.722334 10.7661 1.10476 10.7661 1.50352V1.52764H5.42476V1.50352C5.42476 1.10476 5.26675 0.722334 4.9855 0.44037C4.70424 0.158406 4.32277 0 3.92501 0C3.52725 0 3.14579 0.158406 2.86453 0.44037C2.58327 0.722334 2.42526 1.10476 2.42526 1.50352V1.52764H1.82376C1.34046 1.52891 0.877316 1.72195 0.53557 2.06455C0.193824 2.40716 0.00127018 2.87146 0 3.35598V14.1717C0.0016909 14.656 0.194379 15.1201 0.536035 15.4626C0.87769 15.8051 1.34059 15.9983 1.82376 16H14.1746C14.6581 15.9987 15.1214 15.8057 15.4634 15.4632C15.8054 15.1206 15.9983 14.6563 16 14.1717V3.35598C15.9987 2.87146 15.8062 2.40716 15.4644 2.06455C15.1227 1.72195 14.6595 1.52891 14.1762 1.52764ZM11.8889 1.50352C11.8889 1.4033 11.9286 1.30718 11.9993 1.23631C12.07 1.16544 12.1659 1.12563 12.2659 1.12563C12.3658 1.12563 12.4617 1.16544 12.5324 1.23631C12.6031 1.30718 12.6428 1.4033 12.6428 1.50352V2.99899C12.6428 3.09922 12.6031 3.19534 12.5324 3.2662C12.4617 3.33707 12.3658 3.37688 12.2659 3.37688C12.1659 3.37688 12.07 3.33707 11.9993 3.2662C11.9286 3.19534 11.8889 3.09922 11.8889 2.99899V1.50352ZM3.54807 1.50352C3.54807 1.4033 3.58778 1.30718 3.65847 1.23631C3.72916 1.16544 3.82504 1.12563 3.92501 1.12563C4.02498 1.12563 4.12086 1.16544 4.19155 1.23631C4.26224 1.30718 4.30195 1.4033 4.30195 1.50352V2.99899C4.30195 3.09922 4.26224 3.19534 4.19155 3.2662C4.12086 3.33707 4.02498 3.37688 3.92501 3.37688C3.82504 3.37688 3.72916 3.33707 3.65847 3.2662C3.58778 3.19534 3.54807 3.09922 3.54807 2.99899V1.50352ZM14.8772 14.1717C14.8747 14.3573 14.8001 14.5345 14.6691 14.6658C14.5382 14.797 14.3614 14.8719 14.1762 14.8744H1.82536C1.63995 14.8723 1.4627 14.7976 1.33144 14.6663C1.20018 14.5351 1.12531 14.3575 1.12281 14.1717V6.59296H14.8772V14.1717Z' fill='%23167CAC'/%3E%3Cpath d='M2.33063 11.282H3.93464V12.6006C3.93464 12.7499 3.99379 12.8931 4.09907 12.9986C4.20435 13.1041 4.34715 13.1634 4.49604 13.1634C4.64494 13.1634 4.78773 13.1041 4.89301 12.9986C4.9983 12.8931 5.05745 12.7499 5.05745 12.6006V11.282H7.46346V12.6006C7.46346 12.7499 7.52261 12.8931 7.62789 12.9986C7.73318 13.1041 7.87597 13.1634 8.02486 13.1634C8.17376 13.1634 8.31655 13.1041 8.42184 12.9986C8.52712 12.8931 8.58627 12.7499 8.58627 12.6006V11.282H10.9923V12.6006C10.9923 12.7499 11.0514 12.8931 11.1567 12.9986C11.262 13.1041 11.4048 13.1634 11.5537 13.1634C11.7026 13.1634 11.8454 13.1041 11.9507 12.9986C12.0559 12.8931 12.1151 12.7499 12.1151 12.6006V11.282H13.7191C13.868 11.282 14.0108 11.2227 14.1161 11.1172C14.2214 11.0116 14.2805 10.8685 14.2805 10.7192C14.2805 10.57 14.2214 10.4268 14.1161 10.3213C14.0108 10.2157 13.868 10.1564 13.7191 10.1564H12.1151V8.84425C12.1151 8.69498 12.0559 8.55183 11.9507 8.44628C11.8454 8.34073 11.7026 8.28143 11.5537 8.28143C11.4048 8.28143 11.262 8.34073 11.1567 8.44628C11.0514 8.55183 10.9923 8.69498 10.9923 8.84425V10.1628H8.58627V8.84425C8.58627 8.69498 8.52712 8.55183 8.42184 8.44628C8.31655 8.34073 8.17376 8.28143 8.02486 8.28143C7.87597 8.28143 7.73318 8.34073 7.62789 8.44628C7.52261 8.55183 7.46346 8.69498 7.46346 8.84425V10.1628H5.05745V8.84425C5.05745 8.69498 4.9983 8.55183 4.89301 8.44628C4.78773 8.34073 4.64494 8.28143 4.49604 8.28143C4.34715 8.28143 4.20435 8.34073 4.09907 8.44628C3.99379 8.55183 3.93464 8.69498 3.93464 8.84425V10.1628H2.33063C2.18174 10.1628 2.03894 10.2221 1.93366 10.3277C1.82837 10.4332 1.76923 10.5764 1.76923 10.7257C1.76923 10.8749 1.82837 11.0181 1.93366 11.1236C2.03894 11.2292 2.18174 11.2885 2.33063 11.2885V11.282Z' fill='%23167CAC'/%3E%3C/svg%3E";
$svg-date-picker-nav-back-button: "data:image/svg+xml,%3Csvg width='7' height='12' viewBox='0 0 7 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0.331685 6.00121C0.330445 5.82446 0.399256 5.65442 0.523053 5.52832L5.84499 0.198256C5.97188 0.0713149 6.14397 -8.63821e-08 6.32341 -6.72174e-08C6.50285 -4.80527e-08 6.67495 0.071315 6.80183 0.198256C6.92872 0.325198 7 0.497368 7 0.67689C7 0.856412 6.92872 1.02858 6.80183 1.15552L1.94874 6.00121L6.80183 10.8526C6.92745 10.9796 6.99751 11.1512 6.99662 11.3299C6.99572 11.5085 6.92393 11.6794 6.79705 11.8051C6.67016 11.9308 6.49857 12.0009 6.32003 12C6.14148 11.9991 5.97061 11.9273 5.84499 11.8003L0.526881 6.47601C0.401568 6.34983 0.331375 6.17908 0.331685 6.00121Z' fill='%231574A1'/%3E%3C/svg%3E%0A";
$svg-date-picker-forward-button: "data:image/svg+xml,%3Csvg width='7' height='12' viewBox='0 0 7 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.66831 5.99879C6.66955 6.17554 6.60074 6.34558 6.47695 6.47168L1.15501 11.8017C1.02812 11.9287 0.85603 12 0.676587 12C0.497145 12 0.325053 11.9287 0.198168 11.8017C0.0712831 11.6748 7.268e-09 11.5026 8.07183e-09 11.3231C8.87567e-09 11.1436 0.0712831 10.9714 0.198168 10.8445L5.05126 5.99879L0.198168 1.14736C0.0725521 1.02042 0.00248585 0.848755 0.00338306 0.670131C0.00428028 0.491506 0.0760674 0.320554 0.202952 0.194881C0.329837 0.0692091 0.501426 -0.000888818 0.679971 9.54485e-06C0.858515 0.000906955 1.02939 0.0727263 1.15501 0.199668L6.47312 5.52399C6.59843 5.65017 6.66862 5.82092 6.66831 5.99879Z' fill='%231574A1'/%3E%3C/svg%3E%0A";
$svg-drop-off-alert: "data:image/svg+xml,%3Csvg viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_13957_112512)'%3E%3Cpath d='M5.99865 0C4.81147 4.82643e-07 3.65095 0.352111 2.66392 1.01179C1.67688 1.67146 0.907678 2.60907 0.45361 3.70599C-0.000459241 4.80291 -0.11899 6.00986 0.113013 7.17415C0.345015 8.33845 0.917126 9.40778 1.75697 10.2469C2.59682 11.086 3.66666 11.6571 4.83117 11.8881C5.99567 12.119 7.20251 11.9994 8.29902 11.5443C9.39553 11.0893 10.3324 10.3192 10.9912 9.33159C11.65 8.34396 12.0011 7.18313 12 5.99594C11.9971 4.40566 11.3638 2.88142 10.2388 1.75742C9.11375 0.633431 7.58894 0.00143011 5.99865 0V0ZM5.99865 11.2478C4.96135 11.2473 3.94748 10.9392 3.08518 10.3627C2.22288 9.7861 1.55085 8.96685 1.15401 8.00846C0.75718 7.05006 0.653353 5.99554 0.855656 4.97815C1.05796 3.96077 1.55731 3.02618 2.29061 2.29251C3.0239 1.55884 3.95823 1.059 4.97551 0.856176C5.99279 0.653349 7.04737 0.756633 8.00597 1.15297C8.96457 1.54931 9.78416 2.22092 10.3612 3.08293C10.9382 3.94493 11.2467 4.95864 11.2478 5.99594C11.2478 7.38835 10.6949 8.72377 9.71053 9.70861C8.7262 10.6934 7.39106 11.2471 5.99865 11.2478V11.2478Z' fill='%2306577C'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M6.22736 8.84695C6.30613 8.76818 6.35038 8.66135 6.35038 8.54996V5.30996C6.35038 5.19857 6.30613 5.09174 6.22736 5.01298C6.1486 4.93421 6.04177 4.88996 5.93038 4.88996C5.81899 4.88996 5.71216 4.93421 5.63339 5.01298C5.55463 5.09174 5.51038 5.19857 5.51038 5.30996V8.54996C5.51038 8.66135 5.55463 8.76818 5.63339 8.84695C5.71216 8.92571 5.81899 8.96996 5.93038 8.96996C6.04177 8.96996 6.1486 8.92571 6.22736 8.84695ZM5.69704 3.97918C5.76611 4.02533 5.84731 4.04996 5.93038 4.04996C5.98558 4.05012 6.04026 4.03936 6.09129 4.01831C6.14232 3.99726 6.18868 3.96633 6.22771 3.9273C6.26675 3.88827 6.29768 3.8419 6.31873 3.79088C6.33978 3.73985 6.35053 3.68516 6.35038 3.62996C6.35038 3.54689 6.32574 3.46569 6.27959 3.39662C6.23344 3.32755 6.16785 3.27372 6.0911 3.24193C6.01436 3.21014 5.92991 3.20183 5.84844 3.21803C5.76697 3.23424 5.69213 3.27424 5.63339 3.33298C5.57465 3.39171 5.53465 3.46655 5.51845 3.54802C5.50224 3.6295 5.51056 3.71394 5.54235 3.79069C5.57414 3.86743 5.62797 3.93303 5.69704 3.97918Z' fill='%2306577C'/%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='clip0_13957_112512'%3E%3Crect width='12' height='12' fill='white'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E%0A";
$svg-loading-modal-image: "data:image/svg+xml;charset=UTF-8,%3csvg fill='none' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 84 32'%3e%3cpath d='M22.39 27.313a3.53 3.53 0 0 0 7.058 0' fill='%23fff'/%3e%3cpath d='M22.39 27.313a3.53 3.53 0 0 0 7.058 0' stroke='%23000' stroke-width='.75' stroke-linecap='round' stroke-linejoin='round'/%3e%3cpath d='M54.459 27.313a3.527 3.527 0 0 0 7.054 0' fill='%23fff'/%3e%3cpath d='M54.459 27.313a3.527 3.527 0 0 0 7.054 0' stroke='%23000' stroke-width='.75' stroke-linecap='round' stroke-linejoin='round'/%3e%3cpath d='M42.06.8c8.08 0 11.89.935 11.89.935 3.58.576 5.696 7.207 5.696 7.207h.727c0-2.427 1.302-2.397 2.341-2 .723.292 1.363.76 1.86 1.361 1.319 1.547.465 1.674.465 1.674h-5.067l3.846 3.01v12.016c0 2.41-2.029 2.31-2.029 2.31H22.338s-2.029.1-2.029-2.31V12.996l3.84-3.009H19.07s-.853-.127.466-1.674a4.693 4.693 0 0 1 1.86-1.361c1.032-.397 2.341-.427 2.341 2h.736s2.117-6.64 5.696-7.21c0 0 3.81-.942 11.89-.942Z' fill='%23fff' stroke='%23000' stroke-width='.75' stroke-linecap='round' stroke-linejoin='round'/%3e%3cpath d='M42.06 8.847c7.924 0 14.685.511 14.685.511 0-2.585-2.38-6.011-2.38-6.011S50.4 2.519 42.06 2.519s-12.303.828-12.303.828-2.38 3.426-2.38 6.011c0 0 6.76-.51 14.683-.51Z' fill='%23DA291C' stroke='%23000' stroke-width='.75' stroke-linecap='round' stroke-linejoin='round'/%3e%3cpath d='M35.277 16.523a62.558 62.558 0 0 1 13.277 0m3.276-.439s2.384-2.257 8.608-2.257c0 0 1.179 2.657-2.54 3.37m-25.894-1.113s-2.387-2.257-8.611-2.257c0 0-1.16 2.579 2.543 3.37m-3.546 5.856s21.52 3.647 39.123 0' stroke='%23000' stroke-width='.75' stroke-linecap='round' stroke-linejoin='round'/%3e%3c/svg%3e";
$svg-shop-list-button-green-availability: "data:image/svg+xml,%3Csvg viewBox='0 0 13 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.5 12C3.19159 12 0.5 9.30841 0.5 6C0.5 2.69159 3.19159 0 6.5 0C9.80841 0 12.5 2.69159 12.5 6C12.5 9.30841 9.80841 12 6.5 12ZM6.5 0.785047C3.62449 0.785047 1.28505 3.12449 1.28505 6C1.28505 8.87551 3.62449 11.215 6.5 11.215C9.37551 11.215 11.715 8.87551 11.715 6C11.715 3.12449 9.37551 0.785047 6.5 0.785047Z' fill='%23006A36'/%3E%3Cpath d='M5.697 7.95252C5.5927 7.95252 5.49289 7.91102 5.41999 7.837L3.90597 6.32299C3.75233 6.16934 3.75233 5.92149 3.90597 5.76785C4.05962 5.6142 4.30747 5.6142 4.46111 5.76785L5.69812 7.00373L8.53999 4.16186C8.69364 4.00822 8.94149 4.00822 9.09513 4.16186C9.24878 4.31551 9.24878 4.56336 9.09513 4.717L5.97626 7.83588C5.90224 7.9099 5.80242 7.9514 5.69925 7.9514L5.697 7.95252Z' fill='%23006A36'/%3E%3C/svg%3E%0A";
$svg-shop-list-button-orange-availability: "data:image/svg+xml,%3Csvg viewBox='0 0 13 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.5 0C9.81368 0 12.5 2.68632 12.5 6C12.5 9.31368 9.81368 12 6.5 12C3.18632 12 0.5 9.31368 0.5 6C0.5 2.68632 3.18632 0 6.5 0ZM6.5 0.84C3.6548 0.84 1.34 3.1548 1.34 6C1.34 8.8452 3.6548 11.16 6.5 11.16C9.3452 11.16 11.66 8.8452 11.66 6C11.66 3.1548 9.3452 0.84 6.5 0.84ZM7.90018 4.00596C8.06422 3.84204 8.33002 3.84192 8.49406 4.00596C8.6581 4.17 8.6581 4.43592 8.49406 4.59996L7.0939 6L8.49406 7.40004C8.6581 7.56408 8.6581 7.83 8.49406 7.99404C8.4121 8.076 8.30458 8.11704 8.19706 8.11704C8.08966 8.11704 7.98214 8.076 7.90018 7.99404L6.50002 6.594L5.09986 7.99404C5.01778 8.076 4.91038 8.11704 4.80286 8.11704C4.69546 8.11704 4.58794 8.076 4.50598 7.99404C4.34182 7.83 4.34182 7.56408 4.50598 7.40004L5.90614 6L4.50598 4.59996C4.34182 4.43592 4.34182 4.17 4.50598 4.00596C4.66978 3.84192 4.93582 3.84204 5.09986 4.00596L6.50002 5.406L7.90018 4.00596Z' fill='%23E86421'/%3E%3C/svg%3E%0A";
$svg-update-zip-text-link: "data:image/svg+xml,%3Csvg viewBox='0 0 13 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.49635 1.00142e-07C5.64734 -0.000153295 4.80722 0.175918 4.0274 0.517444C3.24757 0.858969 2.54443 1.35877 1.96099 1.98626C0.765713 3.27588 0.0999756 4.98141 0.0999756 6.75394C0.0999756 8.52646 0.765713 10.232 1.96099 11.5216L5.98324 15.777C6.04954 15.8475 6.12918 15.9036 6.21736 15.9419C6.30555 15.9802 6.40045 16 6.49635 16C6.59225 16 6.68716 15.9802 6.77534 15.9419C6.86353 15.9036 6.94317 15.8475 7.00946 15.777L11.0317 11.52C12.2391 10.2383 12.909 8.52914 12.8999 6.75394C12.9094 4.97818 12.2394 3.26832 11.0317 1.98626C10.4481 1.35899 9.74493 0.859347 8.96514 0.517839C8.18535 0.176331 7.34532 0.000130509 6.49635 1.00142e-07V1.00142e-07ZM6.49635 9.13131C6.02507 9.13131 5.56437 8.98913 5.17251 8.72275C4.78065 8.45637 4.47524 8.07776 4.29488 7.63479C4.11453 7.19181 4.06734 6.70438 4.15928 6.23412C4.25123 5.76387 4.47817 5.33191 4.81142 4.99287C5.14467 4.65384 5.56925 4.42295 6.03148 4.32941C6.49371 4.23587 6.97282 4.28388 7.40823 4.46736C7.84364 4.65085 8.21579 4.96157 8.47762 5.36023C8.73945 5.7589 8.87921 6.2276 8.87921 6.70707C8.87921 7.34974 8.62837 7.96611 8.18185 8.4207C7.73532 8.87528 7.12964 9.13088 6.49794 9.13131H6.49635Z' fill='%231574A1'/%3E%3C/svg%3E%0A";
$svg-payment-method-review-toggle: "data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 8.9' xml:space='preserve'%3e%3cpath d='M8 8.9c-.2 0-.5-.1-.6-.3L.3 1.5C.1 1.4 0 1.1 0 .9 0 .7.1.4.3.3.4.1.7 0 .9 0c.2 0 .5.1.6.3L8 6.7 14.5.2c.1-.1.4-.2.6-.2.2 0 .5.1.6.3s.3.4.3.6c0 .2-.1.5-.3.6L8.6 8.6c-.1.2-.4.3-.6.3z' fill='%231474a2'/%3e%3c/svg%3e";
$svg-search-icon: "data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M15.7817 14.7328L11.8252 10.7762C12.8833 9.45005 13.3936 7.76911 13.2513 6.07849C13.1091 4.38788 12.325 2.81587 11.0601 1.6852C9.79515 0.554524 8.14538 -0.0490261 6.44946 -0.00154744C4.75353 0.0459312 3.14012 0.740836 1.94045 1.94051C0.740775 3.14018 0.0458701 4.75359 -0.00160848 6.44952C-0.0490871 8.14545 0.554463 9.79521 1.68514 11.0601C2.81581 12.325 4.38782 13.1091 6.07843 13.2514C7.76905 13.3937 9.44999 12.8834 10.7762 11.8252L14.7349 15.7839C14.8044 15.8527 14.8869 15.907 14.9774 15.9439C15.068 15.9808 15.165 15.9995 15.2628 15.9989C15.3606 15.9983 15.4573 15.9784 15.5475 15.9405C15.6376 15.9025 15.7194 15.8471 15.7881 15.7776C15.8568 15.708 15.9112 15.6256 15.9481 15.535C15.985 15.4444 16.0036 15.3474 16.0031 15.2496C16.0025 15.1518 15.9826 15.0551 15.9446 14.965C15.9067 14.8748 15.8513 14.7931 15.7817 14.7243V14.7328ZM6.63737 11.7913C5.61803 11.7913 4.62157 11.4891 3.77402 10.9228C2.92646 10.3564 2.26587 9.5515 1.87578 8.60975C1.4857 7.668 1.38363 6.63172 1.5825 5.63196C1.78136 4.6322 2.27222 3.71386 2.99301 2.99307C3.7138 2.27229 4.63214 1.78142 5.6319 1.58256C6.63166 1.38369 7.66793 1.48576 8.60969 1.87585C9.55144 2.26593 10.3564 2.92652 10.9227 3.77408C11.489 4.62163 11.7913 5.61809 11.7913 6.63743C11.7896 8.00382 11.2461 9.31376 10.2799 10.2799C9.3137 11.2461 8.00376 11.7897 6.63737 11.7913Z' fill='%231574A1'/%3E%3C/svg%3E%0A";
$svg-select-icon: "data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 8.89' xml:space='preserve'%3e%3cpath d='M8 8.89c-.24 0-.46-.09-.63-.26L.26 1.53a.901.901 0 0 1 0-1.27C.43.1.66 0 .9 0s.47.1.64.26L8 6.74 14.47.27c.17-.17.4-.27.64-.27s.47.1.63.27c.17.17.26.4.26.64s-.1.47-.27.63l-7.1 7.09a.86.86 0 0 1-.63.26z' fill='%231474a2'/%3e%3c/svg%3e";
$svg-calendar-picker: "data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M14.1762 1.52764H13.7656V1.50352C13.7656 1.10476 13.6076 0.722334 13.3263 0.44037C13.0451 0.158406 12.6636 0 12.2659 0C11.8681 0 11.4866 0.158406 11.2054 0.44037C10.9241 0.722334 10.7661 1.10476 10.7661 1.50352V1.52764H5.42476V1.50352C5.42476 1.10476 5.26675 0.722334 4.9855 0.44037C4.70424 0.158406 4.32277 0 3.92501 0C3.52725 0 3.14579 0.158406 2.86453 0.44037C2.58327 0.722334 2.42526 1.10476 2.42526 1.50352V1.52764H1.82376C1.34046 1.52891 0.877316 1.72195 0.53557 2.06455C0.193824 2.40716 0.00127018 2.87146 0 3.35598V14.1717C0.0016909 14.656 0.194379 15.1201 0.536035 15.4626C0.87769 15.8051 1.34059 15.9983 1.82376 16H14.1746C14.6581 15.9987 15.1214 15.8057 15.4634 15.4632C15.8054 15.1206 15.9983 14.6563 16 14.1717V3.35598C15.9987 2.87146 15.8062 2.40716 15.4644 2.06455C15.1227 1.72195 14.6595 1.52891 14.1762 1.52764ZM11.8889 1.50352C11.8889 1.4033 11.9286 1.30718 11.9993 1.23631C12.07 1.16544 12.1659 1.12563 12.2659 1.12563C12.3658 1.12563 12.4617 1.16544 12.5324 1.23631C12.6031 1.30718 12.6428 1.4033 12.6428 1.50352V2.99899C12.6428 3.09922 12.6031 3.19534 12.5324 3.2662C12.4617 3.33707 12.3658 3.37688 12.2659 3.37688C12.1659 3.37688 12.07 3.33707 11.9993 3.2662C11.9286 3.19534 11.8889 3.09922 11.8889 2.99899V1.50352ZM3.54807 1.50352C3.54807 1.4033 3.58778 1.30718 3.65847 1.23631C3.72916 1.16544 3.82504 1.12563 3.92501 1.12563C4.02498 1.12563 4.12086 1.16544 4.19155 1.23631C4.26224 1.30718 4.30195 1.4033 4.30195 1.50352V2.99899C4.30195 3.09922 4.26224 3.19534 4.19155 3.2662C4.12086 3.33707 4.02498 3.37688 3.92501 3.37688C3.82504 3.37688 3.72916 3.33707 3.65847 3.2662C3.58778 3.19534 3.54807 3.09922 3.54807 2.99899V1.50352ZM14.8772 14.1717C14.8747 14.3573 14.8001 14.5345 14.6691 14.6658C14.5382 14.797 14.3614 14.8719 14.1762 14.8744H1.82536C1.63995 14.8723 1.4627 14.7976 1.33144 14.6663C1.20018 14.5351 1.12531 14.3575 1.12281 14.1717V6.59296H14.8772V14.1717Z' fill='%23167CAC'/%3E%3Cpath d='M2.33063 11.282H3.93464V12.6006C3.93464 12.7499 3.99379 12.8931 4.09907 12.9986C4.20435 13.1041 4.34715 13.1634 4.49604 13.1634C4.64494 13.1634 4.78773 13.1041 4.89301 12.9986C4.9983 12.8931 5.05745 12.7499 5.05745 12.6006V11.282H7.46346V12.6006C7.46346 12.7499 7.52261 12.8931 7.62789 12.9986C7.73318 13.1041 7.87597 13.1634 8.02486 13.1634C8.17376 13.1634 8.31655 13.1041 8.42184 12.9986C8.52712 12.8931 8.58627 12.7499 8.58627 12.6006V11.282H10.9923V12.6006C10.9923 12.7499 11.0514 12.8931 11.1567 12.9986C11.262 13.1041 11.4048 13.1634 11.5537 13.1634C11.7026 13.1634 11.8454 13.1041 11.9507 12.9986C12.0559 12.8931 12.1151 12.7499 12.1151 12.6006V11.282H13.7191C13.868 11.282 14.0108 11.2227 14.1161 11.1172C14.2214 11.0116 14.2805 10.8685 14.2805 10.7192C14.2805 10.57 14.2214 10.4268 14.1161 10.3213C14.0108 10.2157 13.868 10.1564 13.7191 10.1564H12.1151V8.84425C12.1151 8.69498 12.0559 8.55183 11.9507 8.44628C11.8454 8.34073 11.7026 8.28143 11.5537 8.28143C11.4048 8.28143 11.262 8.34073 11.1567 8.44628C11.0514 8.55183 10.9923 8.69498 10.9923 8.84425V10.1628H8.58627V8.84425C8.58627 8.69498 8.52712 8.55183 8.42184 8.44628C8.31655 8.34073 8.17376 8.28143 8.02486 8.28143C7.87597 8.28143 7.73318 8.34073 7.62789 8.44628C7.52261 8.55183 7.46346 8.69498 7.46346 8.84425V10.1628H5.05745V8.84425C5.05745 8.69498 4.9983 8.55183 4.89301 8.44628C4.78773 8.34073 4.64494 8.28143 4.49604 8.28143C4.34715 8.28143 4.20435 8.34073 4.09907 8.44628C3.99379 8.55183 3.93464 8.69498 3.93464 8.84425V10.1628H2.33063C2.18174 10.1628 2.03894 10.2221 1.93366 10.3277C1.82837 10.4332 1.76923 10.5764 1.76923 10.7257C1.76923 10.8749 1.82837 11.0181 1.93366 11.1236C2.03894 11.2292 2.18174 11.2885 2.33063 11.2885V11.282Z' fill='%23167CAC'/%3E%3C/svg%3E";
$svg-shop-list-button-green-availability: "data:image/svg+xml,%3Csvg viewBox='0 0 13 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.5 12C3.19159 12 0.5 9.30841 0.5 6C0.5 2.69159 3.19159 0 6.5 0C9.80841 0 12.5 2.69159 12.5 6C12.5 9.30841 9.80841 12 6.5 12ZM6.5 0.785047C3.62449 0.785047 1.28505 3.12449 1.28505 6C1.28505 8.87551 3.62449 11.215 6.5 11.215C9.37551 11.215 11.715 8.87551 11.715 6C11.715 3.12449 9.37551 0.785047 6.5 0.785047Z' fill='%23006A36'/%3E%3Cpath d='M5.697 7.95252C5.5927 7.95252 5.49289 7.91102 5.41999 7.837L3.90597 6.32299C3.75233 6.16934 3.75233 5.92149 3.90597 5.76785C4.05962 5.6142 4.30747 5.6142 4.46111 5.76785L5.69812 7.00373L8.53999 4.16186C8.69364 4.00822 8.94149 4.00822 9.09513 4.16186C9.24878 4.31551 9.24878 4.56336 9.09513 4.717L5.97626 7.83588C5.90224 7.9099 5.80242 7.9514 5.69925 7.9514L5.697 7.95252Z' fill='%23006A36'/%3E%3C/svg%3E%0A";
$svg-shop-list-button-orange-availability: "data:image/svg+xml,%3Csvg viewBox='0 0 13 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.5 0C9.81368 0 12.5 2.68632 12.5 6C12.5 9.31368 9.81368 12 6.5 12C3.18632 12 0.5 9.31368 0.5 6C0.5 2.68632 3.18632 0 6.5 0ZM6.5 0.84C3.6548 0.84 1.34 3.1548 1.34 6C1.34 8.8452 3.6548 11.16 6.5 11.16C9.3452 11.16 11.66 8.8452 11.66 6C11.66 3.1548 9.3452 0.84 6.5 0.84ZM7.90018 4.00596C8.06422 3.84204 8.33002 3.84192 8.49406 4.00596C8.6581 4.17 8.6581 4.43592 8.49406 4.59996L7.0939 6L8.49406 7.40004C8.6581 7.56408 8.6581 7.83 8.49406 7.99404C8.4121 8.076 8.30458 8.11704 8.19706 8.11704C8.08966 8.11704 7.98214 8.076 7.90018 7.99404L6.50002 6.594L5.09986 7.99404C5.01778 8.076 4.91038 8.11704 4.80286 8.11704C4.69546 8.11704 4.58794 8.076 4.50598 7.99404C4.34182 7.83 4.34182 7.56408 4.50598 7.40004L5.90614 6L4.50598 4.59996C4.34182 4.43592 4.34182 4.17 4.50598 4.00596C4.66978 3.84192 4.93582 3.84204 5.09986 4.00596L6.50002 5.406L7.90018 4.00596Z' fill='%23E86421'/%3E%3C/svg%3E%0A";
$svg-update-zip-text-link: "data:image/svg+xml,%3Csvg viewBox='0 0 13 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.49635 1.00142e-07C5.64734 -0.000153295 4.80722 0.175918 4.0274 0.517444C3.24757 0.858969 2.54443 1.35877 1.96099 1.98626C0.765713 3.27588 0.0999756 4.98141 0.0999756 6.75394C0.0999756 8.52646 0.765713 10.232 1.96099 11.5216L5.98324 15.777C6.04954 15.8475 6.12918 15.9036 6.21736 15.9419C6.30555 15.9802 6.40045 16 6.49635 16C6.59225 16 6.68716 15.9802 6.77534 15.9419C6.86353 15.9036 6.94317 15.8475 7.00946 15.777L11.0317 11.52C12.2391 10.2383 12.909 8.52914 12.8999 6.75394C12.9094 4.97818 12.2394 3.26832 11.0317 1.98626C10.4481 1.35899 9.74493 0.859347 8.96514 0.517839C8.18535 0.176331 7.34532 0.000130509 6.49635 1.00142e-07V1.00142e-07ZM6.49635 9.13131C6.02507 9.13131 5.56437 8.98913 5.17251 8.72275C4.78065 8.45637 4.47524 8.07776 4.29488 7.63479C4.11453 7.19181 4.06734 6.70438 4.15928 6.23412C4.25123 5.76387 4.47817 5.33191 4.81142 4.99287C5.14467 4.65384 5.56925 4.42295 6.03148 4.32941C6.49371 4.23587 6.97282 4.28388 7.40823 4.46736C7.84364 4.65085 8.21579 4.96157 8.47762 5.36023C8.73945 5.7589 8.87921 6.2276 8.87921 6.70707C8.87921 7.34974 8.62837 7.96611 8.18185 8.4207C7.73532 8.87528 7.12964 9.13088 6.49794 9.13131H6.49635Z' fill='%231574A1'/%3E%3C/svg%3E%0A";