453 lines
16 KiB
Vue
453 lines
16 KiB
Vue
<template>
|
|
<div
|
|
v-if="!isDeductibleOnly"
|
|
class="cart-table">
|
|
<div class="cart-item-list">
|
|
<div
|
|
v-if="showDeductibleCartItem"
|
|
class="cart-item">
|
|
<div class="price-row">
|
|
<span id="deductible-label">{{ deductibleLabel }}</span>
|
|
<span id="deductible-value">{{ getDisplayed(deductible) }}</span>
|
|
</div>
|
|
</div>
|
|
<div
|
|
v-else
|
|
class="cart-item">
|
|
<div class="price-row">
|
|
<span id="base-price-label">{{ basePriceLabel }}</span>
|
|
<span id="base-price-value">{{ getDisplayed(servicePrice) }}</span>
|
|
</div>
|
|
</div>
|
|
<div
|
|
v-if="showRecalibrationCartItem"
|
|
class="cart-item">
|
|
<div class="price-row">
|
|
<span id="recalibration-label">{{ recalibrationLabel }}</span>
|
|
<span id="recalibration-value">{{ formatAmountInDollars(recalibrationPrice) }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
v-for="(item, i) in cartItems"
|
|
:key="i"
|
|
class="cart-item packaged">
|
|
<div class="price-row">
|
|
<span>{{ 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">
|
|
<div
|
|
v-if="!isUnverified"
|
|
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>
|
|
</div>
|
|
<div v-else class="deductible-box">
|
|
<span class="deductible-label">{{ deductibleLabel }}:</span>
|
|
<span class="deductible-value">{{ getDisplayed(deductible) }}</span>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { useMainStore } from '@/store';
|
|
import contentGroupModal from '@/iss-components/content-group-modal/content-group-modal.vue';
|
|
import textBlock from '@/digital-components/text-block/text-block.vue';
|
|
import textLink from '@/ux-components/text-link/text-link.vue';
|
|
import { formatAmountInDollars } from '@/helpers/text-helper.js';
|
|
|
|
// Constants
|
|
import { experimentSettings } from '@/constants/experiments';
|
|
import partTypeStrings from '@/constants/part-type-strings.js';
|
|
import cartItemType from '@/constants/cart-item-type.js';
|
|
import widgetFields from '@/constants/cms-widget-fields.js';
|
|
import {
|
|
getCartTotal,
|
|
getDeductible,
|
|
getLineItems, getMobileFeeLineItem,
|
|
getRecalibrationTotal,
|
|
getRecycleFeeLineItem, getSalesTax,
|
|
getServiceLineItems, getSubtotal, isOrderITAC, isOrderNoComp,
|
|
isOrderUnverified
|
|
} from '@/helpers/cart-helper';
|
|
import { getPriceOfLineItems, getTaxOfLineItems } from '@/helpers/price-calculator';
|
|
import { processIfStatements } from '@/helpers/cms-content-helper';
|
|
|
|
const VERIFYING_COVERAGE = 'Verifying coverage';
|
|
|
|
export default {
|
|
name: 'confirmation-cart',
|
|
components: {
|
|
textLink
|
|
},
|
|
props: {
|
|
readOnly: Boolean,
|
|
showAsPaid: Boolean,
|
|
submittedOrder: Object
|
|
},
|
|
data() {
|
|
return {
|
|
widget: {
|
|
amountDue: 'AmountDueTextWidget',
|
|
amountPaid: 'AmountPaidTextWidget',
|
|
deductible: 'DeductibleWidget',
|
|
basePrice: 'BasePriceWidget',
|
|
recalibration: 'RecalibrationWidget',
|
|
subtotal: 'SubtotalWidget',
|
|
salesTax: 'SalesTaxWidget',
|
|
recycleFee: 'RecycleFeeWidget',
|
|
mobileFee: 'MobileServiceWidget',
|
|
servicePackage: 'ServicePackageTitle',
|
|
vapsItemDescriptions: 'VapsItemDescriptions',
|
|
warrantyText: 'WarrantyCartItemTextWidget',
|
|
guaranteeText: 'GuaranteeCartItemTextWidget'
|
|
},
|
|
cartItemType
|
|
};
|
|
},
|
|
computed: {
|
|
cartOrder() {
|
|
return this.submittedOrder ?? useMainStore().order;
|
|
},
|
|
availableVaps() {
|
|
return this.cartOrder.availableVaps;
|
|
},
|
|
deductible() {
|
|
return getDeductible(this.cartOrder);
|
|
},
|
|
isDeductibleOnly() {
|
|
return this.cartItems.length === 0;
|
|
},
|
|
showDeductibleCartItem() {
|
|
return this.isUnverified || (!this.isNoComp && !this.isITAC);
|
|
},
|
|
showRecalibrationCartItem() {
|
|
return this.recalibrationPrice > 0;
|
|
},
|
|
recalibrationPrice() {
|
|
return getRecalibrationTotal(this.cartOrder);
|
|
},
|
|
lineItems() {
|
|
return getLineItems(this.cartOrder);
|
|
},
|
|
recycleFeeLineItem() {
|
|
return getRecycleFeeLineItem(this.cartOrder);
|
|
},
|
|
servicePrice() {
|
|
const price = getPriceOfLineItems(getServiceLineItems(this.cartOrder)) ?? 0;
|
|
return price - this.recalibrationPrice;
|
|
},
|
|
isUnverified() {
|
|
return isOrderUnverified(this.cartOrder);
|
|
},
|
|
isITAC() {
|
|
return isOrderITAC(this.cartOrder);
|
|
},
|
|
isNoComp() {
|
|
return isOrderNoComp(this.cartOrder);
|
|
},
|
|
// TODO fix rounding
|
|
subTotal() {
|
|
return getSubtotal(this.cartOrder);
|
|
},
|
|
salesTax() {
|
|
return getSalesTax(this.cartOrder);
|
|
},
|
|
total() {
|
|
return getCartTotal(this.cartOrder);
|
|
},
|
|
amountDue() {
|
|
return this.showAsPaid ? 0 : this.total;
|
|
},
|
|
amountPaid() {
|
|
return !this.showAsPaid ? 0 : this.total;
|
|
},
|
|
availableLineItems() {
|
|
const { supportingItems, glassParts, otherParts, feeItems } = this.lineItems;
|
|
const result = [
|
|
...(supportingItems ?? []),
|
|
...(glassParts ?? []),
|
|
...(otherParts ?? []),
|
|
...(this.availableVaps ?? []),
|
|
...(feeItems ?? [])
|
|
];
|
|
return result;
|
|
},
|
|
cartItems() {
|
|
const items = [];
|
|
const isRecycleFeeHidden = this.getSettingValue(experimentSettings.ISS_FEATURE_TOGGLE_IS_RECYCLE_FEE_HIDDEN) === 'true';
|
|
const isMobileFeeHidden = this.getSettingValue(experimentSettings.ISS_FEATURE_TOGGLE_IS_MOBILE_FEE_HIDDEN) === 'true';
|
|
if (this.isITAC || this.isNoComp) {
|
|
items.push(this.warrantyCartItem);
|
|
}
|
|
if (this.recycleFeeCartItem && !isRecycleFeeHidden) {
|
|
items.push(this.recycleFeeCartItem);
|
|
}
|
|
if (this.mobileFeeCartItem && !isMobileFeeHidden) {
|
|
items.push(this.mobileFeeCartItem);
|
|
}
|
|
const vapPartTypesInOrder = Array.from(new Set(this.lineItems.vaps?.map((vap) => vap.partType) ?? []));
|
|
const vapsCartItems = vapPartTypesInOrder.map((partType) => {
|
|
switch (partType) {
|
|
case partTypeStrings.FRONT_WIPER:
|
|
return this.frontWipersCartItem;
|
|
case partTypeStrings.REAR_WIPER:
|
|
return this.rearWipersCartItem;
|
|
case partTypeStrings.RAIN_DEFENSE:
|
|
return this.rainDefenseCartItem;
|
|
default:
|
|
return null;
|
|
}
|
|
});
|
|
items.push(...vapsCartItems.filter((item) => item != null));
|
|
return items;
|
|
},
|
|
frontWipersCartItem() {
|
|
return this.getCartItemForVapsPart(partTypeStrings.FRONT_WIPER);
|
|
},
|
|
rearWipersCartItem() {
|
|
return this.getCartItemForVapsPart(partTypeStrings.REAR_WIPER);
|
|
},
|
|
rainDefenseCartItem() {
|
|
return this.getCartItemForVapsPart(partTypeStrings.RAIN_DEFENSE);
|
|
},
|
|
amountDueLabel() {
|
|
return this.getCmsContent(this.widget.amountDue, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
|
},
|
|
amountPaidLabel() {
|
|
return this.getCmsContent(this.widget.amountPaid, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
|
},
|
|
deductibleLabel() {
|
|
return this.getCmsContent(this.widget.deductible, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
|
},
|
|
basePriceLabel() {
|
|
const content = this.getCmsContent(this.widget.basePrice, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
|
return processIfStatements(
|
|
content,
|
|
'custom',
|
|
this.getCustomValueFromString
|
|
);
|
|
},
|
|
recalibrationLabel() {
|
|
return this.getCmsContent(this.widget.recalibration, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
|
},
|
|
subtotalLabel() {
|
|
return this.getCmsContent(this.widget.subtotal, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
|
},
|
|
salesTaxLabel() {
|
|
return this.getCmsContent(this.widget.salesTax, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
|
},
|
|
recycleFeeCartItem() {
|
|
return this.recycleFeeLineItem
|
|
? this.getCartItem(
|
|
this.getCmsContent(this.widget.recycleFee, widgetFields.TEXT_BLOCK_WIDGET.TEXT),
|
|
[this.recycleFeeLineItem],
|
|
cartItemType.RECYCLE_FEE,
|
|
partTypeStrings.REPLACE_FEE
|
|
)
|
|
: null;
|
|
},
|
|
mobileFeeCartItem() {
|
|
const mobileFeeLineItem = getMobileFeeLineItem(this.cartOrder);
|
|
return mobileFeeLineItem
|
|
? this.getCartItem(
|
|
this.getCmsContent(this.widget.mobileFee, widgetFields.TEXT_BLOCK_WIDGET.TEXT),
|
|
[mobileFeeLineItem],
|
|
cartItemType.MOBILE_FEE,
|
|
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();
|
|
},
|
|
getDisplayed(amount) {
|
|
return this.isUnverified
|
|
? VERIFYING_COVERAGE
|
|
: formatAmountInDollars(amount);
|
|
},
|
|
getCartItem(label, lineItems, cartItemTypeString, partType) {
|
|
let cartItem = null;
|
|
|
|
if (lineItems && lineItems.length > 0) {
|
|
cartItem = {
|
|
name: label,
|
|
cartItemType: cartItemTypeString,
|
|
partType,
|
|
subTotal: getPriceOfLineItems(lineItems),
|
|
salesTax: getTaxOfLineItems(lineItems)
|
|
};
|
|
}
|
|
|
|
return cartItem;
|
|
},
|
|
getCartItemForVapsPart(partType) {
|
|
const label = this.getCmsContentForVapsType(partType);
|
|
const lineItems = this.lineItems.vaps
|
|
?.filter((vapsLineItem) => vapsLineItem.partType === partType) ?? [];
|
|
return this.getCartItem(label, lineItems, cartItemType.VAP, partType);
|
|
},
|
|
removeVap(partType) {
|
|
const newVaps = useMainStore().lineItems.vaps?.filter((vap) => vap?.partType !== partType) ?? [];
|
|
useMainStore().updateVaps(newVaps);
|
|
},
|
|
getCmsContentForVapsType(vapsPartType) {
|
|
const vapsItemDescriptions = this.getCmsContent(
|
|
this.widget.vapsItemDescriptions,
|
|
widgetFields.INPUT_QUESTION_WIDGET.ANSWERS
|
|
);
|
|
|
|
if (!vapsItemDescriptions) {
|
|
return '';
|
|
}
|
|
|
|
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
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
@import "@/styles/ux-variables-svg-strings.scss";
|
|
|
|
.cart-table {
|
|
max-height: 50rem;
|
|
transition: all 150ms ease-in;
|
|
min-width: fit-content;
|
|
|
|
.cart-item {
|
|
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;
|
|
}
|
|
|
|
.price-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
span, a {
|
|
white-space: nowrap;
|
|
padding: 0rem 1rem;
|
|
}
|
|
|
|
a:hover {
|
|
color: $heritage-blue-secondary;
|
|
}
|
|
}
|
|
|
|
.cart-footer {
|
|
border-top: 1px solid $green;
|
|
}
|
|
|
|
.subtotal-and-tax {
|
|
background-color: $green-150;
|
|
font-weight: $font-weight-bold;
|
|
color: $black;
|
|
>div {
|
|
padding: .5rem 1rem;
|
|
}
|
|
}
|
|
|
|
.bottom-amount-due {
|
|
color: $white;
|
|
font-weight: 600;
|
|
background-color: $green;
|
|
padding: .5rem 1rem;
|
|
}
|
|
}
|
|
.deductible-box {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: .25rem;
|
|
}
|
|
.deductible-label {
|
|
color: $black;
|
|
font-weight: 600;
|
|
}
|
|
.deductible-value {
|
|
color: $green;
|
|
font-weight: $font-weight-bold;
|
|
font-size: 1.625rem;
|
|
}
|
|
</style>
|