Merge pull request #623 from Safelite/feature/SSR-1114

add cart to order-confirmation page
This commit is contained in:
katiekroell 2024-04-19 07:46:33 -04:00 committed by GitHub
commit fef57f7a26
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 244 additions and 41 deletions

View file

@ -11,6 +11,7 @@ Object {
"isExpanded": false, "isExpanded": false,
"widget": Object { "widget": Object {
"amountDue": "AmountDueTextWidget", "amountDue": "AmountDueTextWidget",
"amountPaid": "AmountPaidTextWidget",
"basePrice": "BasePriceWidget", "basePrice": "BasePriceWidget",
"deductible": "DeductibleWidget", "deductible": "DeductibleWidget",
"mobileFee": "MobileServiceWidget", "mobileFee": "MobileServiceWidget",

View file

@ -253,6 +253,27 @@ describe('cart-dropdown component', () => {
// Assert // Assert
expect(salesTax.exists()).toBeTruthy(); expect(salesTax.exists()).toBeTruthy();
}); });
test('amount paid when Pay in Advance', () => {
// Arrange
const reference = '#cart-amount-paid';
const isExpanded = true;
const showAsPaid = true;
const initialPropsData = { isExpanded, showAsPaid };
const storeData = {
order: {
payment: {
isPayInAdvance: true
}
}
};
const { wrapper } = getMountedComponent(storeData, {}, initialPropsData);
// Act
const amountPaid = wrapper.find(reference);
// Assert
expect(amountPaid.exists()).toBeTruthy();
});
test('bottom amount due', () => { test('bottom amount due', () => {
// Arrange // Arrange
const reference = '#bottom-amount-due'; const reference = '#bottom-amount-due';
@ -631,7 +652,13 @@ describe('cart-dropdown component', () => {
vaps: null vaps: null
}, },
payment: { payment: {
insuranceCoverage: { isVerified: true } insuranceCoverage: {
coverageStatus: coverageStatuses.VERIFIED
}
},
policy: {
noCoverage: false,
isITAC: false
} }
}, },
issConfig: { issConfig: {
@ -669,7 +696,9 @@ describe('cart-dropdown component', () => {
] ]
}, },
payment: { payment: {
insuranceCoverage: { isVerified: true } insuranceCoverage: {
coverageStatus: coverageStatuses.VERIFIED
}
} }
}, },
issConfig: { issConfig: {
@ -1784,7 +1813,13 @@ describe('cart-dropdown component', () => {
isNoComp: false, isNoComp: false,
policyLookupSuccessful: true policyLookupSuccessful: true
}, },
currentDeductible: 321 currentDeductible: 321,
policyLookupSuccessful: true,
payment: {
insuranceCoverage: {
coverageStatus: coverageStatuses.VERIFIED
}
}
} }
}; };
const { wrapper } = getMountedComponent(storeData); const { wrapper } = getMountedComponent(storeData);

View file

