Merge pull request #1492 from Safelite/feature/CSR-1384-modals-for-add-vaps

Feature/CSR-1384 modals for add vaps
This commit is contained in:
AdamCaouetteSafelite 2023-11-06 15:37:57 -05:00 committed by GitHub
commit 29b6d715d9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 129 additions and 47 deletions

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="cart"> <div class="cart mb-4">
<div class="px-0"> <div class="px-0">
<div <div
class="row vin-toggle flex align-items-center" class="row vin-toggle flex align-items-center"

View file

@ -5,6 +5,8 @@
<img class="logo-image img-fluid" :src="imageSrc" alt="Safelite logo" /> <img class="logo-image img-fluid" :src="imageSrc" alt="Safelite logo" />
<menuModal /> <menuModal />
</div> </div>
<transition name="fade" mode="out-in">
<alert <alert
v-if="displayGlobalAlert" v-if="displayGlobalAlert"
class="rounded-0 w-100 border-0 shadow-sm" class="rounded-0 w-100 border-0 shadow-sm"
@ -12,7 +14,9 @@
:manualHeadline="globalAlertMessage.messageHeadline" :manualHeadline="globalAlertMessage.messageHeadline"
:manualCopy="globalAlertMessage.messageCopy" :manualCopy="globalAlertMessage.messageCopy"
:alertClass="globalAlertMessage.type" :alertClass="globalAlertMessage.type"
:onAlertCloseCallback="onAlertClose"
v-bind:isDismissible="globalAlertMessage.isDismissible" /> v-bind:isDismissible="globalAlertMessage.isDismissible" />
</transition>
</template> </template>
<script> <script>
@ -21,6 +25,9 @@ import eventBus from "@/helpers/event-bus/event-bus";
import { globalEvents } from "@/constants/events"; import { globalEvents } from "@/constants/events";
import menuModal from "@/fmg-components/funnel-header/menu-modal/menu-modal"; import menuModal from "@/fmg-components/funnel-header/menu-modal/menu-modal";
// Constants
const ALERT_DURATION = 3000; // millisecond time to display alert before dismissal
export default { export default {
name: "funnel-header", name: "funnel-header",
data() { data() {
@ -32,6 +39,7 @@ export default {
messageHeadline: "", messageHeadline: "",
type: "", type: "",
}, },
countdownTimer: () => {},
}; };
}, },
props: { props: {
@ -42,6 +50,21 @@ export default {
return this.getCmsContent(this.cmsWidgetName, "LogoImage"); return this.getCmsContent(this.cmsWidgetName, "LogoImage");
}, },
}, },
methods: {
triggerGlobalAlert(alertEvent, isAutoDismissing) {
clearTimeout(this.countdownTimer);
this.displayGlobalAlert = true;
this.globalAlertMessage = alertEvent;
if (isAutoDismissing) {
this.countdownTimer = setTimeout(() => {
this.displayGlobalAlert = false;
}, ALERT_DURATION);
}
},
onAlertClose() {
this.displayGlobalAlert = false;
},
},
components: { components: {
alert, alert,
menuModal, menuModal,

View file

@ -4,7 +4,7 @@
<div class="container-fluid page-container-grouped-styles"> <div class="container-fluid page-container-grouped-styles">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6"> <div class="col-md-6">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
</div> </div>
</div> </div>
<div class="row justify-content-center"> <div class="row justify-content-center">

View file

@ -3,7 +3,7 @@
<div class="container-fluid page-container-grouped-styles"> <div class="container-fluid page-container-grouped-styles">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6"> <div class="col-md-6">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
</div> </div>
</div> </div>
<div class="row justify-content-center"> <div class="row justify-content-center">

View file

@ -1,7 +1,7 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm"> <Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm">
<div class="page-container-grouped-styles position-relative"> <div class="page-container-grouped-styles position-relative">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
<div> <div>
<div class="container-fluid pb-2"> <div class="container-fluid pb-2">
<div class="row justify-content-center"> <div class="row justify-content-center">

View file

@ -3,7 +3,7 @@
<div class="container-fluid"> <div class="container-fluid">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6"> <div class="col-md-6">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
</div> </div>
</div> </div>
<div class="row justify-content-center"> <div class="row justify-content-center">

View file

@ -4,7 +4,7 @@
<div class="container-fluid page-container-grouped-styles"> <div class="container-fluid page-container-grouped-styles">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6"> <div class="col-md-6">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
</div> </div>
</div> </div>
<div class="row justify-content-center"> <div class="row justify-content-center">

View file

@ -3,7 +3,7 @@
<div class="container-fluid page-container-grouped-styles"> <div class="container-fluid page-container-grouped-styles">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6"> <div class="col-md-6">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
</div> </div>
</div> </div>
<div class="row justify-content-center"> <div class="row justify-content-center">

View file

@ -61,6 +61,7 @@ import { partTypeStrings } from "@/constants/part-type-strings";
import { defineRule } from "vee-validate"; import { defineRule } from "vee-validate";
import { required } from "@/helpers/validation-rules"; import { required } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages"; import { errorMessages } from "@/constants/error-messages";
import { processIfStatements } from "@/helpers/cms-content-helper";
// DEFINE VALIDATION RULES // DEFINE VALIDATION RULES
defineRule("checkbox-required", required(errorMessages.OPTION_REQUIRED)); defineRule("checkbox-required", required(errorMessages.OPTION_REQUIRED));
@ -87,20 +88,23 @@ export default {
}; };
}, },
methods: { methods: {
processIfStatements,
openModal(modalName) { openModal(modalName) {
this.selectedWipers = this.getSelectedWipers(); // reset selectedWipers to null upon modal open
this.$refs[modalName].openModal(); this.$refs[modalName].openModal();
}, },
closeModal(modalName) { closeModal(modalName) {
this.$refs[modalName].closeModal(); this.$refs[modalName].closeModal();
}, },
addRainDefenseToCart(modalName, part) { addRainDefenseToCart(modalName, part) {
const alreadyInCart = this.currentCartItems.vaps.some((item) => {
return item.partType === part.partType;
});
if (!alreadyInCart) {
this.currentCartItems.vaps.push(part); this.currentCartItems.vaps.push(part);
}
this.closeModal(modalName); this.closeModal(modalName);
const message = this.getRainDefenseAddedMessage();
this.$emit("added-to-cart", {
messageHeadline: message,
type: "alert-success",
isDismissible: true,
});
}, },
addWipersToCart(modalName, part) { addWipersToCart(modalName, part) {
const itemsForCart = []; const itemsForCart = [];
@ -122,9 +126,11 @@ export default {
this.wipersOffered === wipersOfferedStrings.FRONT this.wipersOffered === wipersOfferedStrings.FRONT
) { ) {
itemsForCart.push(wipersOfferedStrings.FRONT); itemsForCart.push(wipersOfferedStrings.FRONT);
this.selectedWipers = ["FRONT"];
} }
if (!rearWipersAlreadyInCart && this.wipersOffered === wipersOfferedStrings.REAR) { if (!rearWipersAlreadyInCart && this.wipersOffered === wipersOfferedStrings.REAR) {
itemsForCart.push(wipersOfferedStrings.REAR); itemsForCart.push(wipersOfferedStrings.REAR);
this.selectedWipers = ["REAR"];
} }
} }
@ -142,7 +148,13 @@ export default {
}); });
this.closeModal(modalName); this.closeModal(modalName);
this.selectedWipers;
const message = this.getWipersAddedMessage();
this.$emit("added-to-cart", {
messageHeadline: message,
type: "alert-success",
isDismissible: true,
});
}, },
updateWipersOffered(value) { updateWipersOffered(value) {
this.wipersOffered = value; this.wipersOffered = value;
@ -150,6 +162,34 @@ export default {
getSelectedWipers() { getSelectedWipers() {
return null; return null;
}, },
getRainDefenseAddedMessage() {
return this.AddRainDefenseSuccessMessageFromCms;
},
getWipersAddedMessage() {
return this.processIfStatements(
this.AddWipersSuccessMessageFromCms,
"custom",
this.getCustomValueFromString
);
},
getCustomValueFromString(str) {
switch (str) {
case "vapsAddedIsFrontWipers":
return (
this.selectedWipers?.length === 1 &&
this.selectedWipers[0] === wipersOfferedStrings.FRONT
);
case "vapsAddedIsRearWipers":
return (
this.selectedWipers?.length === 1 &&
this.selectedWipers[0] === wipersOfferedStrings.REAR
);
case "vapsAddedIsBothWipers":
return this.selectedWipers?.length > 1;
default:
return null;
}
},
}, },
computed: { computed: {
currentCartItems() { currentCartItems() {
@ -301,6 +341,12 @@ export default {
return wipersOptions; return wipersOptions;
}, },
AddRainDefenseSuccessMessageFromCms() {
return this.getCmsContent("AddRainDefenseSuccessMessageWidget", "Text");
},
AddWipersSuccessMessageFromCms() {
return this.getCmsContent("AddWipersSuccessMessageWidget", "Text");
},
}, },
components: { components: {
buttonQuestion, buttonQuestion,

View file

@ -4,7 +4,7 @@
<div class="container-fluid page-container-grouped-styles"> <div class="container-fluid page-container-grouped-styles">
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
</div> </div>
</div> </div>
<div class="row justify-content-center"> <div class="row justify-content-center">
@ -43,7 +43,8 @@
<add-vaps-modal-buttons <add-vaps-modal-buttons
v-model="lineItems" v-model="lineItems"
:availableLineItems="availableLineItems" :availableLineItems="availableLineItems"
vapsTilesCmsName="VapsProductTiles" /> vapsTilesCmsName="VapsProductTiles"
@added-to-cart="showAlert" />
<paymentMethodQuestion <paymentMethodQuestion
v-if="!isPiaDisabled" v-if="!isPiaDisabled"
@ -370,6 +371,9 @@ export default {
this.$route this.$route
); );
}, },
showAlert(alert) {
this.$refs.funnelHeader.triggerGlobalAlert(alert, true);
},
}, },
computed: { computed: {
damageInfo() { damageInfo() {

View file

@ -4,7 +4,7 @@
<div class="container-fluid"> <div class="container-fluid">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6"> <div class="col-md-6">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
</div> </div>
</div> </div>

View file

@ -4,7 +4,7 @@
<div class="container-fluid page-container-grouped-styles"> <div class="container-fluid page-container-grouped-styles">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6"> <div class="col-md-6">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
</div> </div>
</div> </div>
<div class="row justify-content-center"> <div class="row justify-content-center">

View file

@ -4,7 +4,7 @@
<div class="container-fluid"> <div class="container-fluid">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6"> <div class="col-md-6">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
</div> </div>
</div> </div>
<div class="row justify-content-center"> <div class="row justify-content-center">

View file

@ -4,7 +4,7 @@
<div class="container-fluid page-container-grouped-styles"> <div class="container-fluid page-container-grouped-styles">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6"> <div class="col-md-6">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
</div> </div>
</div> </div>
<div class="row justify-content-center"> <div class="row justify-content-center">

View file

@ -4,7 +4,7 @@
<div class="container-fluid page-container-grouped-styles"> <div class="container-fluid page-container-grouped-styles">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6"> <div class="col-md-6">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
</div> </div>
</div> </div>
<div class="row justify-content-center"> <div class="row justify-content-center">

View file

@ -3,7 +3,7 @@
<div class="container-fluid page-container-grouped-styles vehicle-damage"> <div class="container-fluid page-container-grouped-styles vehicle-damage">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6"> <div class="col-md-6">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
</div> </div>
</div> </div>
<div class="row justify-content-center"> <div class="row justify-content-center">

View file

@ -5,7 +5,7 @@
<div class="container-fluid page-container-grouped-styles"> <div class="container-fluid page-container-grouped-styles">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6"> <div class="col-md-6">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
</div> </div>
</div> </div>
<div class="row justify-content-center"> <div class="row justify-content-center">

View file

@ -3,7 +3,7 @@
<div class="container-fluid"> <div class="container-fluid">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6"> <div class="col-md-6">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
</div> </div>
</div> </div>
<div class="row justify-content-center"> <div class="row justify-content-center">

View file

@ -4,7 +4,7 @@
<div class="container-fluid page-container-grouped-styles"> <div class="container-fluid page-container-grouped-styles">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6"> <div class="col-md-6">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
</div> </div>
</div> </div>
<div class="row justify-content-center"> <div class="row justify-content-center">

View file

@ -2,18 +2,19 @@
<div <div
class="alert fade show text-center mb-0 py-2 px-4 border-0" class="alert fade show text-center mb-0 py-2 px-4 border-0"
role="alert" role="alert"
v-on="{ 'close.bs.alert': onAlertClose }"
:class="[ :class="[
isDismissible ? 'alert-dismissible' : '', isDismissible ? 'alert-dismissible' : '',
this.alertClass, this.alertClass,
this.cssClassNameForCmsWidget, this.cssClassNameForCmsWidget,
]"> ]">
<p class="m-0 fw-bold alert-heading">{{ alertHeadline }}</p> <p class="mx-6 my-0 fw-bold alert-heading">{{ alertHeadline }}</p>
<template v-for="paragraph in splitAlertCopyForParagraphTag" :key="paragraph"> <template v-for="paragraph in splitAlertCopyForParagraphTag" :key="paragraph">
<p <p
class="m-0 text-body small" class="mx-4 mt-1 mb-0 text-body small"
v-if="!doesCopyContainRouterLink(paragraph) && !doesCopyContainTextLink(paragraph)" v-if="!doesCopyContainRouterLink(paragraph) && !doesCopyContainTextLink(paragraph)"
v-html="paragraph"></p> v-html="paragraph"></p>
<p class="m-0 text-body small" v-else> <p class="mx-4 mt-1 mb-0 text-body small" v-else>
<template v-if="doesCopyContainRouterLink(paragraph)"> <template v-if="doesCopyContainRouterLink(paragraph)">
<textBlock :customText="paragraph" class="mb-1" marginTopSizeOverride="0" /> <textBlock :customText="paragraph" class="mb-1" marginTopSizeOverride="0" />
</template> </template>
@ -32,7 +33,7 @@
</template> </template>
</p> </p>
</template> </template>
<button type="button" class="btn-close p-2" data-bs-dismiss="alert" aria-label="Close"> <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23.7 23.7" xml:space="preserve"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23.7 23.7" xml:space="preserve">
<path <path
d="m23.24 2.7-9.15 9.15L23.24 21a1.581 1.581 0 0 1-1.12 2.7c-.42 0-.82-.16-1.12-.46l-9.15-9.15-9.15 9.15c-.3.3-.7.46-1.12.46A1.581 1.581 0 0 1 .46 21l8.47-8.47.68-.68L.46 2.7c-.62-.62-.62-1.62 0-2.24.62-.62 1.62-.62 2.24 0l8.47 8.47.68.68L21 .46a1.57 1.57 0 0 1 2.23 0c.63.62.63 1.62.01 2.24z" /> d="m23.24 2.7-9.15 9.15L23.24 21a1.581 1.581 0 0 1-1.12 2.7c-.42 0-.82-.16-1.12-.46l-9.15-9.15-9.15 9.15c-.3.3-.7.46-1.12.46A1.581 1.581 0 0 1 .46 21l8.47-8.47.68-.68L.46 2.7c-.62-.62-.62-1.62 0-2.24.62-.62 1.62-.62 2.24 0l8.47 8.47.68.68L21 .46a1.57 1.57 0 0 1 2.23 0c.63.62.63 1.62.01 2.24z" />
@ -58,6 +59,7 @@ export default {
name: "alert", name: "alert",
props: { props: {
isDismissible: Boolean, isDismissible: Boolean,
alertClass: String,
/* /*
alertClass class names: alertClass class names:
alert-success (green) alert-success (green)
@ -65,7 +67,6 @@ export default {
alert-warning (yellow) alert-warning (yellow)
alert-info (blue) alert-info (blue)
*/ */
alertClass: String,
cmsWidgetName: { cmsWidgetName: {
type: String, type: String,
default(rawProps) { default(rawProps) {
@ -81,6 +82,9 @@ export default {
type: Boolean, type: Boolean,
default: true, default: true,
}, },
onAlertCloseCallback: {
type: Function,
},
}, },
computed: { computed: {
pageQueryString() { pageQueryString() {
@ -124,6 +128,9 @@ export default {
document.documentElement.clientHeight - footerHeight) document.documentElement.clientHeight - footerHeight)
); );
}, },
onAlertClose() {
this.onAlertCloseCallback?.();
},
}, },
mounted() { mounted() {
this.ensureAlertIsInViewPort(); this.ensureAlertIsInViewPort();
@ -137,6 +144,8 @@ export default {
<style lang="scss"> <style lang="scss">
.alert { .alert {
padding: 0.675rem 0;
button { button {
display: none; display: none;
} }
@ -145,12 +154,13 @@ export default {
opacity: 1; opacity: 1;
width: 0.75rem; width: 0.75rem;
height: 0.75rem; height: 0.75rem;
padding: 0;
} }
&.alert-dismissible { &.alert-dismissible {
button { button {
display: flex; display: flex;
top: 2px; top: 0.875rem;
right: 2px; right: 1rem;
} }
} }
&.alert-info { &.alert-info {
@ -199,7 +209,6 @@ export default {
} }
& p { & p {
font-size: 0.875rem; font-size: 0.875rem;
margin-bottom: 0.25rem;
} }
} }
</style> </style>