CSR-1383: add product tiles; some cart updates for CSR-1382

This commit is contained in:
Adam Caouette 2023-09-28 16:52:07 -04:00
parent 166978bc3a
commit 86add1e358
7 changed files with 404 additions and 156 deletions

View file

@ -14,6 +14,9 @@ module.exports = {
"!src/helpers/unit-test-helper.js", "!src/helpers/unit-test-helper.js",
"!src/layouts/vehicle-damage/windshield-options/windshield-options.vue", "!src/layouts/vehicle-damage/windshield-options/windshield-options.vue",
"!src/layouts/reveal/**/*.vue", "!src/layouts/reveal/**/*.vue",
"!src/layouts/payment-method/**/*.vue",
"!src/fmg-components/cart/**/*.vue",
"!src/fmg-components/add-vaps-modal-buttons/**/*.vue",
"!src/ux-components/text-link/**/*.vue", "!src/ux-components/text-link/**/*.vue",
"!src/layouts/vin-lookup/**/*.vue", //Temporary for Quote page testing "!src/layouts/vin-lookup/**/*.vue", //Temporary for Quote page testing
"!src/common-components/funnel-header/menu-modal/**/*.vue", "!src/common-components/funnel-header/menu-modal/**/*.vue",

View file

@ -197,6 +197,12 @@ export default {
classes += "flex-column"; classes += "flex-column";
} }
break; break;
case "addVapsButton":
classes = "row g-4 justify-content-center mb-2";
if (this.isWide) {
classes += " flex-column";
}
break;
case "radio": case "radio":
classes = "ui-radio d-flex"; classes = "ui-radio d-flex";
break; break;

View file

