INSR-7757: More cart updates

This commit is contained in:
Alex Humphries 2026-02-11 14:08:58 -05:00
parent b5ecac8b5a
commit 4dab87bf3f
9 changed files with 199 additions and 2297 deletions

View file

@ -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;

View file

@ -8,7 +8,8 @@
REPLACE_FEE: 'REPLACE FEE',
MOBILE_FEE: 'MOBILE FEE',
REPAIR_FEE: 'REPAIR FEE',
WARRANTY: 'WARRANTY'
WARRANTY: 'WARRANTY',
MOLDING: 'MOULDING'
});
export default partTypeStrings;

View file

@ -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">
<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">
<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,13 @@ 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);
console.log('Base Price Label before processing if statements:', content);
return processIfStatements(
content,
'custom',
this.getCustomValueFromString
);
},
subtotalLabel() {
return this.getCmsContent(this.widget.subtotal, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
@ -409,15 +369,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 +435,17 @@ export default {
const vapsTypeDescription = vapsItemDescriptions?.find((entry) => entry?.Name === vapsPartType);
return vapsTypeDescription?.Text ?? '';
},
getCustomValueFromString(str) {
console.log(this.cartOrder)
switch (str) {
case 'isRepair':
return this.cartOrder.damage.isRepair;
case 'hasMoldingPart':
return this.hasMoldingPart;
default:
return null;
}
},
formatAmountInDollars
}
};
@ -471,45 +456,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 +498,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 +525,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;
}

File diff suppressed because it is too large Load diff

View file

@ -1,478 +0,0 @@
<template>
<div class="cart-table">
<div class="cart-item-list">
<div
id="cart-deductible-or-base-price"
class="cart-item">
<template
v-if="showDeductibleCartItem">
<span id="deductible-label">{{ deductibleLabel }}</span>
<span id="deductible-value">{{ getDisplayed(deductible) }}</span>
</template>
<template
v-else>
<span id="base-price-label">{{ basePriceLabel }}</span>
<span id="base-price-value">{{ getDisplayed(servicePrice) }}</span>
</template>
</div>
<!-- NonPackaged Cart Items -->
<div
v-for="(item, i) in nonServicePackageCartItems"
:key="i"
class="cart-item">
<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>
<!-- Packaged Cart Items -->
<div
v-for="(item, i) in servicePackageCartItems"
:key="i"
class="cart-item">
<span>{{ item?.name ?? '' }}</span>
<span>{{ formatAmountInDollars(item?.subTotal ?? 0) }}</span>
</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>
<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';
import { getHighestFullySatisfiedTier, getPackageContents } from '@/helpers/service-package-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,
getRecycleFeeLineItem, getSalesTax,
getServiceLineItems, getSubtotal, isOrderITAC, isOrderNoComp,
isOrderUnverified
} from '@/helpers/cart-helper';
import { getPriceOfLineItems, getTaxOfLineItems } from '@/helpers/price-calculator';
const VERIFYING_COVERAGE = 'Verifying coverage';
const RECYCLING_MODAL_REF_NAME = 'RecycleModal';
export default {
name: 'confirmation-cart',
components: {
contentGroupModal,
textBlock,
textLink
},
props: {
showAsPaid: Boolean,
recyclingModalCmsWidgetName: String,
submittedOrder: Object
},
data() {
return {
widget: {
amountDue: 'AmountDueTextWidget',
amountPaid: 'AmountPaidTextWidget',
deductible: 'DeductibleWidget',
basePrice: 'BasePriceWidget',
subtotal: 'SubtotalWidget',
salesTax: 'SalesTaxWidget',
recycleFee: 'RecycleFeeWidget',
mobileFee: 'MobileServiceWidget',
servicePackage: 'ServicePackageTitle',
vapsItemDescriptions: 'VapsItemDescriptions',
warrantyText: 'WarrantyCartItemTextWidget',
guaranteeText: 'GuaranteeCartItemTextWidget'
},
cartItemType,
RECYCLING_MODAL_REF_NAME
};
},
computed: {
cartOrder() {
return this.submittedOrder ?? useMainStore().order;
},
availableVaps() {
return this.cartOrder.availableVaps;
},
deductible() {
return getDeductible(this.cartOrder);
},
showDeductibleCartItem() {
return this.isUnverified || (!this.isNoComp && !this.isITAC);
},
lineItems() {
return getLineItems(this.cartOrder);
},
recycleFeeLineItem() {
return getRecycleFeeLineItem(this.cartOrder);
},
servicePrice() {
return getPriceOfLineItems(getServiceLineItems(this.cartOrder)) ?? 0;
},
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;
},
vehicleDamage() {
return this.submittedOrder ? this.submittedOrder.damage : useMainStore().damage;
},
servicePackageTier() {
const { glassToReplace, isRepair } = this.vehicleDamage;
const { vaps } = this.lineItems;
return getHighestFullySatisfiedTier(
glassToReplace ?? [],
this.availableLineItems,
isRepair,
vaps ?? []
);
},
partTypesInServicePackage() {
const { glassToReplace, isRepair } = this.vehicleDamage;
return getPackageContents(
glassToReplace ?? [],
this.availableLineItems,
isRepair,
this.servicePackageTier
) ?? [];
},
servicePackageCartItems() {
const items = [];
if (this.partTypesInServicePackage.includes(partTypeStrings.FRONT_WIPER) ?? this.frontWipersCartItem) {
items.push(this.frontWipersCartItem);
}
if (this.partTypesInServicePackage.includes(partTypeStrings.REAR_WIPER) ?? this.rearWipersCartItem) {
items.push(this.rearWipersCartItem);
}
if (this.partTypesInServicePackage.includes(partTypeStrings.RAIN_DEFENSE) ?? this.rainDefenseCartItem) {
items.push(this.rainDefenseCartItem);
}
return items;
},
nonServicePackageCartItems() {
const vapPartTypesInOrder = Array.from(new Set(this.lineItems.vaps?.map((vap) => vap.partType) ?? []));
const vapPartTypesInOrderButNotPackage = vapPartTypesInOrder
.filter((partType) => !this.partTypesInServicePackage.includes(partType))
?? [];
const vapsCartItemsNotInPackage = vapPartTypesInOrderButNotPackage.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;
}
});
const items = vapsCartItemsNotInPackage.filter((item) => item != null);
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.recycleFeeCartItem && !isRecycleFeeHidden) {
items.push(this.recycleFeeCartItem);
}
if (this.mobileFeeCartItem && !isMobileFeeHidden) {
items.push(this.mobileFeeCartItem);
}
if (this.isITAC || this.isNoComp) {
items.push(this.warrantyCartItem);
}
return items;
},
frontWipersCartItem() {
return this.getCartItemForVapsPart(partTypeStrings.FRONT_WIPER);
},
rearWipersCartItem() {
return this.getCartItemForVapsPart(partTypeStrings.REAR_WIPER);
},
rainDefenseCartItem() {
return this.getCartItemForVapsPart(partTypeStrings.RAIN_DEFENSE);
},
allCartItems() {
return [
...this.servicePackageCartItems,
...this.nonServicePackageCartItems
];
},
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() {
return this.getCmsContent(this.widget.basePrice, 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);
},
servicePackageNames() {
return this.getCmsContent(
this.widget.servicePackage,
widgetFields.INPUT_QUESTION_WIDGET.ANSWERS
);
},
servicePackageLabelWidget() {
if (!this.servicePackageNames) {
return '';
}
const currentPackage = this.servicePackageNames
?.find((entry) => entry?.Name === this.servicePackageTier);
return currentPackage?.SubWidgetName ?? '';
},
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.submittedOrder.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.VAP,
partType: partTypeStrings.WARRANTY,
subTotal: 0,
salesTax: 0
}
}
},
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 ?? '';
},
formatAmountInDollars
}
};
</script>
<style lang="scss" scoped>
@import "@/styles/ux-variables-svg-strings.scss";
$RECYCLING_MODAL_REF_NAME: 'RecycleModal';
.cart-table {
max-height: 50rem;
transition: all 150ms ease-in;
min-width: fit-content;
:deep(a) {
padding-bottom: 0 !important;
line-height: 1.625rem !important;
text-underline-offset: auto !important;
}
.cart-item {
display: flex;
flex-direction: row;
justify-content: space-between;
padding: .5rem 0rem;
width: 100%;
min-width: fit-content;
&:nth-child(odd) {
background-color: #f4f4f4;
}
&:last-child {
border-bottom: 1px solid #d4d6d8;
}
span {
white-space: nowrap;
padding: 0rem 1rem;
}
}
.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;
}
}
:deep(.modal-content a.external-text) {
text-underline-offset: 0.25rem;
line-height: 2;
padding: 0 0 0.25rem 0;
font-weight: 500;
max-width: -webkit-fit-content;
max-width: -moz-fit-content;
max-width: fit-content;
}
:deep(##{$RECYCLING_MODAL_REF_NAME} h5) {
text-align: center;
}
:deep(#recycle-fee-label a){
line-height: initial;
}
</style>