@ -129,6 +129,13 @@
<span id="sales-tax-label">{{ salesTaxLabel }}</span> <span id="sales-tax-label">{{ salesTaxLabel }}</span>
<span id="sales-tax-value">{{ formatAmountInDollars(salesTax) }}</span> <span id="sales-tax-value">{{ formatAmountInDollars(salesTax) }}</span>
</div> </div>
<div
v-if="showAsPaid"
id="cart-amount-paid"
class="pt-1 col d-flex justify-content-between">
<span id="amount-paid-label">{{ amountPaidLabel }}</span>
<span if="amount-paid-value">{{ getDisplayed(amountPaid) }}</span>
</div>
</div> </div>
<div <div
id="bottom-amount-due" id="bottom-amount-due"
@ -176,14 +183,16 @@ export default {
readOnly: Boolean, readOnly: Boolean,
recyclingModalCmsWidgetName: String, recyclingModalCmsWidgetName: String,
showDropdownHeader: Boolean, showDropdownHeader: Boolean,
isInitiallyExpanded: Boolean isInitiallyExpanded: Boolean,
submittedOrder: Object
}, },
data() { data() {
return { return {
isExpanded: this.isInitiallyExpanded, isExpanded: this.isInitiallyExpanded,
availableVaps: useMainStore().order.availableVaps, availableVaps: this.submittedOrder ? this.submittedOrder.lineItems.vaps : useMainStore().order.availableVaps,
widget: { widget: {
amountDue: 'AmountDueTextWidget', amountDue: 'AmountDueTextWidget',
amountPaid: 'AmountPaidTextWidget',
deductible: 'DeductibleWidget', deductible: 'DeductibleWidget',
basePrice: 'BasePriceWidget', basePrice: 'BasePriceWidget',
subtotal: 'SubtotalWidget', subtotal: 'SubtotalWidget',
@ -199,17 +208,20 @@ export default {
computed: { computed: {
deductible() { deductible() {
// Note: added as computed so it can be used in the template. // Note: added as computed so it can be used in the template.
return useMainStore().order.currentDeductible; return this.submittedOrder ? this.submittedOrder.currentDeductible : useMainStore().order.currentDeductible;
}, },
showDeductibleCartItem() { showDeductibleCartItem() {
return !useMainStore().isNoComp && !useMainStore().isITAC; return !this.isNoComp && !this.isITAC;
},
lineItems() {
return this.submittedOrder ? this.submittedOrder.lineItems : useMainStore().lineItems;
}, },
recycleFeeLineItem() { recycleFeeLineItem() {
return useMainStore().lineItems.supportingItems return this.lineItems.supportingItems
?.find((lineItem) => lineItem.partNumber === partNumberStrings.RECYCLE_FEE); ?.find((lineItem) => lineItem.partNumber === partNumberStrings.RECYCLE_FEE);
}, },
baseServiceLineItems() { baseServiceLineItems() {
const { supportingItems, glassParts, otherParts } = useMainStore().lineItems; const { supportingItems, glassParts, otherParts } = this.lineItems;
const parts = [ const parts = [
...(supportingItems ?? []), ...(supportingItems ?? []),
...(glassParts ?? []), ...(glassParts ?? []),
@ -220,8 +232,21 @@ export default {
baseServicePrice() { baseServicePrice() {
return getPriceOfLineItems(this.baseServiceLineItems) ?? 0; return getPriceOfLineItems(this.baseServiceLineItems) ?? 0;
}, },
isVerifiedCoverageStatus() {
return this.submittedOrder ? this.submittedOrder.payment.insuranceCoverage.isVerified : useMainStore().isVerifiedCoverageStatus;
},
isUnverified() {
return !this.isNoComp && !this.isITAC
&& (this.deductible == null || !this.isVerifiedCoverageStatus);
},
isITAC() {
return this.submittedOrder ? this.submittedOrder.policy.isITAC : useMainStore().isITAC;
},
isNoComp() {
return this.submittedOrder ? this.submittedOrder.policy.noCoverage : useMainStore().isNoComp;
},
subTotal() { subTotal() {
const { supportingItems, glassParts, otherParts, vaps, mobileFee } = useMainStore().lineItems; const { supportingItems, glassParts, otherParts, vaps, mobileFee } = this.lineItems;
const allLineItems = [ const allLineItems = [
...(supportingItems ?? []), ...(supportingItems ?? []),
...(glassParts ?? []), ...(glassParts ?? []),
@ -229,12 +254,12 @@ export default {
...(vaps ?? []), ...(vaps ?? []),
mobileFee mobileFee
]; ];
return !useMainStore().isNoComp && !useMainStore().isITAC return !this.isNoComp && !this.isITAC
? this.deductible + getPriceOfLineItems([...this.feeLineItems, ...(vaps ?? [])]) ? this.deductible + getPriceOfLineItems([...this.feeLineItems, ...(vaps ?? [])])
: getPriceOfLineItems(allLineItems); : getPriceOfLineItems(allLineItems);
}, },
feeLineItems() { feeLineItems() {
const { mobileFee } = useMainStore().lineItems; const { mobileFee } = this.lineItems;
const result = []; const result = [];
if (mobileFee) { if (mobileFee) {
result.push(mobileFee); result.push(mobileFee);
@ -251,8 +276,8 @@ export default {
let result = 0; let result = 0;
if (!useMainStore().isUnverified) { if (!this.isUnverified) {
if (useMainStore().isITAC || useMainStore().isNoComp) { if (this.isITAC || this.isNoComp) {
result += sumTax(this.baseServiceLineItems); result += sumTax(this.baseServiceLineItems);
} else { } else {
// deductible-case need to show tax for Recycle Fee // deductible-case need to show tax for Recycle Fee
@ -260,7 +285,7 @@ export default {
} }
} }
result += sumTax(useMainStore().lineItems.vaps ?? []); result += sumTax(this.lineItems.vaps ?? []);
return result; return result;
}, },
@ -269,8 +294,13 @@ export default {
? 0 ? 0
: this.subTotal + this.salesTax; : this.subTotal + this.salesTax;
}, },
amountPaid() {
return !this.showAsPaid
? 0
: this.subTotal + this.salesTax;
},
availableLineItems() { availableLineItems() {
const { supportingItems, glassParts, otherParts, mobileFee } = useMainStore().lineItems; const { supportingItems, glassParts, otherParts, mobileFee } = this.lineItems;
const result = [ const result = [
...(supportingItems ?? []), ...(supportingItems ?? []),
...(glassParts ?? []), ...(glassParts ?? []),
@ -282,9 +312,12 @@ export default {
} }
return result; return result;
}, },
vehicleDamage() {
return this.submittedOrder ? this.submittedOrder.damage : useMainStore().damage;
},
servicePackageTier() { servicePackageTier() {
const { glassToReplace, isRepair } = useMainStore().damage; const { glassToReplace, isRepair } = this.vehicleDamage;
const { vaps } = useMainStore().lineItems; const { vaps } = this.lineItems;
return getHighestFullySatisfiedTier( return getHighestFullySatisfiedTier(
glassToReplace ?? [], glassToReplace ?? [],
this.availableLineItems, this.availableLineItems,
@ -293,7 +326,7 @@ export default {
); );
}, },
partTypesInServicePackage() { partTypesInServicePackage() {
const { glassToReplace, isRepair } = useMainStore().damage; const { glassToReplace, isRepair } = this.vehicleDamage;
return getPackageContents( return getPackageContents(
glassToReplace ?? [], glassToReplace ?? [],
this.availableLineItems, this.availableLineItems,
@ -315,7 +348,7 @@ export default {
return items; return items;
}, },
nonServicePackageCartItems() { nonServicePackageCartItems() {
const vapPartTypesInOrder = Array.from(new Set(useMainStore().lineItems.vaps?.map((vap) => vap.partType) ?? [])); const vapPartTypesInOrder = Array.from(new Set(this.lineItems.vaps?.map((vap) => vap.partType) ?? []));
const vapPartTypesInOrderButNotPackage = vapPartTypesInOrder const vapPartTypesInOrderButNotPackage = vapPartTypesInOrder
.filter((partType) => !this.partTypesInServicePackage.includes(partType)) .filter((partType) => !this.partTypesInServicePackage.includes(partType))
?? []; ?? [];
@ -358,6 +391,9 @@ export default {
amountDueLabel() { amountDueLabel() {
return this.getCmsContent(this.widget.amountDue, widgetFields.TEXT_BLOCK_WIDGET.TEXT); return this.getCmsContent(this.widget.amountDue, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
}, },
amountPaidLabel() {
return this.getCmsContent(this.widget.amountPaid, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
},
deductibleLabel() { deductibleLabel() {
return this.getCmsContent(this.widget.deductible, widgetFields.TEXT_BLOCK_WIDGET.TEXT); return this.getCmsContent(this.widget.deductible, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
}, },
@ -396,7 +432,7 @@ export default {
: null; : null;
}, },
mobileFeeCartItem() { mobileFeeCartItem() {
const mobileFeeLineItem = useMainStore().lineItems.mobileFee; const mobileFeeLineItem = this.lineItems.mobileFee;
return mobileFeeLineItem return mobileFeeLineItem
? this.getCartItem( ? this.getCartItem(
this.getCmsContent(this.widget.mobileFee, widgetFields.TEXT_BLOCK_WIDGET.TEXT), this.getCmsContent(this.widget.mobileFee, widgetFields.TEXT_BLOCK_WIDGET.TEXT),
@ -415,9 +451,9 @@ export default {
this.isExpanded = !this.isExpanded; this.isExpanded = !this.isExpanded;
}, },
getDisplayed(amount) { getDisplayed(amount) {
return useMainStore().isUnverified return this.isUnverified
&& !useMainStore().isNoComp && !this.isNoComp
&& !useMainStore().isITAC && !this.isITAC
? VERIFYING_COVERAGE ? VERIFYING_COVERAGE
: formatAmountInDollars(amount); : formatAmountInDollars(amount);
}, },
@ -441,7 +477,7 @@ export default {
}, },
getCartItemForVapsPart(partType) { getCartItemForVapsPart(partType) {
const label = this.getCmsContentForVapsType(partType); const label = this.getCmsContentForVapsType(partType);
const lineItems = useMainStore().lineItems.vaps const lineItems = this.lineItems.vaps
?.filter((vapsLineItem) => vapsLineItem.partType === partType) ?? []; ?.filter((vapsLineItem) => vapsLineItem.partType === partType) ?? [];
return this.getCartItem(label, lineItems, cartItemType.VAP, partType); return this.getCartItem(label, lineItems, cartItemType.VAP, partType);
}, },

View file

@ -14,7 +14,8 @@ jest.mock('@/helpers/layout-helper.js', () => jest.fn());
jest.mock('@/helpers/cms-content-helper', () => ({ jest.mock('@/helpers/cms-content-helper', () => ({
fetchCmsContentForPage: jest.fn(), fetchCmsContentForPage: jest.fn(),
processIfStatements: jest.fn() processIfStatements: jest.fn(),
splitCopyOnCMSPlaceHolder: jest.fn()
})); }));
const wordingText = 'wording text {custom:address}'; const wordingText = 'wording text {custom:address}';
@ -105,7 +106,17 @@ const sessionStorage = {
zipCode: '12345' zipCode: '12345'
} }
} }
} },
payment: {
isPayInAdvance: false
},
lineItems: {
vaps: []
},
policy: {
noCoverage: false
},
damage: {}
}; };
const sessionStorageMock = (() => { const sessionStorageMock = (() => {
@ -302,7 +313,17 @@ describe('OrderConfirmation.vue', () => {
}, },
serviceLocation: { serviceLocation: {
appointmentType: 'Mobile' appointmentType: 'Mobile'
} },
payment: {
isPayInAdvance: false
},
lineItems: {
vaps: []
},
policy: {
noCoverage: false
},
damage: {}
}; };
window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage));
const { wrapper } = getMountedComponent(); const { wrapper } = getMountedComponent();
@ -331,7 +352,17 @@ describe('OrderConfirmation.vue', () => {
zipCode: '12345' zipCode: '12345'
} }
} }
} },
payment: {
isPayInAdvance: false
},
lineItems: {
vaps: []
},
policy: {
noCoverage: false
},
damage: {}
}; };
window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage));
const { wrapper } = getMountedComponent(); const { wrapper } = getMountedComponent();
@ -368,7 +399,17 @@ describe('OrderConfirmation.vue', () => {
state: 'AZ', state: 'AZ',
zipCode: '12345', zipCode: '12345',
appointmentType: 'Mobile' appointmentType: 'Mobile'
} },
payment: {
isPayInAdvance: false
},
lineItems: {
vaps: []
},
policy: {
noCoverage: false
},
damage: {}
}; };
window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage));
const { wrapper } = getMountedComponent(); const { wrapper } = getMountedComponent();
@ -397,7 +438,17 @@ describe('OrderConfirmation.vue', () => {
} }
}, },
appointmentType: 'Dropoff' appointmentType: 'Dropoff'
} },
payment: {
isPayInAdvance: false
},
lineItems: {
vaps: []
},
policy: {
noCoverage: false
},
damage: {}
}; };
window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage));
const { wrapper } = getMountedComponent(); const { wrapper } = getMountedComponent();
@ -426,7 +477,17 @@ describe('OrderConfirmation.vue', () => {
} }
}, },
appointmentType: 'Inshop' appointmentType: 'Inshop'
} },
payment: {
isPayInAdvance: false
},
lineItems: {
vaps: []
},
policy: {
noCoverage: false
},
damage: {}
}; };
window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage));
const { wrapper } = getMountedComponent(); const { wrapper } = getMountedComponent();
@ -452,7 +513,17 @@ describe('OrderConfirmation.vue', () => {
state: 'AZ', state: 'AZ',
zipCode: '12345', zipCode: '12345',
appointmentType: 'Mobile' appointmentType: 'Mobile'
} },
payment: {
isPayInAdvance: false
},
lineItems: {
vaps: []
},
policy: {
noCoverage: false
},
damage: {}
}; };
window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage));
const { wrapper } = getMountedComponent(); const { wrapper } = getMountedComponent();
@ -481,7 +552,17 @@ describe('OrderConfirmation.vue', () => {
} }
}, },
appointmentType: 'Inshop' appointmentType: 'Inshop'
} },
payment: {
isPayInAdvance: false
},
lineItems: {
vaps: []
},
policy: {
noCoverage: false
},
damage: {}
}; };
window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage));
const { wrapper } = getMountedComponent(); const { wrapper } = getMountedComponent();
@ -507,7 +588,17 @@ describe('OrderConfirmation.vue', () => {
state: 'AZ', state: 'AZ',
zipCode: '12345', zipCode: '12345',
appointmentType: 'Mobile' appointmentType: 'Mobile'
} },
payment: {
isPayInAdvance: false
},
lineItems: {
vaps: []
},
policy: {
noCoverage: false
},
damage: {}
}; };
window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage));
const { wrapper } = getMountedComponent(); const { wrapper } = getMountedComponent();
@ -536,7 +627,17 @@ describe('OrderConfirmation.vue', () => {
} }
}, },
appointmentType: 'Dropoff' appointmentType: 'Dropoff'
} },
payment: {
isPayInAdvance: false
},
lineItems: {
vaps: []
},
policy: {
noCoverage: false
},
damage: {}
}; };
window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage));
const { wrapper } = getMountedComponent(); const { wrapper } = getMountedComponent();
@ -566,7 +667,17 @@ describe('OrderConfirmation.vue', () => {
} }
}, },
appointmentType: 'Inshop' appointmentType: 'Inshop'
} },
payment: {
isPayInAdvance: false
},
lineItems: {
vaps: []
},
policy: {
noCoverage: false
},
damage: {}
}; };
window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage));
const { wrapper } = getMountedComponent(); const { wrapper } = getMountedComponent();