@ -0,0 +1,218 @@
<template>
<baseInputButton
v-bind="$props"
:buttonWrapperClasses="[
'add-vaps-button w-100 rounded-3 d-flex align-items-center h-100 base-input-button',
{ horizontal: isWide },
]"
v-model="selectedValue">
<div
class="d-flex w-100 align-items-center px-1 h-100 add-vaps-button-content button-content rounded-3"
:class="labelClasses">
<img :id="buttonImageId" :src="buttonImage" :alt="altText" />
<div class="copy-wrapper" :class="isWide ? 'ms-4 me-2' : ''">
<p class="small mb-0" :class="isWide ? 'mt-0' : 'mt-1'">
{{ buttonLabel }}
</p>
<p v-if="buttonLabelSubCopy" class="fs-7 m-0 sub-copy">
{{ buttonLabelSubCopy }}
</p>
</div>
<p class="fs-7 mb-0 link-styling" :class="isWide ? 'mt-0' : 'mt-2'">View details</p>
</div>
</baseInputButton>
</template>
<script>
import baseInputButton from "@/digital-components/base-input-button/base-input-button";
import inputButtonWrapperMixin from "@/mixins/input-button-wrapper-mixin";
export default {
name: "addVapsButton",
mixins: [inputButtonWrapperMixin],
components: {
baseInputButton,
},
computed: {
labelClasses() {
if (this.isWide) {
let classes = "flex-row py-2 ps-4 pe-4";
if (this.buttonLabelSubCopy) {
classes += " checkboxTop";
}
return classes;
} else {
return "flex-column py-2";
}
},
},
};
</script>
<style lang="scss">
@mixin add-vaps-button-focus($box-shadow-color) {
&:focus-visible + .add-vaps-button-content {
box-shadow: 0 0 0 2.5px $box-shadow-color;
border-radius: 0.5rem;
}
&:focus + .add-vaps-button-content {
box-shadow: 0 0 0 2.5px $box-shadow-color;
border-radius: 0.5rem;
}
&:checked:focus + .add-vaps-button-content {
box-shadow: 0 0 0 2.5px $box-shadow-color;
}
}
.add-vaps-button {
border: 1px solid $gray-500;
&.has-error {
input[type="checkbox"],
input[type="radio"] {
@include add-vaps-button-focus($red);
}
}
img {
height: auto;
width: 2.6rem;
max-width: 100%;
}
.copy-wrapper {
flex: 1 1 auto;
}
.link-styling {
font-weight: 500;
color: $blue;
text-decoration: underline;
text-underline-offset: 4px;
}
input[type="checkbox"],
input[type="radio"] {
position: absolute;
+ .add-vaps-button-content {
outline: none;
display: flex;
position: relative;
justify-content: space-between;
p {
color: $gray-600;
text-align: center;
&.sub-copy {
color: $green;
}
}
}
@include add-vaps-button-focus($blue);
+ .add-vaps-button-content::before {
content: "";
position: absolute;
display: flex;
margin: 0 auto;
width: 1rem;
height: 1rem;
margin: 3rem 0 0 0;
background: white;
border: 1px solid $gray-500;
border-radius: 2px;
order: 2;
flex-shrink: 0;
color: $gray-600;
}
+ .add-vaps-button-content.checkboxTop::before {
margin: -1.25rem 0.5rem 0 0 !important;
}
+ .add-vaps-button-content.checkboxTop::after {
margin: -1.5rem 0.5rem 0 0 !important;
}
&:checked + .add-vaps-button-content::before {
background: $blue;
}
&:checked + .add-vaps-button-content::after {
content: "";
position: absolute;
margin: 3.2rem 0 0 0;
border-left: 2px solid $white;
border-bottom: 2px solid $white;
height: 6px;
width: 11px;
transform: rotate(-45deg);
z-index: 1;
}
}
input[type="radio"] {
+ .add-vaps-button-content::before {
content: "";
display: none;
}
+ .add-vaps-button-content::after {
content: "";
display: none;
}
+ .add-vaps-button-content {
img {
margin-bottom: 0;
}
}
}
&.horizontal {
input[type="checkbox"],
input[type="radio"] {
+ .add-vaps-button-content::before {
content: "";
position: relative;
margin: 0 0.5rem 0 0;
order: 1;
}
&:checked + .add-vaps-button-content::after {
content: "";
margin: -0.15rem 0 0 0;
left: 1.175rem;
}
&:checked + .add-vaps-button-content {
p {
color: $black;
font-weight: 500;
}
.sub-copy {
color: $gray-600;
font-weight: 400;
}
}
+ .add-vaps-button-content {
outline: none;
min-height: 48px;
color: $gray-600;
img {
margin-bottom: 0;
}
p {
color: $gray-600;
text-align: left;
&.sub-copy {
color: $gray-550;
}
}
}
}
}
}
</style>

View file

@ -0,0 +1,102 @@
<template>
<transition name="fade" mode="out-in">
<div class="add-vaps-buttons" aria-live="polite" v-if="isDisplayed">
<buttonQuestion
ref="buttonQuestion"
:answers="answersToDisplay"
groupName="addVaps"
v-model="selectedValues"
buttonTypeString="addVapsButton"
:buttonTypeObject="addVapsButton"
:isWide="answersToDisplay.length > 1 ? false : true" />
</div>
</transition>
</template>
<script>
import buttonQuestion from "@/digital-components/button-question/button-question";
import addVapsButton from "./add-vaps-button/add-vaps-button";
export default {
name: "add-vaps-modal-buttons",
props: {
cart: Object,
rainDefenseItem: Object,
wiperItems: Object,
vapsTilesCmsName: String,
},
data() {
return {
addVapsButton: addVapsButton,
};
},
computed: {
selectedValues: {
get: function () {
return this.answersToDisplay;
},
set: function (newValue) {
// TODO - emit event to open modal
},
},
isDisplayed() {
return this.showAddRainDefense || this.showAddWipers;
},
showAddRainDefense() {
const rainDefenseIndex = this.cart.findIndex((vap) => {
return vap.partType === "RAIN DEFENSE";
});
return rainDefenseIndex > -1 ? false : true;
},
showAddWipers() {
const rainDefenseIndex = this.cart.findIndex((vap) => {
return vap.partType.includes(" WIPER");
});
return rainDefenseIndex > -1 ? false : true;
},
rainDefensePrice() {
const lineItem = this.rainDefenseItem;
const price = lineItem.laborAmount + lineItem.sellingPrice + lineItem.kitPrice;
return parseFloat(price).toFixed(2);
},
wipersPrice() {
let price = 0;
const lineItems = this.wiperItems;
lineItems?.forEach((item) => {
price += item.laborAmount + item.sellingPrice + item.kitPrice;
});
return parseFloat(price).toFixed(2);
},
answersCmsData() {
return this.getCmsContent(this.vapsTilesCmsName, "Answers");
},
answersToDisplay() {
const answers = [];
if (!this.answersCmsData) return answers;
const getAnswer = (name, answers) => {
const answerIndex = answers.findIndex((answer) => {
return answer.Name === name;
});
return answers[answerIndex];
};
if (this.showAddWipers) {
const answer = getAnswer("ADD WIPER BLADES", this.answersCmsData);
answer.SubText = "+$" + this.wipersPrice;
answers.push(answer);
}
if (this.showAddRainDefense) {
const answer = getAnswer("ADD RAIN DEFENSE", this.answersCmsData);
answer.SubText = "+$" + this.rainDefensePrice;
answers.push(answer);
}
return answers;
},
},
components: {
buttonQuestion,
},
};
</script>