View file

@ -92,9 +92,10 @@
<div class="cart-header">
{{ cartHeaderText }}
</div>
<confirmationCart
<cartDropdown
v-if="showCart"
:showAsPaid="payment.isPayInAdvance"
:readOnly="true"
recyclingModalCmsWidgetName="RecycleModal"
servicePackageTitleWidgetName="ServicePackageTitle"
:submittedOrder="submittedOrder" />
@ -108,7 +109,7 @@
import siteHeader from '@/iss-components/site-header/site-header.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 confirmationCart from '@/iss-components/confirmation-cart/confirmation-cart.vue';
import cartDropdown from '@/iss-components/cart-dropdown/cart-dropdown.vue';
// Supporting files
import {
@ -142,7 +143,7 @@ export default {
siteHeader,
siteFooter,
addToCalendar,
confirmationCart
cartDropdown
},
mixins: [BaseFormMixin],
async beforeRouteEnter(to, from, next) {
@ -683,6 +684,12 @@ export default {
}
}
.service-description {
>span:first-child {
font-weight: $font-weight-bold;
}
}
.work-order-title {
margin-bottom: .625rem;
}

View file

@ -24,7 +24,6 @@
<cartDropdown
:showAsPaid="false"
:readOnly="false"
:showAmountInHeader="true"
recyclingModalCmsWidgetName="RecycleModal"
servicePackageTitleWidgetName="ServicePackageTitle" />
<hr class="mt-0 mb-5" />

View file

@ -42,8 +42,7 @@
<cartDropdown
ref="cart"
:readOnly="true"
:showDropdownHeader="true"
:isInitiallyExpanded="true"
:showAsPaid="false"
recyclingModalCmsWidgetName="RecycleModal"
servicePackageTitleWidgetName="ServicePackageTitle" />
<hr class="my-0">

View file

@ -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,