View file

@ -47,6 +47,14 @@
v-html="appointmentWordingText2"> v-html="appointmentWordingText2">
</div> </div>
</div> </div>
<div>
<cartDropdown
:showAsPaid="isPayInAdvance"
:readOnly="true"
recyclingModalCmsWidgetName="RecycleModal"
servicePackageTitleWidgetName="ServicePackageTitle"
:submittedOrder="submittedOrder" />
</div>
<div <div
class="email-confirmation-text" class="email-confirmation-text"
v-html="confirmationEmailText" /> v-html="confirmationEmailText" />
@ -69,6 +77,8 @@ import siteHeader from '@/iss-components/site-header/site-header.vue';
import vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner.vue'; import vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner.vue';
import siteFooter from '@/iss-components/site-footer/site-footer.vue'; import siteFooter from '@/iss-components/site-footer/site-footer.vue';
import addToCalendar from '@/layouts/order-confirmation/add-to-calendar/add-to-calendar.vue'; import addToCalendar from '@/layouts/order-confirmation/add-to-calendar/add-to-calendar.vue';
import cartDropdown from '@/iss-components/cart-dropdown/cart-dropdown.vue';
// Supporting files // Supporting files
import { fetchCmsContentForPage, processIfStatements } from '@/helpers/cms-content-helper'; import { fetchCmsContentForPage, processIfStatements } from '@/helpers/cms-content-helper';
import settleAllPromises from '@/helpers/layout-helper'; import settleAllPromises from '@/helpers/layout-helper';
@ -91,22 +101,26 @@ export default {
siteHeader, siteHeader,
vehicleBanner, vehicleBanner,
siteFooter, siteFooter,
addToCalendar addToCalendar,
cartDropdown
}, },
mixins: [BaseFormMixin], mixins: [BaseFormMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
console.log('beforeRouteEnter just called');
useMainStore().createSubmittedOrder(); useMainStore().createSubmittedOrder();
// Call APIs // Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage); const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
// Settle promises and get results // Settle promises and get results
const promiseResultMap = [ const promiseResultMap = [
{ {
resultKey: 'cmsContent', resultKey: 'cmsContent',
promise: cmsContentPromise promise: cmsContentPromise
}]; }
// use resultMap to populate layout content. ];
// use resultMap to populate layout content.
const resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
next((vm) => { next((vm) => {
vm.setCmsContent(resultMap.cmsContent); vm.setCmsContent(resultMap.cmsContent);
}); });
@ -263,6 +277,12 @@ export default {
this.submittedOrder.schedule.jobMaxMinutes this.submittedOrder.schedule.jobMaxMinutes
); );
return inshopDurationTime; return inshopDurationTime;
},
isPayInAdvance() {
return this.submittedOrder.payment.isPayInAdvance;
},
selectedVaps() {
return this.submittedOrder.lineItems.vaps;
} }
}, },
mounted() { mounted() {