View file

@ -14,14 +14,18 @@
<div class="service-type" :class="{ packageWithVAPS: packageWithVAPS }"> <div class="service-type" :class="{ packageWithVAPS: packageWithVAPS }">
<textBlock :cmsWidgetName="packageNameWidget" /><span>{{ packagePrice }}</span> <textBlock :cmsWidgetName="packageNameWidget" /><span>{{ packagePrice }}</span>
</div> </div>
<div v-for="(lineItem, i) in displayedPackageLineItems" :key="i" <div
:class="[this.displayedPackageLineItems ? 'vaps' : '']"> v-for="(lineItem, i) in displayedPackageLineItems"
:key="i"
:class="[this.displayedPackageLineItems ? 'vaps' : '']">
<span>{{ lineItem.name }}</span> <span>{{ lineItem.name }}</span>
<span><a href="#">Remove</a></span> <span><a href="#">Remove</a></span>
</div> </div>
<hr style="background: red;"> <hr style="background: red" />
<div v-for="(lineItem, i) in displayedNonPackageLineItems" :key="i" <div
:class="[this.displayedNonPackageLineItems ? 'vaps' : '']"> v-for="(lineItem, i) in displayedNonPackageLineItems"
:key="i"
:class="[this.displayedNonPackageLineItems ? 'vaps' : '']">
<span>{{ lineItem.name }}</span> <span>{{ lineItem.name }}</span>
<span><a href="#">Remove</a></span> <span><a href="#">Remove</a></span>
<span>{{ lineItem.price }}</span> <span>{{ lineItem.price }}</span>
@ -122,7 +126,7 @@ export default {
...findLineItemsWithPartType(vapType, this.nullSafeAvailableLineItems) ...findLineItemsWithPartType(vapType, this.nullSafeAvailableLineItems)
); );
}); });
console.log("VapsLineItems:", vapsLineItemsForSelectedPackage); // console.log("VapsLineItems:", vapsLineItemsForSelectedPackage);
return vapsLineItemsForSelectedPackage; return vapsLineItemsForSelectedPackage;
}, },
}, },
@ -174,15 +178,12 @@ export default {
}; };
displayedPackageLineItems.push(packageLineItem); displayedPackageLineItems.push(packageLineItem);
}); });
console.log("What displayedPackageLineItems is", packageLineItems); // console.log("What displayedPackageLineItems is", packageLineItems);
return displayedPackageLineItems; return displayedPackageLineItems;
}, },
displayedNonPackageLineItems() { displayedNonPackageLineItems() {
const displayedNonPackageLineItems = []; const displayedNonPackageLineItems = [];
const possibleLineItems = [ const possibleLineItems = [...this.vaps, ...this.supportingItems];
...this.orderLineItems.vaps,
...this.orderLineItems.supportingItems,
];
possibleLineItems?.forEach((item) => { possibleLineItems?.forEach((item) => {
let price = item.sellingPrice + item.kitPrice + item.laborAmount; let price = item.sellingPrice + item.kitPrice + item.laborAmount;
const isAllowed = this.allowableLineItems?.findIndex((lineItem) => { const isAllowed = this.allowableLineItems?.findIndex((lineItem) => {
@ -207,13 +208,24 @@ export default {
return displayedNonPackageLineItems; return displayedNonPackageLineItems;
}, },
glassToReplace() { glassToReplace() {
if (!this.damage) {
return;
}
return this.damage.glassToReplace; return this.damage.glassToReplace;
}, },
isRepair() { isRepair() {
if (!this.damage) {
return;
}
return this.damage.isRepair; return this.damage.isRepair;
}, },
vaps() { vaps() {
return this.orderLineItems?.vaps; const vaps = this.orderLineItems?.vaps;
return vaps?.length > 0 ? vaps : [];
},
supportingItems() {
const supportingItems = this.orderLineItems?.supportingItems;
return supportingItems?.length > 0 ? supportingItems : [];
}, },
recycleLabel() { recycleLabel() {
return "Recycling"; return "Recycling";

View file

@ -211,7 +211,6 @@ export function getHighestRequiredTier(glassToReplace, availableLineItems, isRep
export function getHighestFullySatisfiedTier(glassToReplace, availableLineItems, isRepair, vaps) { export function getHighestFullySatisfiedTier(glassToReplace, availableLineItems, isRepair, vaps) {
const packages = getAvailablePackages(glassToReplace, availableLineItems, isRepair); const packages = getAvailablePackages(glassToReplace, availableLineItems, isRepair);
console.log(glassToReplace);
let highestSatisfiedPackage = packageNames.TIER_ONE; let highestSatisfiedPackage = packageNames.TIER_ONE;
for (let i = 0; i < packages.length; i++) { for (let i = 0; i < packages.length; i++) {
@ -224,7 +223,6 @@ export function getHighestFullySatisfiedTier(glassToReplace, availableLineItems,
highestSatisfiedPackage = packages[i].packageName; highestSatisfiedPackage = packages[i].packageName;
} }
} }
console.log(highestSatisfiedPackage);
return highestSatisfiedPackage; return highestSatisfiedPackage;
} }

View file

@ -1,63 +1,39 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }"> <Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
<div class="page-container-grouped-styles"> <loadingModal ref="loadingModal" />
<loadingModal ref="loadingModal" /> <div class="container-fluid page-container-grouped-styles">
<div class="row">
<div class="col">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
</div>
</div>
<div class="row justify-content-md-center">
<div class="col-md-6 col-xl-4">
<vehicleBanner
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="false" />
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <funnelSubHeader class="mb-5" cmsWidgetName="FunnelSubHeaderWidget" />
<funnelSubHeader
typeStyle="h5"
class="mb-5 mt-4 dark-header"
leftAlignHeader
cmsWidgetName="FunnelSubHeaderWidget" />
<div class="fade-on-route-transition sub-container make-tall">
<div>
<cart <cart
:damage="damageInfo" :damage="damageInfo"
:orderLineItems="orderLineItems" :orderLineItems="orderLineItems"
:availableLineItems="availableLineItems" :availableLineItems="cartItems"
servicePackageOptionsCmsName="ServicePackageTitle" servicePackageOptionsCmsName="ServicePackageTitle" />
:allowableCartItems="ALLOWABLE_CART_ITEMS" />
<vehicleReview <add-vaps-modal-buttons
cmsWidgetName="VehicleReviewWidget" :cart="cartItems"
:vehicle="vehicleInfo" :wiperItems="availableWipers"
@edit-clicked="editVehicle" /> :rainDefenseItem="availableRainDefense"
vapsTilesCmsName="VapsProductTiles" />
<hr class="my-0" /> <navbar
cmsWidgetName="FunnelFooterWidget"
<damageReview :isForwardActionDisabled="!meta.valid"
cmsWidgetName="DamageReviewWidget" :isBackButtonHidden="shouldHideBackButton"
damageLocationsWidgetName="DamageLocationsWidget" @back-clicked="backButtonAction"
:damage="damageInfo" @ForwardClicked="forwardButtonAction" />
@edit-clicked="editDamage" />
<hr class="my-0" />
<!-- <servicePackageReview
ref="servicePackageReview"
servicePackageOptionsCmsName="ServicePackageTitle"
defaultPackageItemsCmsName="DefaultPackageItemDescriptions"
vapsItemsCmsName="VapsItemDescriptions"
:damage="damageInfo"
:lineItems="lineItems"
@edit-clicked="editServicePackage" /> -->
<hr class="my-0" />
<serviceLocationReview
cmsWidgetName="ServiceLocationTitleWidget"
:serviceLocation="serviceLocationInfo"
@edit-clicked="editServiceLocation" />
</div> </div>
<navbar
cmsWidgetName="FunnelFooterWidget"
:isForwardActionDisabled="!meta.valid"
:isBackButtonHidden="shouldHideBackButton"
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" />
</div> </div>
</div> </div>
</Form> </Form>
@ -69,57 +45,35 @@ import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
import navbar from "@/fmg-components/nav-bar/nav-bar"; import navbar from "@/fmg-components/nav-bar/nav-bar";
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header"; import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue"; import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
import vehicleReview from "@/layouts/review/review-sections/vehicle-review/vehicle-review";
import damageReview from "@/layouts/review/review-sections/damage-review/damage-review";
import servicePackageReview from "@/layouts/review/review-sections/service-package-review/service-package-review";
import serviceLocationReview from "@/layouts/review/review-sections/service-location-review/service-location-review";
import cart from "@/fmg-components/cart/cart"; import cart from "@/fmg-components/cart/cart";
import addVapsModalButtons from "@/fmg-components/add-vaps-modal-buttons/add-vaps-modal-buttons";
import baseMixin from "@/mixins/base-mixin.js"; import baseMixin from "@/mixins/base-mixin.js";
import vehicleQuestionsMixin from "../../mixins/vehicle-questions-mixin";
import { settleAllPromises } from "@/helpers/layout-helper"; import { settleAllPromises } from "@/helpers/layout-helper";
import { storeActions } from "@/constants/store-actions"; import { storeActions } from "@/constants/store-actions";
import store from "@/store"; import store from "@/store";
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { required } from "@/helpers/validation-rules"; import { Form } from "vee-validate";
import { errorMessages } from "@/constants/error-messages";
import { Form, defineRule } from "vee-validate";
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
import { applicationConfig } from "@/constants/application-config";
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
const ALLOWABLE_CART_ITEMS = [
{
partType: "FRONT WIPER",
isRemovable: true,
},
{
partType: "REAR WIPER",
isRemovable: true,
},
{
partType: "RAIN DEFENSE",
isRemovable: true,
},
{
partType: "RECYCLE FEE",
isRemovable: false,
}
]
export default { export default {
name: "paymentMethod", name: "paymentMethod",
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
// Call APIs // Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage); const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
const wipersPromise = baseMixin.methods.dispatchStoreAction(storeActions.GET_WIPERS); const wipersPromise = baseMixin.methods.dispatchStoreActionWithLogging(
const rainDefensePromise = baseMixin.methods.dispatchStoreAction( storeActions.GET_WIPERS,
storeActions.GET_RAIN_DEFENSE null,
"payment-method"
); );
const supportingItemsPromise = baseMixin.methods.dispatchStoreAction( const rainDefensePromise = baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.GET_SUPPORTING_ITEMS storeActions.GET_RAIN_DEFENSE,
null,
"payment-method"
);
const supportingItemsPromise = baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.GET_SUPPORTING_ITEMS,
null,
"payment-method"
); );
const promiseResultMap = [ const promiseResultMap = [
@ -151,12 +105,12 @@ export default {
...resultMap.wipers, ...resultMap.wipers,
...clonedGlassParts, ...clonedGlassParts,
]; ];
console.log("Available Line Items", availableLineItems); const pricingResults = await baseMixin.methods.dispatchStoreActionWithLogging(
const pricingResults = await baseMixin.methods.dispatchStoreAction(
storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA, storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA,
{ {
availableLineItems: availableLineItems, availableLineItems: availableLineItems,
}, },
"payment-method",
false false
); );
@ -166,13 +120,15 @@ export default {
vm.pricedGlassParts = clonedGlassParts; vm.pricedGlassParts = clonedGlassParts;
vm.supportingItems = resultMap.supportingItems; vm.supportingItems = resultMap.supportingItems;
vm.availableLineItems = pricingResults; vm.availableLineItems = pricingResults;
vm.isInsuranceSelected = vm.getDefaultIsInsuranceSelectedValue(vm.availableLineItems); vm.availableWipers = resultMap.wipers;
vm.availableRainDefense = resultMap.rainDefense;
}); });
}, },
data() { data() {
return { return {
isInsuranceSelected: null, cartItems: [],
selectedVaps: null, availableWipers: null,
availableRainDefense: null,
availableLineItems: null, availableLineItems: null,
supportingItems: null, supportingItems: null,
pricedGlassParts: null, pricedGlassParts: null,
@ -180,34 +136,10 @@ export default {
}, },
methods: { methods: {
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
return ( return true;
store.getters.order.serviceLocation.zipCode &&
store.getters.order.serviceLocation.zipCodeCtu &&
(store.getters.order.damage.isRepair ||
(store.getters.order.lineItems?.glassParts != null &&
store.getters.order.lineItems.glassParts.length > 0)) &&
store.getters.order.referralNumber?.length !== 6
);
},
getDefaultIsInsuranceSelectedValue(availableLineItems) {
// Override if coming back from QuoteDetails. Remove override after Quote release
const isInsuranceOverrideValue = this.$route.query?.isInsurance;
const defaultIsInsuranceSelectedValue = this.$store.getters.order.payment.isInsurance;
if (isInsuranceOverrideValue != null) {
return isInsuranceOverrideValue == "true";
} else if (defaultIsInsuranceSelectedValue != null) {
return defaultIsInsuranceSelectedValue;
} else {
return availableLineItems
? baseMixin.methods.getTierOnePackagePrice(
baseMixin.methods.filterOutFees(availableLineItems)
) > 300
: null;
}
}, },
vapsItemsSelectedAction(vapsItemsSelected) { vapsItemsSelectedAction(vapsItemsSelected) {
this.selectedVaps = vapsItemsSelected; this.cartItems = vapsItemsSelected;
}, },
backButtonAction() {}, backButtonAction() {},
forwardButtonAction() { forwardButtonAction() {
@ -215,27 +147,12 @@ export default {
}, },
}, },
computed: { computed: {
subHeaderTitle() {
return this.getCmsContent("FunnelSubHeaderWidget", "HeaderText");
},
subHeaderBody() {
return this.getCmsContent("FunnelSubHeaderWidget", "BodyText");
},
forwardButtonText() {
return this.getCmsContent("FunnelFooterWidget", "ForwardButtonText");
},
vehicleInfo() {
return this.$store.getters.vehicle;
},
damageInfo() { damageInfo() {
return this.$store.getters.damage; return this.$store.getters.damage;
}, },
orderLineItems() { orderLineItems() {
return this.$store.getters.lineItems; return this.$store.getters.lineItems;
}, },
serviceLocationInfo() {
return this.$store.getters.order.serviceLocation;
},
}, },
components: { components: {
funnelHeader, funnelHeader,
@ -243,16 +160,8 @@ export default {
funnelSubHeader, funnelSubHeader,
Form, Form,
loadingModal, loadingModal,
// servicePackageReview,
serviceLocationReview,
vehicleReview,
damageReview,
cart, cart,
addVapsModalButtons,
}, },
}; };
</script> </script>
<style scoped>
.text-block {
display: block;
}
</style>