Merge pull request #1065 from Safelite/feature/humphries/INSR-7757
INSR-7757: order-confirmation Parity
This commit is contained in:
commit
c415f10ffa
20 changed files with 805 additions and 960 deletions
|
|
@ -56,4 +56,4 @@ module.exports = {
|
|||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
const cartItemType = Object.freeze({
|
||||
RECYCLE_FEE: 'RECYCLE FEE',
|
||||
MOBILE_FEE: 'MOBILE FEE',
|
||||
VAP: 'VAP'
|
||||
VAP: 'VAP',
|
||||
WARRANTY: 'WARRANTY'
|
||||
});
|
||||
|
||||
export default cartItemType;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@
|
|||
RECALIBRATION: 'RECALIBRATION',
|
||||
REPLACE_FEE: 'REPLACE FEE',
|
||||
MOBILE_FEE: 'MOBILE FEE',
|
||||
REPAIR_FEE: 'REPAIR FEE'
|
||||
REPAIR_FEE: 'REPAIR FEE',
|
||||
WARRANTY: 'WARRANTY',
|
||||
MOLDING: 'MOULDING'
|
||||
});
|
||||
|
||||
export default partTypeStrings;
|
||||
|
|
|
|||
|
|
@ -71,6 +71,26 @@ export async function isGlassAvailableForCarId(carId) {
|
|||
}
|
||||
}
|
||||
|
||||
export function getGlassList(glassPieces) {
|
||||
let names = '';
|
||||
switch (glassPieces.length)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
names = glassPieces[0];
|
||||
break;
|
||||
case 2:
|
||||
names = glassPieces.join(' and ');
|
||||
break;
|
||||
default:
|
||||
names = glassPieces.slice(0, -1).join(', ') + ', and ' + glassPieces.slice(-1);
|
||||
break;
|
||||
}
|
||||
|
||||
return names.toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Commented code are copied directly from DigitalConsumer.FixMyGlass
|
||||
* and have not been adjusted for ISS.
|
||||
|
|
|
|||
|
|
@ -7,19 +7,21 @@ Object {
|
|||
"MOBILE_FEE": "MOBILE FEE",
|
||||
"RECYCLE_FEE": "RECYCLE FEE",
|
||||
"VAP": "VAP",
|
||||
"WARRANTY": "WARRANTY",
|
||||
},
|
||||
"isExpanded": false,
|
||||
"widget": Object {
|
||||
"amountDue": "AmountDueTextWidget",
|
||||
"amountPaid": "AmountPaidTextWidget",
|
||||
"basePrice": "BasePriceWidget",
|
||||
"deductible": "DeductibleWidget",
|
||||
"guaranteeText": "GuaranteeCartItemTextWidget",
|
||||
"mobileFee": "MobileServiceWidget",
|
||||
"recycleFee": "RecycleFeeWidget",
|
||||
"salesTax": "SalesTaxWidget",
|
||||
"servicePackage": "ServicePackageTitle",
|
||||
"subtotal": "SubtotalWidget",
|
||||
"vapsItemDescriptions": "VapsItemDescriptions",
|
||||
"warrantyText": "WarrantyCartItemTextWidget",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -73,47 +73,10 @@ describe('cart-dropdown component', () => {
|
|||
expect(wrapper.vm.$data).toMatchSnapshot();
|
||||
});
|
||||
describe('displays', () => {
|
||||
test('cart dropdown head', () => {
|
||||
// Arrange
|
||||
const reference = '#cart-dropdown-head';
|
||||
const { wrapper } = getMountedComponent(cartDropdown);
|
||||
|
||||
// Act
|
||||
const head = wrapper.find(reference);
|
||||
|
||||
// Assert
|
||||
expect(head.exists()).toBeTruthy();
|
||||
});
|
||||
test('cart dropdown head is visible', () => {
|
||||
// Arrange
|
||||
const reference = '#cart-dropdown-head';
|
||||
const { wrapper } = getMountedComponent(cartDropdown, {}, { showDropdownHeader: true });
|
||||
|
||||
// Act
|
||||
const head = wrapper.find(reference);
|
||||
|
||||
// Assert
|
||||
expect(head.exists()).toBeTruthy();
|
||||
expect(head.classes()).toContain('cart-toggle');
|
||||
});
|
||||
test('cart dropdown head is not visible', () => {
|
||||
// Arrange
|
||||
const reference = '#cart-dropdown-head';
|
||||
const { wrapper } = getMountedComponent(cartDropdown, {}, { showDropdownHeader: false });
|
||||
|
||||
// Act
|
||||
const head = wrapper.find(reference);
|
||||
|
||||
// Assert
|
||||
expect(head.exists()).toBeTruthy();
|
||||
expect(head.classes()).not.toContain('cart-toggle');
|
||||
});
|
||||
test('cart table', () => {
|
||||
// Arrange
|
||||
const reference = '#cart-table';
|
||||
const isExpanded = true;
|
||||
const initialData = { isExpanded };
|
||||
const { wrapper } = getMountedComponent({}, {}, initialData);
|
||||
const reference = '.cart-table';
|
||||
const { wrapper } = getMountedComponent({}, {}, {});
|
||||
|
||||
// Act
|
||||
const cartTable = wrapper.find(reference);
|
||||
|
|
@ -121,11 +84,9 @@ describe('cart-dropdown component', () => {
|
|||
// Assert
|
||||
expect(cartTable.exists()).toBeTruthy();
|
||||
});
|
||||
test('cart deductible when showDeductibleLineItem is true', () => {
|
||||
test('cart deductible when showDeductibleCartItem is true', () => {
|
||||
// Arrange
|
||||
const reference = '#cart-deductible';
|
||||
const isExpanded = true;
|
||||
const initialData = { isExpanded };
|
||||
const reference = '#deductible-label';
|
||||
const storeData = {
|
||||
order: {
|
||||
insuranceCoverage: {
|
||||
|
|
@ -134,7 +95,7 @@ describe('cart-dropdown component', () => {
|
|||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData, {}, initialData);
|
||||
const { wrapper } = getMountedComponent(storeData, {}, {});
|
||||
|
||||
// Act
|
||||
const cartDeductible = wrapper.find(reference);
|
||||
|
|
@ -144,9 +105,7 @@ describe('cart-dropdown component', () => {
|
|||
});
|
||||
test('cart base price when showDeductibleLineItem is false', () => {
|
||||
// Arrange
|
||||
const reference = '#cart-base-price';
|
||||
const isExpanded = true;
|
||||
const initialData = { isExpanded };
|
||||
const reference = '#base-price-label';
|
||||
const storeData = {
|
||||
order: {
|
||||
currentDeductible: 0,
|
||||
|
|
@ -160,7 +119,7 @@ describe('cart-dropdown component', () => {
|
|||
isClaimRegistrationRequired: true
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData, {}, initialData);
|
||||
const { wrapper } = getMountedComponent(storeData, {}, {});
|
||||
|
||||
// Act
|
||||
const cartBasePrice = wrapper.find(reference);
|
||||
|
|
@ -168,25 +127,32 @@ describe('cart-dropdown component', () => {
|
|||
// Assert
|
||||
expect(cartBasePrice.exists()).toBeTruthy();
|
||||
});
|
||||
test('service package', () => {
|
||||
// Arrange
|
||||
const reference = '#cart-service-package';
|
||||
const isExpanded = true;
|
||||
const initialData = { isExpanded };
|
||||
const { wrapper } = getMountedComponent({}, {}, initialData);
|
||||
|
||||
// Act
|
||||
const servicePackage = wrapper.find(reference);
|
||||
|
||||
// Assert
|
||||
expect(servicePackage.exists()).toBeTruthy();
|
||||
});
|
||||
test('non service package cart items', () => {
|
||||
// Arrange
|
||||
const reference = '#non-packaged-cart-items';
|
||||
const isExpanded = true;
|
||||
const initialData = { isExpanded };
|
||||
const { wrapper } = getMountedComponent({}, {}, initialData);
|
||||
const reference = '.non-packaged.cart-item';
|
||||
const part1 = { partType: 'apple' };
|
||||
const part2 = { partType: 'banana' };
|
||||
const part3 = { partType: 'orange' };
|
||||
const mobileFee = { partType: 'mobile' };
|
||||
const storeData = {
|
||||
order: {
|
||||
currentDeductible: 0,
|
||||
lineItems: {
|
||||
glassParts: [part1],
|
||||
supportingItems: [part2, part3],
|
||||
otherParts: [],
|
||||
feeItems: [mobileFee]
|
||||
},
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.VERIFIED,
|
||||
coverageType: coverageType.ITAC
|
||||
}
|
||||
},
|
||||
issConfig: {
|
||||
isClaimRegistrationRequired: true
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData, {}, {});
|
||||
|
||||
// Act
|
||||
const nonPackagedCartItems = wrapper.find(reference);
|
||||
|
|
@ -220,9 +186,7 @@ describe('cart-dropdown component', () => {
|
|||
test('cart footer', () => {
|
||||
// Arrange
|
||||
const reference = '#cart-footer';
|
||||
const isExpanded = true;
|
||||
const initialData = { isExpanded };
|
||||
const { wrapper } = getMountedComponent({}, {}, initialData);
|
||||
const { wrapper } = getMountedComponent({}, {}, {});
|
||||
|
||||
// Act
|
||||
const cartFooter = wrapper.find(reference);
|
||||
|
|
@ -233,9 +197,7 @@ describe('cart-dropdown component', () => {
|
|||
test('subtotal', () => {
|
||||
// Arrange
|
||||
const reference = '#cart-subtotal';
|
||||
const isExpanded = true;
|
||||
const initialData = { isExpanded };
|
||||
const { wrapper } = getMountedComponent({}, {}, initialData);
|
||||
const { wrapper } = getMountedComponent({}, {}, {});
|
||||
|
||||
// Act
|
||||
const subtotal = wrapper.find(reference);
|
||||
|
|
@ -246,9 +208,7 @@ describe('cart-dropdown component', () => {
|
|||
test('tax', () => {
|
||||
// Arrange
|
||||
const reference = '#cart-sales-tax';
|
||||
const isExpanded = true;
|
||||
const initialData = { isExpanded };
|
||||
const { wrapper } = getMountedComponent({}, {}, initialData);
|
||||
const { wrapper } = getMountedComponent({}, {}, {});
|
||||
|
||||
// Act
|
||||
const salesTax = wrapper.find(reference);
|
||||
|
|
@ -280,9 +240,7 @@ describe('cart-dropdown component', () => {
|
|||
test('bottom amount due', () => {
|
||||
// Arrange
|
||||
const reference = '#bottom-amount-due';
|
||||
const isExpanded = true;
|
||||
const initialData = { isExpanded };
|
||||
const { wrapper } = getMountedComponent({}, {}, initialData);
|
||||
const { wrapper } = getMountedComponent({}, {}, {});
|
||||
|
||||
// Act
|
||||
const bottomAmountDue = wrapper.find(reference);
|
||||
|
|
@ -295,8 +253,6 @@ describe('cart-dropdown component', () => {
|
|||
test('cart deductible when showDeductibleLineItem is false', () => {
|
||||
// Arrange
|
||||
const reference = '#cart-deductible';
|
||||
const isExpanded = true;
|
||||
const initialData = { isExpanded };
|
||||
const storeData = {
|
||||
order: {
|
||||
currentDeductible: 0,
|
||||
|
|
@ -310,7 +266,7 @@ describe('cart-dropdown component', () => {
|
|||
isClaimRegistrationRequired: true
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData, {}, initialData);
|
||||
const { wrapper } = getMountedComponent(storeData, {}, {});
|
||||
|
||||
// Act
|
||||
const cartDeductible = wrapper.find(reference);
|
||||
|
|
@ -321,8 +277,6 @@ describe('cart-dropdown component', () => {
|
|||
test('cart base price when showDeductibleLineItem is true', () => {
|
||||
// Arrange
|
||||
const reference = '#cart-base-price';
|
||||
const isExpanded = true;
|
||||
const initialData = { isExpanded };
|
||||
const storeData = {
|
||||
order: {
|
||||
insuranceCoverage: {
|
||||
|
|
@ -331,7 +285,7 @@ describe('cart-dropdown component', () => {
|
|||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData, {}, initialData);
|
||||
const { wrapper } = getMountedComponent(storeData, {}, {});
|
||||
|
||||
// Act
|
||||
const cartBasePrice = wrapper.find(reference);
|
||||
|
|
@ -1223,91 +1177,6 @@ describe('cart-dropdown component', () => {
|
|||
expect(result.name).toBe(expectedName);
|
||||
});
|
||||
});
|
||||
describe('packagePrice', () => {
|
||||
test('returns 0 when servicePackageCartItems is empty', () => {
|
||||
// Arrange
|
||||
getPriceOfLineItems.mockImplementation(() => 123);
|
||||
getPackageContents.mockImplementationOnce(() => []);
|
||||
const storeData = {
|
||||
order: {
|
||||
lineItems: {
|
||||
vaps: [
|
||||
{
|
||||
partType: partTypeStrings.REAR_WIPER
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.packagePrice;
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(0);
|
||||
});
|
||||
test('returns expected when servicePackageCartItems not empty', () => {
|
||||
// Arrange
|
||||
const rearPrice = 101;
|
||||
const frontPrice = 9;
|
||||
getPriceOfLineItems.mockImplementation((lineItems) => {
|
||||
if (lineItems.some((item) => item?.partType === partTypeStrings.FRONT_WIPER)) {
|
||||
return frontPrice;
|
||||
}
|
||||
if (lineItems.some((item) => item?.partType === partTypeStrings.REAR_WIPER)) {
|
||||
return rearPrice;
|
||||
}
|
||||
return 1;
|
||||
});
|
||||
getPackageContents.mockImplementationOnce(() => [partTypeStrings.FRONT_WIPER, partTypeStrings.REAR_WIPER]);
|
||||
const storeData = {
|
||||
order: {
|
||||
lineItems: {
|
||||
vaps: [
|
||||
{ partType: partTypeStrings.FRONT_WIPER },
|
||||
{ partType: partTypeStrings.REAR_WIPER }
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData);
|
||||
const expectedPrice = 110;
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.packagePrice;
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(expectedPrice);
|
||||
});
|
||||
test('returns expected when servicePackageCartItems has one item', () => {
|
||||
// Arrange
|
||||
const frontPrice = 9;
|
||||
getPriceOfLineItems.mockImplementation((lineItems) => {
|
||||
if (lineItems.some((item) => item?.partType === partTypeStrings.FRONT_WIPER)) {
|
||||
return frontPrice;
|
||||
}
|
||||
return 1;
|
||||
});
|
||||
getPackageContents.mockImplementationOnce(() => [partTypeStrings.FRONT_WIPER, partTypeStrings.REAR_WIPER]);
|
||||
const storeData = {
|
||||
order: {
|
||||
lineItems: {
|
||||
vaps: [
|
||||
{ partType: partTypeStrings.FRONT_WIPER }
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.packagePrice;
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(frontPrice);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('method', () => {
|
||||
const dollarAmount = '$84.00';
|
||||
|
|
|
|||
|
|
@ -1,156 +1,113 @@
|
|||
<template>
|
||||
<div>
|
||||
<div
|
||||
v-if="showDropdownHeader"
|
||||
id="cart-dropdown-head"
|
||||
class="row flex align-items-center pt-4 cart-header cart-toggle"
|
||||
:class="[isExpanded ? 'expanded' : '']"
|
||||
@click="toggleIsExpanded">
|
||||
<a
|
||||
aria-label="expand cart details"
|
||||
href="javascript:void(0)"
|
||||
class="col d-flex justify-content-between py-0">
|
||||
<span class="color-black">{{ amountDueLabel }}</span>
|
||||
<span class="color-green">{{ getDisplayed(amountDue) }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
id="cart-dropdown-head"
|
||||
class="expanded cart-header">
|
||||
</div>
|
||||
<div
|
||||
id="cart-table"
|
||||
class="cart-table">
|
||||
<div class="cart-table">
|
||||
<div class="cart-item-list">
|
||||
<div
|
||||
id="cart-line-items"
|
||||
class="color-darker-gray">
|
||||
<div
|
||||
id="cart-deductible-or-base-price"
|
||||
class="cart-item cart-gray">
|
||||
<div
|
||||
v-if="showDeductibleCartItem"
|
||||
id="cart-deductible"
|
||||
class="d-flex justify-content-between align-items-center py-2">
|
||||
id="cart-deductible-or-base-price"
|
||||
class="cart-item">
|
||||
<div class="price-row">
|
||||
<template
|
||||
v-if="showDeductibleCartItem">
|
||||
<span id="deductible-label">{{ deductibleLabel }}</span>
|
||||
<span id="deductible-value">{{ getDisplayed(deductible) }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
id="cart-base-price"
|
||||
class="d-flex justify-content-between align-items-center">
|
||||
</template>
|
||||
<template
|
||||
v-else>
|
||||
<span id="base-price-label">{{ basePriceLabel }}</span>
|
||||
<span id="base-price-value">{{ getDisplayed(servicePrice) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Packaged Cart Items -->
|
||||
<div
|
||||
id="cart-service-package"
|
||||
class="px-5">
|
||||
<div
|
||||
id="service-package-name"
|
||||
class="py-2 d-flex justify-content-between align-items-center">
|
||||
<textBlock
|
||||
:cmsWidgetName="servicePackageLabelWidget"
|
||||
class="force-no-top-margin" />
|
||||
<span id="service-package-price">{{ formatAmountInDollars(packagePrice) }}</span>
|
||||
</div>
|
||||
<div
|
||||
id="service-items"
|
||||
class="ps-4">
|
||||
<div
|
||||
v-for="(item, i) in servicePackageCartItems"
|
||||
:key="i">
|
||||
<div class="py-2 d-flex justify-content-between align-items-center">
|
||||
<span>{{ item?.name ?? '' }}</span>
|
||||
<textLink
|
||||
v-if="!readOnly"
|
||||
linkType="text"
|
||||
text="Remove"
|
||||
href="javascript:void(0)"
|
||||
@clickEvent="removeVap(item.partType)">
|
||||
<template #after-text>
|
||||
<span class="sr-only">{{ item?.name ?? '' }}</span>
|
||||
</template>
|
||||
</textLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- NonPackaged Cart Items -->
|
||||
<div id="non-packaged-cart-items">
|
||||
<div
|
||||
v-for="(item, i) in nonServicePackageCartItems"
|
||||
:key="i"
|
||||
class="px-5 py-2 non-packaged-cart-item">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span
|
||||
v-if="item.cartItemType === cartItemType.RECYCLE_FEE"
|
||||
id="recycle-fee-label">
|
||||
<textLink
|
||||
linkType="text"
|
||||
:text="item?.name ?? ''"
|
||||
href="javascript:void(0)"
|
||||
@clickEvent="openModal(RECYCLING_MODAL_REF_NAME)" />
|
||||
</span>
|
||||
<span v-else>{{ item?.name ?? '' }}</span>
|
||||
<span>{{ formatAmountInDollars(item?.subTotal ?? 0) }}</span>
|
||||
</div>
|
||||
<textLink
|
||||
v-if="item.cartItemType === cartItemType.VAP
|
||||
&& !readOnly"
|
||||
linkType="text"
|
||||
text="Remove"
|
||||
href="javascript:void(0)"
|
||||
@clickEvent="removeVap(item.partType)">
|
||||
<template #after-text>
|
||||
<span class="sr-only">{{ item?.name ?? '' }}</span>
|
||||
</template>
|
||||
</textLink>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
id="cart-footer"
|
||||
class="cart-footer pb-5">
|
||||
<div
|
||||
id="cart-subtotal-and-tax"
|
||||
class="subtotal-and-tax">
|
||||
<div
|
||||
id="cart-subtotal"
|
||||
class="py-2 col d-flex justify-content-between">
|
||||
<span id="subtotal-label">{{ subtotalLabel }}</span>
|
||||
<span id="subtotal-value">{{ getDisplayed(subTotal) }}</span>
|
||||
</div>
|
||||
<div
|
||||
id="cart-sales-tax"
|
||||
class="py-2 col d-flex justify-content-between">
|
||||
<span id="sales-tax-label">{{ salesTaxLabel }}</span>
|
||||
<span id="sales-tax-value">{{ formatAmountInDollars(salesTax) }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="showAsPaid"
|
||||
id="cart-amount-paid"
|
||||
class="py-2 col d-flex justify-content-between">
|
||||
<span id="amount-paid-label">{{ amountPaidLabel }}</span>
|
||||
<span if="amount-paid-value">{{ getDisplayed(amountPaid) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
id="bottom-amount-due"
|
||||
class="bottom-amount-due col d-flex justify-content-between">
|
||||
<span id="bottom-amount-due-label">{{ amountDueLabel }}</span>
|
||||
<span id="bottom-amount-due-value">{{ getDisplayed(amountDue) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<contentGroupModal
|
||||
:ref="RECYCLING_MODAL_REF_NAME"
|
||||
:cmsWidgetName="recyclingModalCmsWidgetName">
|
||||
<textBlock cmsWidgetName="RecycleTextBlock" />
|
||||
</contentGroupModal>
|
||||
|
||||
<!-- NonPackaged Cart Items -->
|
||||
<div
|
||||
v-for="(item, i) in nonServicePackageCartItems"
|
||||
:key="i"
|
||||
class="cart-item non-packaged">
|
||||
<div class="price-row">
|
||||
<span
|
||||
v-if="item.cartItemType === cartItemType.RECYCLE_FEE"
|
||||
id="recycle-fee-label">
|
||||
<textLink
|
||||
linkType="text"
|
||||
:text="item?.name ?? ''"
|
||||
href="javascript:void(0)"
|
||||
@clickEvent="openModal(RECYCLING_MODAL_REF_NAME)" />
|
||||
</span>
|
||||
<span v-else>{{ item?.name ?? '' }}</span>
|
||||
<span>{{ formatAmountInDollars(item?.subTotal ?? 0) }}</span>
|
||||
</div>
|
||||
<textLink
|
||||
v-if="item.cartItemType === cartItemType.VAP
|
||||
&& !readOnly"
|
||||
linkType="text"
|
||||
text="Remove"
|
||||
href="javascript:void(0)"
|
||||
@clickEvent="removeVap(item.partType)">
|
||||
<template #after-text>
|
||||
<span class="sr-only">{{ item?.name ?? '' }}</span>
|
||||
</template>
|
||||
</textLink>
|
||||
</div>
|
||||
|
||||
<!-- Packaged Cart Items -->
|
||||
<div
|
||||
v-for="(item, i) in servicePackageCartItems"
|
||||
:key="i"
|
||||
class="cart-item packaged">
|
||||
<div class="price-row">
|
||||
<span>{{ item?.name ?? '' }}</span>
|
||||
<span>{{ formatAmountInDollars(item?.subTotal ?? 0) }}</span>
|
||||
</div>
|
||||
<textLink
|
||||
v-if="!readOnly"
|
||||
linkType="text"
|
||||
text="Remove"
|
||||
href="javascript:void(0)"
|
||||
@clickEvent="removeVap(item.partType)">
|
||||
<template #after-text>
|
||||
<span class="sr-only">{{ item?.name ?? '' }}</span>
|
||||
</template>
|
||||
</textLink>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
id="cart-footer"
|
||||
class="cart-footer pb-5">
|
||||
<div
|
||||
id="cart-subtotal-and-tax"
|
||||
class="subtotal-and-tax">
|
||||
<div
|
||||
id="cart-subtotal"
|
||||
class="py-2 col d-flex justify-content-between">
|
||||
<span id="subtotal-label">{{ subtotalLabel }}</span>
|
||||
<span id="subtotal-value">{{ getDisplayed(subTotal) }}</span>
|
||||
</div>
|
||||
<div
|
||||
id="cart-sales-tax"
|
||||
class="py-2 col d-flex justify-content-between">
|
||||
<span id="sales-tax-label">{{ salesTaxLabel }}</span>
|
||||
<span id="sales-tax-value">{{ formatAmountInDollars(salesTax) }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="showAsPaid"
|
||||
id="cart-amount-paid"
|
||||
class="py-2 col d-flex justify-content-between">
|
||||
<span id="amount-paid-label">{{ amountPaidLabel }}</span>
|
||||
<span if="amount-paid-value">{{ getDisplayed(amountPaid) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
id="bottom-amount-due"
|
||||
class="bottom-amount-due col d-flex justify-content-between">
|
||||
<span id="bottom-amount-due-label">{{ amountDueLabel }}</span>
|
||||
<span id="bottom-amount-due-value">{{ getDisplayed(amountDue) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<contentGroupModal
|
||||
:ref="RECYCLING_MODAL_REF_NAME"
|
||||
:cmsWidgetName="recyclingModalCmsWidgetName">
|
||||
<textBlock cmsWidgetName="RecycleTextBlock" />
|
||||
</contentGroupModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -176,28 +133,26 @@ import {
|
|||
isOrderUnverified
|
||||
} from '@/helpers/cart-helper';
|
||||
import { getPriceOfLineItems, getTaxOfLineItems } from '@/helpers/price-calculator';
|
||||
import { processIfStatements } from '@/helpers/cms-content-helper';
|
||||
|
||||
const VERIFYING_COVERAGE = 'Verifying coverage';
|
||||
const RECYCLING_MODAL_REF_NAME = 'RecycleModal';
|
||||
|
||||
export default {
|
||||
name: 'cart-dropdown',
|
||||
name: 'confirmation-cart',
|
||||
components: {
|
||||
contentGroupModal,
|
||||
textBlock,
|
||||
textLink
|
||||
},
|
||||
props: {
|
||||
showAsPaid: Boolean,
|
||||
readOnly: Boolean,
|
||||
showAsPaid: Boolean,
|
||||
recyclingModalCmsWidgetName: String,
|
||||
showDropdownHeader: Boolean,
|
||||
isInitiallyExpanded: Boolean,
|
||||
submittedOrder: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isExpanded: this.isInitiallyExpanded,
|
||||
widget: {
|
||||
amountDue: 'AmountDueTextWidget',
|
||||
amountPaid: 'AmountPaidTextWidget',
|
||||
|
|
@ -208,7 +163,9 @@ export default {
|
|||
recycleFee: 'RecycleFeeWidget',
|
||||
mobileFee: 'MobileServiceWidget',
|
||||
servicePackage: 'ServicePackageTitle',
|
||||
vapsItemDescriptions: 'VapsItemDescriptions'
|
||||
vapsItemDescriptions: 'VapsItemDescriptions',
|
||||
warrantyText: 'WarrantyCartItemTextWidget',
|
||||
guaranteeText: 'GuaranteeCartItemTextWidget'
|
||||
},
|
||||
cartItemType,
|
||||
RECYCLING_MODAL_REF_NAME
|
||||
|
|
@ -273,7 +230,7 @@ export default {
|
|||
return result;
|
||||
},
|
||||
vehicleDamage() {
|
||||
return this.submittedOrder ? this.submittedOrder.damage : useMainStore().damage;
|
||||
return this.cartOrder.damage;
|
||||
},
|
||||
servicePackageTier() {
|
||||
const { glassToReplace, isRepair } = this.vehicleDamage;
|
||||
|
|
@ -333,6 +290,9 @@ export default {
|
|||
if (this.mobileFeeCartItem && !isMobileFeeHidden) {
|
||||
items.push(this.mobileFeeCartItem);
|
||||
}
|
||||
if (this.isITAC || this.isNoComp) {
|
||||
items.push(this.warrantyCartItem);
|
||||
}
|
||||
return items;
|
||||
},
|
||||
frontWipersCartItem() {
|
||||
|
|
@ -350,12 +310,6 @@ export default {
|
|||
...this.nonServicePackageCartItems
|
||||
];
|
||||
},
|
||||
packagePrice() {
|
||||
return this.servicePackageCartItems?.reduce(
|
||||
(accumulator, cartItem) => accumulator + (cartItem?.subTotal ?? 0),
|
||||
0
|
||||
) ?? 0;
|
||||
},
|
||||
amountDueLabel() {
|
||||
return this.getCmsContent(this.widget.amountDue, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
||||
},
|
||||
|
|
@ -366,7 +320,12 @@ export default {
|
|||
return this.getCmsContent(this.widget.deductible, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
||||
},
|
||||
basePriceLabel() {
|
||||
return this.getCmsContent(this.widget.basePrice, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
||||
const content = this.getCmsContent(this.widget.basePrice, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
||||
return processIfStatements(
|
||||
content,
|
||||
'custom',
|
||||
this.getCustomValueFromString
|
||||
);
|
||||
},
|
||||
subtotalLabel() {
|
||||
return this.getCmsContent(this.widget.subtotal, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
||||
|
|
@ -409,15 +368,29 @@ export default {
|
|||
partTypeStrings.MOBILE_FEE
|
||||
)
|
||||
: null;
|
||||
},
|
||||
warrantyCartItem() {
|
||||
const label = this.cartOrder.damage.glassToReplace?.length > 0
|
||||
? this.getCmsContent(this.widget.warrantyText, widgetFields.TEXT_BLOCK_WIDGET.TEXT)
|
||||
: this.getCmsContent(this.widget.guaranteeText, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
||||
return {
|
||||
name: label,
|
||||
cartItemType: cartItemType.WARRANTY,
|
||||
partType: partTypeStrings.WARRANTY,
|
||||
subTotal: 0,
|
||||
salesTax: 0
|
||||
}
|
||||
},
|
||||
hasMoldingPart() {
|
||||
return this.cartOrder.lineItems.glassParts?.some((glassPart) => {
|
||||
return glassPart.childParts?.some((childPart) => childPart.partType === partTypeStrings.MOLDING);
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openModal(modalName) {
|
||||
this.$refs[modalName].openModal();
|
||||
},
|
||||
toggleIsExpanded() {
|
||||
this.isExpanded = !this.isExpanded;
|
||||
},
|
||||
getDisplayed(amount) {
|
||||
return this.isUnverified
|
||||
? VERIFYING_COVERAGE
|
||||
|
|
@ -461,6 +434,16 @@ export default {
|
|||
const vapsTypeDescription = vapsItemDescriptions?.find((entry) => entry?.Name === vapsPartType);
|
||||
return vapsTypeDescription?.Text ?? '';
|
||||
},
|
||||
getCustomValueFromString(str) {
|
||||
switch (str) {
|
||||
case 'isRepair':
|
||||
return this.cartOrder.damage.isRepair;
|
||||
case 'hasMoldingPart':
|
||||
return this.hasMoldingPart;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
formatAmountInDollars
|
||||
}
|
||||
};
|
||||
|
|
@ -471,45 +454,38 @@ export default {
|
|||
|
||||
$RECYCLING_MODAL_REF_NAME: 'RecycleModal';
|
||||
|
||||
.force-no-top-margin {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
.color-black {
|
||||
color: $black;
|
||||
}
|
||||
|
||||
.color-green {
|
||||
color: $green;
|
||||
}
|
||||
|
||||
.color-darker-gray {
|
||||
color: $darker-gray;
|
||||
}
|
||||
|
||||
.cart-table {
|
||||
max-height: 0;
|
||||
transition: all 350ms ease-in;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
margin-top: 1rem;
|
||||
|
||||
:deep(a) {
|
||||
padding-bottom: 0 !important;
|
||||
line-height: 1.625rem !important;
|
||||
text-underline-offset: auto !important;
|
||||
}
|
||||
|
||||
.non-packaged-cart-item:nth-child(odd){
|
||||
background-color: $gray-100;
|
||||
}
|
||||
max-height: 50rem;
|
||||
transition: all 150ms ease-in;
|
||||
min-width: fit-content;
|
||||
|
||||
.cart-item {
|
||||
padding: 0.25rem 1.5rem;
|
||||
}
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: .5rem 0rem;
|
||||
width: 100%;
|
||||
min-width: fit-content;
|
||||
&:nth-child(odd) {
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 1px solid #d4d6d8;
|
||||
}
|
||||
|
||||
.cart-gray {
|
||||
background-color: $gray-100;
|
||||
.price-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
span, a {
|
||||
white-space: nowrap;
|
||||
padding: 0rem 1rem;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: $heritage-blue-secondary;
|
||||
}
|
||||
}
|
||||
|
||||
.cart-footer {
|
||||
|
|
@ -520,13 +496,16 @@ $RECYCLING_MODAL_REF_NAME: 'RecycleModal';
|
|||
background-color: $green-150;
|
||||
font-weight: $font-weight-bold;
|
||||
color: $black;
|
||||
padding: 0.25rem 1.5rem;
|
||||
>div {
|
||||
padding: .5rem 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-amount-due {
|
||||
color: $white;
|
||||
font-weight: 600;
|
||||
background-color: $green;
|
||||
padding: 0.5rem 1.5rem;
|
||||
padding: .5rem 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -544,39 +523,6 @@ $RECYCLING_MODAL_REF_NAME: 'RecycleModal';
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
.cart-header {
|
||||
&.cart-toggle {
|
||||
font-weight: $font-weight-bold;
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
transition: all 0.5s ease;
|
||||
background-image: url($svg-payment-method-review-toggle);
|
||||
background-repeat: no-repeat;
|
||||
background-position: right center;
|
||||
width: 1rem;
|
||||
height: 0.5625rem;
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
right: 0.75rem;
|
||||
margin: 0.5rem 0 0.5rem 1rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
&.expanded:after {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
&.expanded + .cart-table {
|
||||
max-height: 50rem;
|
||||
transition: all 150ms ease-in;
|
||||
overflow: hidden;
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(#recycle-fee-label a){
|
||||
line-height: initial;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,10 @@
|
|||
import calendarOptions from '@/constants/calendar-options';
|
||||
|
||||
// Supporting Files
|
||||
import { shallowMount, mount } from '@vue/test-utils';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
||||
import { useMainStore } from '@/store';
|
||||
import addToCalendar from '@/layouts/order-confirmation/add-to-calendar/add-to-calendar.vue';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
|
||||
const appointmentText = 'Appointment content';
|
||||
function setupMocks({ customMountOptions }) {
|
||||
|
|
@ -21,49 +20,6 @@ function setupMocks({ customMountOptions }) {
|
|||
}
|
||||
];
|
||||
const wrapper = shallowMount(addToCalendar, mountOptions);
|
||||
wrapper.vm.$refs.calendarModalQuestion.openModal = jest.fn();
|
||||
return { wrapper };
|
||||
}
|
||||
|
||||
const calendarModalQuestionStub = {
|
||||
render: () => {}
|
||||
};
|
||||
|
||||
const mockMixin = {
|
||||
methods: {
|
||||
getCmsContent: jest.fn().mockImplementation(() => appointmentText)
|
||||
}
|
||||
};
|
||||
|
||||
function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRun = () => {}, mixin = mockMixin) {
|
||||
const mountOptions = getMountOptions({
|
||||
router: {
|
||||
navigate: jest.fn(),
|
||||
navigateToExternalUrl: jest.fn()
|
||||
}
|
||||
});
|
||||
|
||||
mountOptions.global.stubs = {
|
||||
calendarModalQuestion: calendarModalQuestionStub
|
||||
};
|
||||
|
||||
const testingPinia = createTestingPinia({
|
||||
initialState: {
|
||||
main: mainInitialState
|
||||
}
|
||||
});
|
||||
useMainStore(testingPinia);
|
||||
methodToRun();
|
||||
|
||||
mountOptions.global.mixins[0].methods.getSettingValue = jest.fn(() => 'false');
|
||||
mountOptions.global.plugins = [testingPinia];
|
||||
mountOptions.mixins = [mixin];
|
||||
mountOptions.data = () => (
|
||||
initialData
|
||||
);
|
||||
|
||||
const wrapper = shallowMount(addToCalendar, mountOptions);
|
||||
wrapper.vm.$refs.calendarModalQuestion.openModal = jest.fn();
|
||||
return { wrapper };
|
||||
}
|
||||
|
||||
|
|
@ -119,51 +75,8 @@ afterEach(() => {
|
|||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
const sessionStorage = {
|
||||
schedule: {
|
||||
date: '2019-01-01',
|
||||
startTime: '09:00',
|
||||
endTime: '10:00',
|
||||
routeCode: '000'
|
||||
},
|
||||
lineItems: {
|
||||
glassParts: [
|
||||
{
|
||||
partNumber: 'ABC123'
|
||||
}
|
||||
],
|
||||
supportingItems: []
|
||||
},
|
||||
serviceLocation: {
|
||||
address: '',
|
||||
address2: null,
|
||||
city: '',
|
||||
state: 'AZ',
|
||||
appointmentType: 'Inshop',
|
||||
zipCode: '12345',
|
||||
zipCodeCtu: '01234',
|
||||
provider: {
|
||||
providerNumber: '123',
|
||||
address: {
|
||||
streetAddress: 'test1',
|
||||
city: 'test',
|
||||
state: 'AZ',
|
||||
zipCode: '12345',
|
||||
zipCodeCtu: '01234'
|
||||
}
|
||||
}
|
||||
},
|
||||
damage: {
|
||||
isRepair: false
|
||||
},
|
||||
referralNumber: '1234567',
|
||||
payment: {
|
||||
isInsurance: true
|
||||
}
|
||||
};
|
||||
|
||||
describe('Add-to-calendar methods...', () => {
|
||||
test('Add-to-calendar should trigger openModal method', () => {
|
||||
test('Add-to-calendar should trigger open options', async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({
|
||||
customMountOptions: {
|
||||
|
|
@ -183,10 +96,11 @@ describe('Add-to-calendar methods...', () => {
|
|||
});
|
||||
|
||||
// Act
|
||||
wrapper.vm.openCalendarModal();
|
||||
wrapper.vm.openCalendarOptions();
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.$refs.calendarModalQuestion.openModal).toBeCalled();
|
||||
expect(wrapper.vm.$refs.calendarOptions.open).toEqual(true);
|
||||
});
|
||||
|
||||
test('getCalendarData should return expected model value.', () => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="calendar-section">
|
||||
<div class="add-to-calendar">
|
||||
<button @click="openCalendarModal">
|
||||
<button @click="toggleCalendarOptionsOpen">
|
||||
<span class="add-to-calendar-span">
|
||||
<img
|
||||
src="@/assets/img/icons/add-to-calendar.svg"
|
||||
|
|
@ -10,14 +10,12 @@
|
|||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<calendarModalQuestion
|
||||
ref="calendarModalQuestion"
|
||||
v-model="selectedCalendarOption"
|
||||
customComponentId="calendarModalQuestion"
|
||||
cmsWidgetName="CalendarModalQuestion"
|
||||
<calendarOptionSelection
|
||||
ref="calendarOptions"
|
||||
:open="calendarOptionsOpen"
|
||||
:calendarOptionsData="calendarValues"
|
||||
@calendarModalClosed="calendarModalClosed">
|
||||
</calendarModalQuestion>
|
||||
@calendarOptionSelected="calendarOptionSelected">
|
||||
</calendarOptionSelection>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
|
@ -32,12 +30,12 @@ import { AppointmentTypeStrings, RouteCodeFlags } from '@/constants/schedule-con
|
|||
import { getCalendarFile, download } from '@/helpers/add-to-calendar-helper';
|
||||
import serviceType from '@/constants/service-type';
|
||||
import applicationConfig from '@/constants/application-config.js';
|
||||
import calendarModalQuestion from '@/layouts/order-confirmation/add-to-calendar/calendar-modal-question/calendar-modal-question.vue';
|
||||
import calendarOptionSelection from '@/layouts/order-confirmation/add-to-calendar/calendar-option-selection/calendar-option-selection.vue';
|
||||
|
||||
export default {
|
||||
name: 'add-to-calendar',
|
||||
components: {
|
||||
calendarModalQuestion
|
||||
calendarOptionSelection
|
||||
},
|
||||
props: {
|
||||
mobileWidgetName: String,
|
||||
|
|
@ -59,7 +57,7 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
selectedCalendarOption: null
|
||||
calendarOptionsOpen: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -84,7 +82,7 @@ export default {
|
|||
return this.getCmsContent(this.mobileWidgetName, 'BodyText');
|
||||
},
|
||||
AddToCalendar_InShop_Subject() {
|
||||
return this.getCmsContent('AddToCalendar_InShop', 'HeaderText');
|
||||
return this.getCmsContent(this.inShopWidgetName, 'HeaderText');
|
||||
},
|
||||
AddToCalendar_InShop_Body() {
|
||||
return this.getCmsContent(this.inShopWidgetName, 'BodyText');
|
||||
|
|
@ -192,14 +190,6 @@ export default {
|
|||
};
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
selectedCalendarOption(newValue) {
|
||||
if (newValue) {
|
||||
this.setCalendarAppointment(newValue);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
setCalendarAppointment(calendarOption) {
|
||||
if (
|
||||
|
|
@ -211,12 +201,20 @@ export default {
|
|||
window.open(calendarOption.url);
|
||||
}
|
||||
},
|
||||
calendarModalClosed() {
|
||||
// Clear the selectedOption if close the modal
|
||||
this.selectedCalendarOption = null;
|
||||
toggleCalendarOptionsOpen() {
|
||||
if (this.calendarOptionsOpen) {
|
||||
this.closeCalendarOptions();
|
||||
} else {
|
||||
this.openCalendarOptions();
|
||||
}
|
||||
},
|
||||
openCalendarModal() {
|
||||
this.$refs.calendarModalQuestion.openModal();
|
||||
openCalendarOptions() {
|
||||
this.calendarOptionsOpen = true;
|
||||
window.addEventListener('click', this.clickListener);
|
||||
},
|
||||
calendarOptionSelected(calendarOptionIndex) {
|
||||
this.setCalendarAppointment(this.calendarValues[calendarOptionIndex]);
|
||||
this.closeCalendarOptions();
|
||||
},
|
||||
getCalendarData(type) {
|
||||
let URL = '';
|
||||
|
|
@ -253,29 +251,39 @@ export default {
|
|||
};
|
||||
const calBody = getCalendarFile(calFile);
|
||||
download('SafeliteAppointment.ics', calBody);
|
||||
},
|
||||
closeCalendarOptions() {
|
||||
this.calendarOptionsOpen = false;
|
||||
window.removeEventListener('click', this.clickListener);
|
||||
},
|
||||
clickListener(event) {
|
||||
const calendarSection = document.querySelector('.calendar-section');
|
||||
if (calendarSection && !calendarSection.contains(event.target)) {
|
||||
this.closeCalendarOptions();
|
||||
}
|
||||
}
|
||||
},
|
||||
unmounted() {
|
||||
window.removeEventListener('click', this.clickListener);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
<style scoped lang="scss">
|
||||
div.calendar-section {
|
||||
position: relative;
|
||||
top: 17px;
|
||||
display: inline;
|
||||
cursor: pointer;
|
||||
}
|
||||
.add-to-calendar-text {
|
||||
margin: 0 0 0 0.4375rem;
|
||||
color: #1574a1;
|
||||
color: $heritage-blue-secondary;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.add-to-calendar button {
|
||||
border: 0;
|
||||
background-color: #fff;
|
||||
}
|
||||
.calendar-section {
|
||||
display: table;
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.calendar-section .add-to-calendar {
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
.calendar-section .add-to-calendar .add-to-calendar-span {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,81 +0,0 @@
|
|||
<template>
|
||||
<baseInputButton
|
||||
v-bind="$props"
|
||||
v-model="selectedValue"
|
||||
buttonWrapperClasses="list-group base-input-button list-button rounded-3 d-flex flex-column w-100 mb-2">
|
||||
<div
|
||||
:aria-label="buttonLabel"
|
||||
class="button-content list-button-content d-flex flex-column justify-content-center py-3 px-4">
|
||||
<span
|
||||
class="m-0 position-relative"
|
||||
:class="textPosition">
|
||||
<img :src="buttonLabel" />
|
||||
<span class="calendar-text">{{ value }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</baseInputButton>
|
||||
</template>
|
||||
<script>
|
||||
import baseInputButton from '@/digital-components/base-input-button/base-input-button.vue';
|
||||
import inputButtonWrapperMixin from '@/mixins/input-button-wrapper-mixin.js';
|
||||
|
||||
export default {
|
||||
name: 'calendar-modal-list-button',
|
||||
components: {
|
||||
baseInputButton
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.list-button {
|
||||
outline: none;
|
||||
input[type="radio"],
|
||||
input[type="checkbox"] {
|
||||
position: static; //override bootstrap
|
||||
|
||||
&:focus-visible + .list-button-content {
|
||||
box-shadow: 0 0 0 2.5px $blue;
|
||||
}
|
||||
&:focus + .list-button-content {
|
||||
box-shadow: 0 0 0 2.5px $blue;
|
||||
}
|
||||
&:checked + .list-button-content {
|
||||
color: $black;
|
||||
font-weight: 500;
|
||||
background: $blue-100;
|
||||
box-shadow: 0 0 0 1px $blue;
|
||||
}
|
||||
&:checked:focus + .list-button-content {
|
||||
box-shadow: 0 0 0 2.5px $blue;
|
||||
}
|
||||
&:checked + .list-button-content p,
|
||||
&:checked + .list-button-content span {
|
||||
font-weight: 500;
|
||||
}
|
||||
&:checked + .list-button-content span:nth-child(2) {
|
||||
font-weight: 400;
|
||||
color: $gray-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
.list-button-content {
|
||||
color: $gray-600;
|
||||
position: relative;
|
||||
background: $white;
|
||||
transition: all 150ms linear;
|
||||
border-radius: $border-radius-lg;
|
||||
border: 0.063rem solid $gray-500;
|
||||
width: 100%;
|
||||
outline: none;
|
||||
|
||||
span.calendar-text {
|
||||
margin-left: 0.313rem;
|
||||
padding: 0.125rem 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.position-relative {
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import calendarModalQuestion from '@/layouts/order-confirmation/add-to-calendar/calendar-modal-question/calendar-modal-question.vue';
|
||||
|
||||
describe('calendar-modal-question.vue', () => {
|
||||
test('When selected calendar option is changed, a correctly selectedCalendarOption should be emitted', async () => {
|
||||
// Arrange
|
||||
const url = 'iCal url';
|
||||
const name = 'iCal';
|
||||
const icon = 'iCal.svg';
|
||||
const expectedEmit = [
|
||||
[
|
||||
{
|
||||
url,
|
||||
name,
|
||||
icon
|
||||
}
|
||||
]
|
||||
];
|
||||
|
||||
const wrapper = shallowMount(calendarModalQuestion, {
|
||||
propsData: {
|
||||
calendarOptionsData: [
|
||||
{
|
||||
url,
|
||||
name,
|
||||
icon
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
wrapper.vm.$refs.calendarOptions.closeModal = jest.fn();
|
||||
wrapper.vm.selectedCalendarOption = name;
|
||||
|
||||
// Act
|
||||
wrapper.vm.setSelectedCalendarOption();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.emitted()['update:modelValue']).toEqual(expectedEmit);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,180 +0,0 @@
|
|||
<template>
|
||||
<modal
|
||||
:ref="modalName"
|
||||
:fontSize="true"
|
||||
:headerText="modalHeaderText"
|
||||
:footerButtonText="footerCloseButtonText"
|
||||
:onModalClosedCallback="onModalClosed"
|
||||
class="calendar-modal"
|
||||
@isModalOpened="setModalStatus"
|
||||
@footerButtonEvent="setSelectedCalendarOption">
|
||||
<template v-if="isModalOpened">
|
||||
<buttonQuestion
|
||||
ref="buttonQuestion"
|
||||
v-model="selectedCalendarOption"
|
||||
buttonTypeString="calendarModalListButton"
|
||||
:buttonTypeObject="calendarModalListButton"
|
||||
:answers="getCalendarOptionData"
|
||||
groupName="chooseCalendarType"
|
||||
class="mt-1"
|
||||
textPosition="text-center"
|
||||
isRequired
|
||||
validationRules="calendar-option-required" />
|
||||
</template>
|
||||
</modal>
|
||||
</template>
|
||||
<script>
|
||||
import modal from '@/digital-components/modal/modal.vue';
|
||||
import buttonQuestion from '@/digital-components/button-question/button-question.vue';
|
||||
import { defineRule, useField } from 'vee-validate';
|
||||
import errorMessages from '@/constants/error-messages.js';
|
||||
import { required } from '@/helpers/validation-rules';
|
||||
import { deepClone } from '@/helpers/object-helper';
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import calendarModalListButton from './calendar-modal-list-button/calendar-modal-list-button.vue';
|
||||
// Validation for the modal button
|
||||
defineRule('calendar-option-required', required(errorMessages.OPTION_REQUIRED));
|
||||
export default {
|
||||
name: 'calendar-modal-question',
|
||||
components: {
|
||||
modal,
|
||||
buttonQuestion
|
||||
},
|
||||
props: {
|
||||
calendarOptionsData: Object,
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
url: null,
|
||||
name: null,
|
||||
icon: null
|
||||
})
|
||||
}
|
||||
},
|
||||
emits: ['calendar-modal-closed', 'update:modelValue'],
|
||||
setup(props) {
|
||||
const uuid = uuidv4();
|
||||
const componentId = !props.customComponentId
|
||||
? `component-${uuid}`
|
||||
: props.customComponentId;
|
||||
|
||||
const { modelValue } = deepClone(props);
|
||||
const initialValue = modelValue;
|
||||
|
||||
const fieldOptions = {
|
||||
value: modelValue,
|
||||
initialValue
|
||||
};
|
||||
|
||||
const { errorMessage, handleChange, meta, validate, errors } = useField(
|
||||
componentId,
|
||||
props.validationRules,
|
||||
fieldOptions
|
||||
);
|
||||
|
||||
return {
|
||||
componentId,
|
||||
errorMessage,
|
||||
handleChange,
|
||||
validate,
|
||||
meta,
|
||||
errors
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isModalOpened: false,
|
||||
selectedValue: null,
|
||||
calendarModalListButton
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
modalName() {
|
||||
return 'calendarOptions';
|
||||
},
|
||||
modalHeaderText() {
|
||||
return 'Add to Calendar';
|
||||
},
|
||||
footerCloseButtonText() {
|
||||
return 'Add to Calendar';
|
||||
},
|
||||
modal() {
|
||||
return this.$refs[this.modalName];
|
||||
},
|
||||
selectedCalendarOption: {
|
||||
get() {
|
||||
return this.modelValue?.name;
|
||||
},
|
||||
set(newValue) {
|
||||
// Button Question only supports Number, or String data types so we must convert to the full object before emitting
|
||||
this.selectedValue = this.getSelectedCalendarObject(newValue);
|
||||
}
|
||||
},
|
||||
getCalendarOptionData() {
|
||||
return this.calendarOptionsData.map((item) => ({
|
||||
value: item.name,
|
||||
buttonLabel: item.icon
|
||||
}));
|
||||
},
|
||||
watch: {
|
||||
modelValue: {
|
||||
handler(newValue) {
|
||||
this.handleChange(newValue);
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openModal() {
|
||||
this.modal.openModal();
|
||||
},
|
||||
setModalStatus(isOpened) {
|
||||
this.isModalOpened = isOpened;
|
||||
},
|
||||
closeModal() {
|
||||
this.modal.closeModal();
|
||||
},
|
||||
onModalClosed() {
|
||||
this.$emit('calendar-modal-closed');
|
||||
},
|
||||
async setSelectedCalendarOption() {
|
||||
this.$emit('update:modelValue', this.selectedValue);
|
||||
this.closeModal();
|
||||
},
|
||||
|
||||
getSelectedCalendarObject(calendarOptionId) {
|
||||
// eslint-disable-next-line no-shadow
|
||||
const calendarOption = this.calendarOptionsData?.find((calendarOption) => calendarOption.name === calendarOptionId);
|
||||
|
||||
if (calendarOption) {
|
||||
return {
|
||||
url: calendarOption.url,
|
||||
name: calendarOption.name,
|
||||
icon: calendarOption.icon
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
url: null,
|
||||
name: null,
|
||||
icon: null
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.calendar-modal.modal.modal-component {
|
||||
> .modal-dialog > .modal-content {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
.modal-header {
|
||||
padding-bottom: 0;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<template>
|
||||
<a
|
||||
:aria-label="buttonLabel"
|
||||
class="d-flex flex-row option-button">
|
||||
<img :src="buttonLabel" />
|
||||
<span class="calendar-text">{{ value }}</span>
|
||||
</a>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'calendar-option-button',
|
||||
props: {
|
||||
buttonLabel: String,
|
||||
value: String
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.option-button {
|
||||
text-decoration: none;
|
||||
color: #4d4e53;
|
||||
font-weight: 400;
|
||||
padding: 0.5rem;
|
||||
border-radius: 4px;
|
||||
padding: .75rem 3.125rem .0625rem 1rem;
|
||||
height: 2.75rem;
|
||||
|
||||
&:hover {
|
||||
background-color: $background-color-selected;
|
||||
}
|
||||
img {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
.calendar-text {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import calendarOptionSelection from '@/layouts/order-confirmation/add-to-calendar/calendar-option-selection/calendar-option-selection.vue';
|
||||
|
||||
describe('calendar-option-selection.vue', () => {
|
||||
test('When selected calendar option is changed, a correctly selectedCalendarOption should be emitted', async () => {
|
||||
// Arrange
|
||||
const url = 'iCal url';
|
||||
const name = 'iCal';
|
||||
const icon = 'iCal.svg';
|
||||
const url2 = 'Google url';
|
||||
const name2 = 'Google';
|
||||
const icon2 = 'Google.svg';
|
||||
const expectedEmit = [
|
||||
[
|
||||
1
|
||||
]
|
||||
];
|
||||
|
||||
const wrapper = shallowMount(calendarOptionSelection, {
|
||||
propsData: {
|
||||
calendarOptionsData: [
|
||||
{
|
||||
url,
|
||||
name,
|
||||
icon
|
||||
},
|
||||
{
|
||||
url: url2,
|
||||
name: name2,
|
||||
icon: icon2
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
// Act
|
||||
wrapper.vm.setSelectedCalendarOption(1);
|
||||
|
||||
// Assert
|
||||
expect(wrapper.emitted()['calendar-option-selected']).toEqual(expectedEmit);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
|
||||
<template>
|
||||
<div
|
||||
v-if="open"
|
||||
class="calendar-selection">
|
||||
<template
|
||||
v-for="(option, index) in getCalendarOptionData"
|
||||
:key="option.value">
|
||||
<calendarOptionButton
|
||||
:buttonLabel="option.buttonLabel"
|
||||
:value="option.value"
|
||||
@click="setSelectedCalendarOption(index)" />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import calendarOptionButton from './calendar-option-button/calendar-option-button.vue';
|
||||
export default {
|
||||
name: 'calendar-option-selection',
|
||||
components: {
|
||||
calendarOptionButton
|
||||
},
|
||||
props: {
|
||||
calendarOptionsData: Array,
|
||||
open: Boolean
|
||||
},
|
||||
emits: ['calendar-option-selected'],
|
||||
computed: {
|
||||
getCalendarOptionData() {
|
||||
return this.calendarOptionsData.map((item) => ({
|
||||
value: item.name,
|
||||
buttonLabel: item.icon
|
||||
}));
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async setSelectedCalendarOption(index) {
|
||||
this.$emit('calendar-option-selected', index);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.calendar-selection {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
display: table;
|
||||
border: solid 1px #d4d6d8;
|
||||
border-radius: .25rem;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0 0 10px 0 rgba(88, 188, 233, 0.2);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -206,6 +206,7 @@ describe('OrderConfirmation.vue', () => {
|
|||
useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => sessionStorage);
|
||||
};
|
||||
const { wrapper } = getMountedComponent(initialStore, {}, mockStoreActions);
|
||||
console.log(wrapper.vm.$refs);
|
||||
|
||||
// Act
|
||||
const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
|
@ -363,7 +364,7 @@ describe('OrderConfirmation.vue', () => {
|
|||
expect(actualEmail).toBe(emailAddress);
|
||||
});
|
||||
});
|
||||
describe('confirmationEmailText', () => {
|
||||
describe('orderConfirmationUpdateAppointmentText', () => {
|
||||
const greaterId = '>';
|
||||
const lessId = '<';
|
||||
test.each([
|
||||
|
|
@ -383,7 +384,7 @@ describe('OrderConfirmation.vue', () => {
|
|||
const { wrapper } = getMountedComponent(initialStore, {}, mockStoreActions);
|
||||
|
||||
// Act
|
||||
const text = wrapper.vm.confirmationEmailText;
|
||||
const text = wrapper.vm.orderConfirmationUpdateAppointmentText;
|
||||
|
||||
// Assert
|
||||
expect(text).toBe(expected);
|
||||
|
|
@ -391,13 +392,13 @@ describe('OrderConfirmation.vue', () => {
|
|||
});
|
||||
describe('appointmentTimeText', () => {
|
||||
test.each([
|
||||
['Between 9 AM - 10 AM', '09:00', '10:00'],
|
||||
['Between 1 PM - 5 PM', '13:00', '17:00'],
|
||||
['Between 8:30 AM - 3 PM', '08:30', '15:00'],
|
||||
['Between 1 AM - 2 AM', '01:00', '02:00'],
|
||||
['Between 12 AM - 2 AM', '00:00', '02:00'],
|
||||
['Between 12 PM - 2 AM', '12:00', '02:00'],
|
||||
['Between 11 PM - 12 AM', '23:00', '00:00']
|
||||
['Your appointment is at 9 AM - 10 AM', '09:00', '10:00'],
|
||||
['Your appointment is at 1 PM - 5 PM', '13:00', '17:00'],
|
||||
['Your appointment is at 8:30 AM - 3 PM', '08:30', '15:00'],
|
||||
['Your appointment is at 1 AM - 2 AM', '01:00', '02:00'],
|
||||
['Your appointment is at 12 AM - 2 AM', '00:00', '02:00'],
|
||||
['Your appointment is at 12 PM - 2 AM', '12:00', '02:00'],
|
||||
['Your appointment is at 11 PM - 12 AM', '23:00', '00:00']
|
||||
])('should return Mobile time in expected format "%p" when start time "%p" and end time "%p"', (expected, startTime, endTime) => {
|
||||
// Arrange
|
||||
const order = deepClone(sessionStorage);
|
||||
|
|
@ -438,12 +439,12 @@ describe('OrderConfirmation.vue', () => {
|
|||
expect(testValue).toEqual('Drop off before 9:30 AM');
|
||||
});
|
||||
test.each([
|
||||
['at 9:00 AM', '09:00', '10:00'],
|
||||
['at 1:00 PM', '13:00', '17:00'],
|
||||
['at 8:30 AM', '08:30', '15:00'],
|
||||
['at 1:00 AM', '01:00', '02:00'],
|
||||
['at 12:00 AM', '00:00', '02:00'],
|
||||
['at 12:00 PM', '12:00', '02:00']
|
||||
['Your appointment is at 9:00 AM', '09:00', '10:00'],
|
||||
['Your appointment is at 1:00 PM', '13:00', '17:00'],
|
||||
['Your appointment is at 8:30 AM', '08:30', '15:00'],
|
||||
['Your appointment is at 1:00 AM', '01:00', '02:00'],
|
||||
['Your appointment is at 12:00 AM', '00:00', '02:00'],
|
||||
['Your appointment is at 12:00 PM', '12:00', '02:00']
|
||||
])('should return In Shop time in expected format "%p" when start time "%p"', (expected, startTime, endTime) => {
|
||||
// Arrange
|
||||
const order = deepClone(sessionStorage);
|
||||
|
|
@ -479,8 +480,8 @@ describe('OrderConfirmation.vue', () => {
|
|||
}
|
||||
}
|
||||
};
|
||||
const expectedServiceLocation = '123 Service Rd., Box 4,<br/> Columbus, OH 12345';
|
||||
const expectedShopLocation = '304 Provider Ln.,<br/> Pittsburg, PA 16001';
|
||||
const expectedServiceLocation = '123 Service Rd., Box 4, Columbus, OH 12345';
|
||||
const expectedShopLocation = '304 Provider Ln., Pittsburg, PA 16001';
|
||||
test.each([
|
||||
AppointmentTypeStrings.MOBILE,
|
||||
AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP
|
||||
|
|
@ -554,7 +555,7 @@ describe('OrderConfirmation.vue', () => {
|
|||
useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order);
|
||||
};
|
||||
const { wrapper } = getMountedComponent({}, {}, mockStoreActions);
|
||||
const expected = '123 Service Rd., Box 4,<br/> Columbus, OH 12345';
|
||||
const expected = '123 Service Rd., Box 4, Columbus, OH 12345';
|
||||
|
||||
// Act
|
||||
const text = wrapper.vm.serviceLocationFullAddress;
|
||||
|
|
@ -568,7 +569,7 @@ describe('OrderConfirmation.vue', () => {
|
|||
useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order);
|
||||
};
|
||||
const { wrapper } = getMountedComponent({}, {}, mockStoreActions);
|
||||
const expected = ', Box 4,<br/> Columbus, OH 12345';
|
||||
const expected = ', Box 4, Columbus, OH 12345';
|
||||
|
||||
// Act
|
||||
const text = wrapper.vm.serviceLocationFullAddress;
|
||||
|
|
@ -582,7 +583,7 @@ describe('OrderConfirmation.vue', () => {
|
|||
useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order);
|
||||
};
|
||||
const { wrapper } = getMountedComponent({}, {}, mockStoreActions);
|
||||
const expected = '123 Service Rd., <br/> Columbus, OH 12345';
|
||||
const expected = '123 Service Rd., Columbus, OH 12345';
|
||||
|
||||
// Act
|
||||
const text = wrapper.vm.serviceLocationFullAddress;
|
||||
|
|
@ -596,7 +597,7 @@ describe('OrderConfirmation.vue', () => {
|
|||
useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order);
|
||||
};
|
||||
const { wrapper } = getMountedComponent({}, {}, mockStoreActions);
|
||||
const expected = '123 Service Rd., Box 4,<br/> , OH 12345';
|
||||
const expected = '123 Service Rd., Box 4, , OH 12345';
|
||||
|
||||
// Act
|
||||
const text = wrapper.vm.serviceLocationFullAddress;
|
||||
|
|
@ -610,7 +611,7 @@ describe('OrderConfirmation.vue', () => {
|
|||
useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order);
|
||||
};
|
||||
const { wrapper } = getMountedComponent({}, {}, mockStoreActions);
|
||||
const expected = '123 Service Rd., Box 4,<br/> Columbus, 12345';
|
||||
const expected = '123 Service Rd., Box 4, Columbus, 12345';
|
||||
|
||||
// Act
|
||||
const text = wrapper.vm.serviceLocationFullAddress;
|
||||
|
|
@ -624,7 +625,7 @@ describe('OrderConfirmation.vue', () => {
|
|||
useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order);
|
||||
};
|
||||
const { wrapper } = getMountedComponent({}, {}, mockStoreActions);
|
||||
const expected = '123 Service Rd., Box 4,<br/> Columbus, OH ';
|
||||
const expected = '123 Service Rd., Box 4, Columbus, OH ';
|
||||
|
||||
// Act
|
||||
const text = wrapper.vm.serviceLocationFullAddress;
|
||||
|
|
@ -642,7 +643,7 @@ describe('OrderConfirmation.vue', () => {
|
|||
useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order);
|
||||
};
|
||||
const { wrapper } = getMountedComponent({}, {}, mockStoreActions);
|
||||
const expected = ', <br/> , ';
|
||||
const expected = ', , ';
|
||||
|
||||
// Act
|
||||
const text = wrapper.vm.serviceLocationFullAddress;
|
||||
|
|
@ -675,7 +676,7 @@ describe('OrderConfirmation.vue', () => {
|
|||
useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order);
|
||||
};
|
||||
const { wrapper } = getMountedComponent({}, {}, mockStoreActions);
|
||||
const expected = '304 Provider Ln.,<br/> Pittsburg, PA 16001';
|
||||
const expected = '304 Provider Ln., Pittsburg, PA 16001';
|
||||
|
||||
// Act
|
||||
const text = wrapper.vm.providerFullAddress;
|
||||
|
|
@ -703,7 +704,7 @@ describe('OrderConfirmation.vue', () => {
|
|||
useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order);
|
||||
};
|
||||
const { wrapper } = getMountedComponent({}, {}, mockStoreActions);
|
||||
const expected = '304 Provider Ln.,<br/> , PA 16001';
|
||||
const expected = '304 Provider Ln., , PA 16001';
|
||||
|
||||
// Act
|
||||
const text = wrapper.vm.providerFullAddress;
|
||||
|
|
@ -717,7 +718,7 @@ describe('OrderConfirmation.vue', () => {
|
|||
useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order);
|
||||
};
|
||||
const { wrapper } = getMountedComponent({}, {}, mockStoreActions);
|
||||
const expected = '304 Provider Ln.,<br/> Pittsburg, 16001';
|
||||
const expected = '304 Provider Ln., Pittsburg, 16001';
|
||||
|
||||
// Act
|
||||
const text = wrapper.vm.providerFullAddress;
|
||||
|
|
@ -731,7 +732,7 @@ describe('OrderConfirmation.vue', () => {
|
|||
useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order);
|
||||
};
|
||||
const { wrapper } = getMountedComponent({}, {}, mockStoreActions);
|
||||
const expected = '304 Provider Ln.,<br/> Pittsburg, PA ';
|
||||
const expected = '304 Provider Ln., Pittsburg, PA ';
|
||||
|
||||
// Act
|
||||
const text = wrapper.vm.providerFullAddress;
|
||||
|
|
@ -956,9 +957,9 @@ describe('OrderConfirmation.vue', () => {
|
|||
describe('Methods', () => {
|
||||
describe('formatDate', () => {
|
||||
test.each([
|
||||
['Wednesday, April 22', '2020-04-22'],
|
||||
['Thursday, February 1', '2018-02-01'],
|
||||
['Wednesday, December 30', '2026-12-30']
|
||||
['Wednesday, April 22, 2020', '2020-04-22'],
|
||||
['Thursday, February 1, 2018', '2018-02-01'],
|
||||
['Wednesday, December 30, 2026', '2026-12-30']
|
||||
])('returns %p given %p', (expected, date) => {
|
||||
// Arrange
|
||||
const order = deepClone(sessionStorage);
|
||||
|
|
|
|||
|
|
@ -8,21 +8,34 @@
|
|||
<div class="justify-content-center">
|
||||
<siteHeader :cmsWidgetName="widgets.siteHeader" />
|
||||
</div>
|
||||
<div class="iss-heritage-container-width">
|
||||
<div class="order-confirmation-container iss-heritage-content-container-width">
|
||||
<div class="text-center text-color--black pt-5 pb-5 fs-5">
|
||||
<div class="iss-heritage-container-width order-confirmation-container row">
|
||||
<div class="appointment-info-container col-lg-7">
|
||||
<div class="header">
|
||||
<img :src="orderConfirmationImage" />
|
||||
<span
|
||||
class="ms-2"
|
||||
class="header-text"
|
||||
v-html="orderConfirmationHeaderText"></span>
|
||||
</div>
|
||||
<div class="appointment-details">
|
||||
<div class="appointment-date-time text-center mt-4">
|
||||
<p>{{ formatDate(schedule.date) }}</p>
|
||||
<p class="mt-2">
|
||||
{{ appointmentTimeText }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="subheader">
|
||||
<span
|
||||
class="subheader-text"
|
||||
v-html="orderConfirmationSubheaderText"></span>
|
||||
<span
|
||||
class="update-appointment-text"
|
||||
v-html="orderConfirmationUpdateAppointmentText"></span>
|
||||
</div>
|
||||
<div class="service-description confirmation-section">
|
||||
<span>{{ serviceDescriptionText }}</span>
|
||||
</div>
|
||||
<div class="work-order-details confirmation-section">
|
||||
<span class="work-order-title">{{ workOrderTitle }}</span>
|
||||
<span class="work-order-description">{{ workOrderNumber }}</span>
|
||||
</div>
|
||||
<div class="appointment-details confirmation-section">
|
||||
<span class="appointment-title">{{ appointmentWordingText }}</span>
|
||||
<span>{{ formatDate(schedule.date) }}</span>
|
||||
<span>{{ appointmentTimeText }}</span>
|
||||
<span class="appointment-location">{{ appointmentLocation }}</span>
|
||||
<addToCalendar
|
||||
mobileWidgetName="AddToCalendar_Mobile"
|
||||
inShopWidgetName="AddToCalendar_InShop"
|
||||
|
|
@ -40,29 +53,32 @@
|
|||
:hasRecalibrationPart="hasRecalibrationPart"
|
||||
:uniqueId="referralNumber"
|
||||
:isRepair="isRepair" />
|
||||
<div
|
||||
class="appointment-text text-center lh-base"
|
||||
v-html="appointmentWordingText"></div>
|
||||
<div
|
||||
class="appointment-text text-center lh-base mt-2"
|
||||
v-html="appointmentWordingText2"></div>
|
||||
</div>
|
||||
<hr class="mb-0" />
|
||||
<div>
|
||||
<cartDropdown
|
||||
v-if="showCart"
|
||||
:showAsPaid="payment.isPayInAdvance"
|
||||
:readOnly="true"
|
||||
:isInitiallyExpanded="false"
|
||||
:showDropdownHeader="true"
|
||||
recyclingModalCmsWidgetName="RecycleModal"
|
||||
servicePackageTitleWidgetName="ServicePackageTitle"
|
||||
:submittedOrder="submittedOrder" />
|
||||
<div
|
||||
v-if="displayWipers"
|
||||
class="wiper-details confirmation-section">
|
||||
<span class="wipers-title">{{ wipersTitle }}</span>
|
||||
<span
|
||||
v-for="(description, index) in wipersBody"
|
||||
:key="index"
|
||||
class="wipers-body">{{ description }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="displayRainRepel"
|
||||
class="rain-repel-details confirmation-section">
|
||||
<span class="rain-repel-title">{{ rainRepelTitle }}</span>
|
||||
<span class="rain-repel-body">{{ rainRepelBody }}</span>
|
||||
</div>
|
||||
<hr class="mt-0 mb-5" />
|
||||
<div
|
||||
class="email-confirmation-text"
|
||||
v-html="confirmationEmailText" />
|
||||
v-if="displaySMSPhone"
|
||||
class="sms-phone-details confirmation-section">
|
||||
<span class="sms-phone-title">{{ smsTitle }}</span>
|
||||
<span class="sms-phone-body">{{ contactInfo.servicePhone }}</span>
|
||||
</div>
|
||||
<div class="contact-details confirmation-section">
|
||||
<span class="contact-title">{{ contactDetailsTitle }}</span>
|
||||
<span class="contact-body">{{ contactDetailsBody }}</span>
|
||||
</div>
|
||||
<siteFooter
|
||||
v-if="carrierUrl"
|
||||
ref="siteFooter"
|
||||
|
|
@ -72,6 +88,18 @@
|
|||
@ForwardClicked="forwardButtonAction"
|
||||
@backClicked="navigateBack" />
|
||||
</div>
|
||||
<div class="cart-container col-lg-4 offset-lg-1 col-md-5">
|
||||
<div class="cart-header">
|
||||
{{ cartHeaderText }}
|
||||
</div>
|
||||
<cartDropdown
|
||||
v-if="showCart"
|
||||
:showAsPaid="payment.isPayInAdvance"
|
||||
:readOnly="true"
|
||||
recyclingModalCmsWidgetName="RecycleModal"
|
||||
servicePackageTitleWidgetName="ServicePackageTitle"
|
||||
:submittedOrder="submittedOrder" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
|
|
@ -103,6 +131,9 @@ import { toTitleCase } from '@/helpers/text-helper.js';
|
|||
import { AppointmentTypeStrings } from '@/constants/schedule-constants';
|
||||
import applicationConfig from '@/constants/application-config';
|
||||
import widgetFields from '@/constants/cms-widget-fields.js';
|
||||
import { getGlassList } from '@/helpers/damage-helper';
|
||||
import partTypeStrings from '@/constants/part-type-strings';
|
||||
import coverageType from '@/constants/coverage-type';
|
||||
|
||||
export default {
|
||||
name: 'order-confirmation',
|
||||
|
|
@ -144,18 +175,21 @@ export default {
|
|||
serviceLocation,
|
||||
schedule,
|
||||
customer,
|
||||
contactInfo,
|
||||
payment,
|
||||
customerPortalLoginToken,
|
||||
damage,
|
||||
referralNumber
|
||||
referralNumber,
|
||||
hasRecalibrationPart
|
||||
} = this.submittedOrder;
|
||||
const { issConfig, hasRecalibrationPart } = this.mainStore;
|
||||
const { issConfig } = this.mainStore;
|
||||
return {
|
||||
vehicle,
|
||||
customerEmail: customer?.emailAddress,
|
||||
payment,
|
||||
schedule,
|
||||
serviceLocation,
|
||||
contactInfo,
|
||||
appointmentType: serviceLocation.appointmentType,
|
||||
providerAddress: serviceLocation?.provider?.address,
|
||||
customerPortalLoginToken,
|
||||
|
|
@ -164,12 +198,23 @@ export default {
|
|||
isRepair: damage.isRepair,
|
||||
hasRecalibrationPart,
|
||||
referralNumber: referralNumber?.toString(),
|
||||
isNoComp: this.submittedOrder.insuranceCoverage.coverageType === coverageType.NO_COMP,
|
||||
widgets: {
|
||||
siteHeader: 'SiteHeaderWidget',
|
||||
emailConfirmation: 'EmailConfirmationWordingWidget',
|
||||
emailConfirmationNoComp: 'EmailConfirmationNoCompWordingWidget',
|
||||
orderConfirmation: 'OrderConfirmationContent',
|
||||
serviceDescription: 'ServiceDescriptionTextWidget',
|
||||
workOrderNumberTitle: 'WorkOrderNumberTextWidget',
|
||||
mobile: 'MobileWordingWidget',
|
||||
dropOffAndInShop: 'DropOffAndInShopWordingWidget',
|
||||
wipersText: 'WipersTextWidget',
|
||||
rainRepel: 'RainRepelWidget',
|
||||
smsUpdates: 'SmsUpdatesWidget',
|
||||
contactDetails: 'ContactDetailsWidget',
|
||||
vapsItemDescriptions: 'VapsItemDescriptions',
|
||||
payAtAppointment: 'PayAtAppointmentTextWidget',
|
||||
orderDetails: 'OrderDetailsTextWidget',
|
||||
siteFooter: 'SiteFooterWidget'
|
||||
}
|
||||
};
|
||||
|
|
@ -182,18 +227,29 @@ export default {
|
|||
vehicleYear: this.vehicle?.year,
|
||||
vehicleMake: this.vehicle?.make,
|
||||
vehicleModel: this.vehicle?.model,
|
||||
serviceType: this.serviceTypeText,
|
||||
address: this.appointmentLocation,
|
||||
inShopDuration: this.inShopAppointmentDuration,
|
||||
email: this.customerEmail,
|
||||
phone: this.contactInfo.servicePhone,
|
||||
workOrderNumber: this.workOrderNumber,
|
||||
CUSTOMER_PORTAL_URL: applicationConfig.CUSTOMER_PORTAL_URL,
|
||||
CUSTOMER_PORTAL_LOGIN_TOKEN: this.customerPortalLoginToken
|
||||
};
|
||||
},
|
||||
confirmationEmailText() {
|
||||
const content = this.getCmsContentWithCustomValues(
|
||||
this.widgets.emailConfirmation,
|
||||
widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT
|
||||
);
|
||||
orderConfirmationUpdateAppointmentText() {
|
||||
let content = '';
|
||||
if(this.isNoComp) {
|
||||
content = this.getCmsContentWithCustomValues(
|
||||
this.widgets.emailConfirmationNoComp,
|
||||
widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT
|
||||
);
|
||||
} else {
|
||||
content = this.getCmsContentWithCustomValues(
|
||||
this.widgets.emailConfirmation,
|
||||
widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT
|
||||
);
|
||||
}
|
||||
return content
|
||||
?.replaceAll('<', '<')
|
||||
?.replaceAll('>', '>');
|
||||
|
|
@ -210,10 +266,51 @@ export default {
|
|||
widgetFields.CONTENT_GROUP_WIDGET.IMAGE
|
||||
);
|
||||
},
|
||||
orderConfirmationSubheaderText() {
|
||||
return this.getCmsContent(
|
||||
this.widgets.orderConfirmation,
|
||||
widgetFields.CONTENT_GROUP_WIDGET.SUBHEADER_TEXT
|
||||
);
|
||||
},
|
||||
serviceDescriptionText() {
|
||||
const content = this.getCmsContentWithCustomValues(
|
||||
this.widgets.serviceDescription,
|
||||
widgetFields.TEXT_BLOCK_WIDGET.TEXT
|
||||
);
|
||||
return content;
|
||||
},
|
||||
serviceTypeText() {
|
||||
const glassPieces = this.submittedOrder.lineItems.glassParts?.map((part) => part.partType.toLowerCase()) ?? [];
|
||||
let glassList = getGlassList(glassPieces);
|
||||
let workType = '';
|
||||
if (this.isRepair) {
|
||||
workType = 'repair';
|
||||
} else {
|
||||
workType = 'replacement';
|
||||
if (this.hasRecalibrationPart) {
|
||||
if (glassList === 'windshield') {
|
||||
workType = 'replacement and recalibration';
|
||||
}
|
||||
else {
|
||||
glassList = glassList.replace('windshield', 'windshield replacement, recalibration');
|
||||
}
|
||||
}
|
||||
}
|
||||
return `${glassList} ${workType}`;
|
||||
},
|
||||
workOrderTitle() {
|
||||
return this.getCmsContent(
|
||||
this.widgets.workOrderNumberTitle,
|
||||
widgetFields.TEXT_BLOCK_WIDGET.TEXT
|
||||
);
|
||||
},
|
||||
workOrderNumber() {
|
||||
return this.submittedOrder.workOrderNumber;
|
||||
},
|
||||
mobileWordingText() {
|
||||
return this.getCmsContentWithCustomValues(
|
||||
this.widgets.mobile,
|
||||
widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT
|
||||
widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT
|
||||
);
|
||||
},
|
||||
mobileWordingText2() {
|
||||
|
|
@ -225,7 +322,7 @@ export default {
|
|||
nonMobileWordingText() {
|
||||
return this.getCmsContentWithCustomValues(
|
||||
this.widgets.dropOffAndInShop,
|
||||
widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT
|
||||
widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT
|
||||
);
|
||||
},
|
||||
nonMobileWordingText2() {
|
||||
|
|
@ -241,12 +338,12 @@ export default {
|
|||
}
|
||||
if (this.isInShopAppointment) {
|
||||
const formattedStartTime = get12HourTimeFormat(startTime);
|
||||
return `at ${formattedStartTime}`;
|
||||
return `Your appointment is at ${formattedStartTime}`;
|
||||
}
|
||||
if (this.isMobileAppointment) {
|
||||
const mobileStartTime = get12HourTimeMobileFormat(startTime);
|
||||
const mobileEndTime = get12HourTimeMobileFormat(endTime);
|
||||
return `Between ${mobileStartTime} - ${mobileEndTime}`;
|
||||
return `Your appointment is at ${mobileStartTime} - ${mobileEndTime}`;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
|
@ -261,12 +358,12 @@ export default {
|
|||
},
|
||||
serviceLocationFullAddress() {
|
||||
const { address, address2, city, state, zipCode } = this.serviceLocation;
|
||||
return `${address ?? ''}, ${address2 ? `${address2},` : ''}<br/> ${city ?? ''}, ${state ?? ''} ${zipCode ?? ''}`;
|
||||
return `${address ?? ''}, ${address2 ? `${address2},` : ''} ${city ?? ''}, ${state ?? ''} ${zipCode ?? ''}`;
|
||||
},
|
||||
providerFullAddress() {
|
||||
const { streetAddress, city, state, zipCode } = this.providerAddress;
|
||||
return streetAddress
|
||||
? `${toTitleCase(streetAddress)},<br/> ${toTitleCase(city)}, ${state ?? ''} ${zipCode ?? ''}`
|
||||
? `${toTitleCase(streetAddress)}, ${toTitleCase(city)}, ${state ?? ''} ${zipCode ?? ''}`
|
||||
: '';
|
||||
},
|
||||
appointmentWordingText() {
|
||||
|
|
@ -318,6 +415,84 @@ export default {
|
|||
// settleTenderAmount always shows 0 via localhost or dev.
|
||||
// Temporarily set return true to see cart in localhost or dev environment
|
||||
return false;
|
||||
},
|
||||
displayWipers() {
|
||||
const hasWiperPart = this.submittedOrder.lineItems.vaps.some((part) => part.partType.toLowerCase().includes('wiper'));
|
||||
return hasWiperPart;
|
||||
},
|
||||
wipersTitle() {
|
||||
return this.getCmsContent(
|
||||
this.widgets.wipersText,
|
||||
widgetFields.TEXT_BLOCK_WIDGET.TEXT
|
||||
);
|
||||
},
|
||||
wipersBody() {
|
||||
const wiperTypesOnOrder = [];
|
||||
|
||||
const hasFrontWiper = this.submittedOrder.lineItems.vaps.some((part) => part.partType === partTypeStrings.FRONT_WIPER);
|
||||
const hasRearWiper = this.submittedOrder.lineItems.vaps.some((part) => part.partType === partTypeStrings.REAR_WIPER);
|
||||
|
||||
if (hasFrontWiper) {
|
||||
wiperTypesOnOrder.push(partTypeStrings.FRONT_WIPER);
|
||||
}
|
||||
if (hasRearWiper) {
|
||||
wiperTypesOnOrder.push(partTypeStrings.REAR_WIPER);
|
||||
}
|
||||
|
||||
const wiperDescriptions = wiperTypesOnOrder.map((type) => {
|
||||
return this.getCmsContentForVapsType(type);
|
||||
});
|
||||
|
||||
return wiperDescriptions;
|
||||
},
|
||||
displayRainRepel() {
|
||||
return this.submittedOrder.lineItems.vaps.some((part) => part.partType === partTypeStrings.RAIN_DEFENSE);
|
||||
},
|
||||
rainRepelTitle() {
|
||||
return this.getCmsContent(
|
||||
this.widgets.rainRepel,
|
||||
widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT
|
||||
);
|
||||
},
|
||||
rainRepelBody() {
|
||||
return this.getCmsContent(
|
||||
this.widgets.rainRepel,
|
||||
widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT
|
||||
);
|
||||
},
|
||||
displaySMSPhone() {
|
||||
return this.contactInfo.requestTextUpdates && this.contactInfo.servicePhone;
|
||||
},
|
||||
smsTitle() {
|
||||
return this.getCmsContent(
|
||||
this.widgets.smsUpdates,
|
||||
widgetFields.TEXT_BLOCK_WIDGET.TEXT
|
||||
);
|
||||
},
|
||||
contactDetailsTitle() {
|
||||
return this.getCmsContent(
|
||||
this.widgets.contactDetails,
|
||||
widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT
|
||||
);
|
||||
},
|
||||
contactDetailsBody() {
|
||||
return this.getCmsContentWithCustomValues(
|
||||
this.widgets.contactDetails,
|
||||
widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT
|
||||
);
|
||||
},
|
||||
cartHeaderText() {
|
||||
if (this.payment.isPayInAdvance) {
|
||||
return this.getCmsContent(
|
||||
this.widgets.orderDetails,
|
||||
widgetFields.TEXT_BLOCK_WIDGET.TEXT
|
||||
);
|
||||
} else {
|
||||
return this.getCmsContent(
|
||||
this.widgets.payAtAppointment,
|
||||
widgetFields.TEXT_BLOCK_WIDGET.TEXT
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
|
@ -411,68 +586,138 @@ export default {
|
|||
return dateObject.toLocaleDateString('en-us', {
|
||||
weekday: 'long',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
day: 'numeric',
|
||||
year: 'numeric'
|
||||
});
|
||||
},
|
||||
getCmsContentForVapsType(vapsPartType) {
|
||||
const vapsItemDescriptions = this.getCmsContent(
|
||||
this.widgets.vapsItemDescriptions,
|
||||
widgetFields.INPUT_QUESTION_WIDGET.ANSWERS
|
||||
);
|
||||
|
||||
if (!vapsItemDescriptions) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const vapsTypeDescription = vapsItemDescriptions?.find((entry) => entry?.Name === vapsPartType);
|
||||
return vapsTypeDescription?.Text ?? '';
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.iss-heritage-container-width {
|
||||
.order-confirmation-container {
|
||||
position: relative;
|
||||
min-height: 1px;
|
||||
padding-left: .9375rem;
|
||||
padding-right: .9375rem;
|
||||
.order-confirmation-container {
|
||||
.appointment-info-container
|
||||
{
|
||||
padding: 0 .9375rem;
|
||||
margin-bottom: 1.875rem;
|
||||
}
|
||||
}
|
||||
|
||||
.page-container-grouped-styles {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.text-color--black {
|
||||
color: $black;
|
||||
}
|
||||
|
||||
.appointment-details {
|
||||
margin-bottom: 1.5rem;
|
||||
padding: 1rem 1.5rem 1.5rem;
|
||||
box-shadow: 0 0.188rem 0.625rem rgb(0 0 0 / 0.2);
|
||||
border-radius: 0.313rem;
|
||||
}
|
||||
|
||||
.appointment-date-time P {
|
||||
color: $black;
|
||||
font-size: $h5-font-size;
|
||||
line-height: map-get($spacers, 6);
|
||||
margin-bottom: 0;
|
||||
|
||||
+ p {
|
||||
font-size: map-get($spacers, 4);
|
||||
line-height: 1.625rem;
|
||||
font-weight: $font-weight-bold;
|
||||
.cart-container {
|
||||
padding: 0 .9375rem;
|
||||
}
|
||||
}
|
||||
|
||||
.appointment-text {
|
||||
:deep(p) {
|
||||
margin: 0;
|
||||
}
|
||||
:deep(strong) {
|
||||
font-weight: $font-weight-bold;
|
||||
.header {
|
||||
flex-direction: row;
|
||||
margin-top: 1.25rem;
|
||||
margin-bottom: .625rem;
|
||||
font-size: 1.25rem;
|
||||
color: $black;
|
||||
}
|
||||
}
|
||||
|
||||
.email-confirmation-text {
|
||||
:deep(a) {
|
||||
font-weight: $font-weight-bold;
|
||||
text-decoration: underline;
|
||||
img {
|
||||
width: 2.25rem;
|
||||
height: 2.25rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
:deep(strong) {
|
||||
font-weight: $font-weight-bold;
|
||||
|
||||
.subheader {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 1.25rem;
|
||||
|
||||
.subheader-text {
|
||||
margin-bottom: .625rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
color: $black;
|
||||
}
|
||||
|
||||
.update-appointment-text {
|
||||
:deep(.text-primary) {
|
||||
color: $heritage-blue-primary;
|
||||
}
|
||||
:deep(a) {
|
||||
font-weight: $font-weight-bold;
|
||||
color: $heritage-blue-primary;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
color: $heritage-blue-secondary;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.confirmation-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1.875rem 0;
|
||||
border-bottom: 1px solid lightgray;
|
||||
|
||||
>:first-child {
|
||||
font-weight: 600;
|
||||
color: $black;
|
||||
}
|
||||
}
|
||||
|
||||
.service-description {
|
||||
>span:first-child {
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
}
|
||||
|
||||
.work-order-title {
|
||||
margin-bottom: .625rem;
|
||||
}
|
||||
|
||||
.appointment-details {
|
||||
padding-bottom: 2.0625rem;
|
||||
|
||||
.appointment-title {
|
||||
margin-bottom: .625rem;
|
||||
}
|
||||
|
||||
.appointment-location {
|
||||
margin-top: .625rem;
|
||||
}
|
||||
}
|
||||
|
||||
.rain-repel-details {
|
||||
.rain-repel-title {
|
||||
color: #4d4e53;
|
||||
}
|
||||
}
|
||||
|
||||
.sms-phone-details {
|
||||
.sms-phone-title {
|
||||
color: #4d4e53;
|
||||
}
|
||||
}
|
||||
|
||||
.contact-details {
|
||||
.contact-title {
|
||||
margin-bottom: .625rem;
|
||||
}
|
||||
}
|
||||
|
||||
.cart-header {
|
||||
margin-top: 1.25rem;
|
||||
margin-bottom: .625rem;
|
||||
color: $black;
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -24,8 +24,6 @@
|
|||
<cartDropdown
|
||||
:showAsPaid="false"
|
||||
:readOnly="false"
|
||||
:showDropdownHeader="true"
|
||||
:isInitiallyExpanded="false"
|
||||
recyclingModalCmsWidgetName="RecycleModal"
|
||||
servicePackageTitleWidgetName="ServicePackageTitle" />
|
||||
<hr class="mt-0 mb-5" />
|
||||
|
|
|
|||
|
|
@ -42,8 +42,7 @@
|
|||
<cartDropdown
|
||||
ref="cart"
|
||||
:readOnly="true"
|
||||
:showDropdownHeader="true"
|
||||
:isInitiallyExpanded="true"
|
||||
:showAsPaid="false"
|
||||
recyclingModalCmsWidgetName="RecycleModal"
|
||||
servicePackageTitleWidgetName="ServicePackageTitle" />
|
||||
<hr class="my-0">
|
||||
|
|
|
|||
|
|
@ -299,6 +299,11 @@ export const useMainStore = defineStore({
|
|||
const nonWindshieldItems = damage.glassToReplace?.filter((glass) => glass.glassLocation !== damageLocationsSelected.WINDSHIELD);
|
||||
return !!nonWindshieldItems?.length;
|
||||
},
|
||||
hasMoldingPart: (s) => {
|
||||
return s.order.lineItems.glassParts?.some((glassPart) => {
|
||||
return glassPart.childParts?.some((childPart) => childPart.partType === partTypeStrings.MOLDING);
|
||||
});
|
||||
},
|
||||
isMobileAppointment: (state) => state.order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE
|
||||
|| state.order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP,
|
||||
isDropOffAppointment: (state) => state.order.serviceLocation.appointmentType === AppointmentTypeStrings.DROP_OFF,
|
||||
|
|
@ -2886,12 +2891,13 @@ export const useMainStore = defineStore({
|
|||
await this.getCarrierAccountInfo();
|
||||
const submittedOrder = this.order;
|
||||
const { experiments } = this.applicationUser;
|
||||
const { issConfig } = this;
|
||||
const { issConfig, hasRecalibrationPart } = this;
|
||||
|
||||
submittedOrder.isUnverified = this.isUnverified;
|
||||
submittedOrder.isVerified = this.isVerified;
|
||||
submittedOrder.submitType = submitType;
|
||||
submittedOrder.payment.isPayInAdvance = this.isPayInAdvance;
|
||||
submittedOrder.hasRecalibrationPart = hasRecalibrationPart;
|
||||
|
||||
// set to sessionStorage
|
||||
window.sessionStorage.setItem(webStorageConstants.SUBMITTED_ORDER, JSON.stringify(submittedOrder));
|
||||
|
|
|
|||
Loading…
Reference in a new issue