341 lines
13 KiB
Vue
341 lines
13 KiB
Vue
<template>
|
|
<div class="promo-modal-question">
|
|
<textLink
|
|
id="promoLinkPromptId"
|
|
linkType="text"
|
|
:text="promoLinkText"
|
|
href="#!"
|
|
@click-event="openModal" />
|
|
<modal
|
|
:ref="modalName"
|
|
:headerText="modalHeaderText"
|
|
:onModalClosedCallback="onModalClosed"
|
|
:onModalOpenedCallback="focusOnPromoInput"
|
|
@keypress.enter="onEnter"
|
|
:footerButtonText="modalFooterText"
|
|
@footer-button-event="addPromoCode">
|
|
<promoQuestion
|
|
ref="promoQuestion"
|
|
customInputId="promoCode"
|
|
v-model="promoCode"
|
|
v-on="{ 'textboxQuestionEvent.inputIdAssigned': onInputIdAssigned }"
|
|
cmsWidgetName="PromoQuestionWidget" />
|
|
<alert
|
|
ref="alertInvalidPromo"
|
|
v-if="displayInvalidPromoAlert"
|
|
v-html="errorMessage"
|
|
class="mt-4 alert"
|
|
cmsWidgetName="AlertInvalidPromoWidget"
|
|
alertClass="alert-danger"
|
|
v-bind:isDismissible="false" />
|
|
<alert
|
|
ref="AlertSimilarPromo"
|
|
v-if="displaySimilarPromoAlert"
|
|
class="mt-4 alert"
|
|
cmsWidgetName="AlertSimilarPromoWidget"
|
|
alertClass="alert-danger"
|
|
v-bind:isDismissible="false" />
|
|
<alert
|
|
ref="alertInvalidStackingPromo"
|
|
v-if="displayStackingPromoAlert"
|
|
v-html="errorMessage"
|
|
class="mt-4 alert"
|
|
cmsWidgetName="AlertStackingPromoWidget"
|
|
alertClass="alert-danger"
|
|
v-bind:isDismissible="false" />
|
|
<alert
|
|
ref="AlertInShopPromo"
|
|
v-if="displayInShopPromoAlert"
|
|
v-html="errorMessage"
|
|
class="mt-4 alert"
|
|
cmsWidgetName="AlertInshopPromoWidget"
|
|
alertClass="alert-danger"
|
|
v-bind:isDismissible="false" />
|
|
<div class="mt-4" v-for="(promoCode, i) in this.getPromoCodeList()" :key="i">
|
|
<span class="applied-promo">Promo code "{{ promoCode }}" applied </span>
|
|
<textLink
|
|
ref="removeLink"
|
|
linkType="text"
|
|
:text="removeLinkText"
|
|
href="#!"
|
|
@click-event="removeItem(promoCode)" />
|
|
</div>
|
|
</modal>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
import textLink from "@/ux-components/text-link/text-link";
|
|
import promoQuestion from "@/layouts/payment-method/promo-modal-question/promo-question/promo-question";
|
|
import modal from "@/digital-components/modal/modal";
|
|
import alert from "@/ux-components/alert/alert";
|
|
import { storeActions } from "@/constants/store-actions.js";
|
|
import {
|
|
getVapsThatNeedToBeAddedToSatisfyPromos,
|
|
promoErrorCodes,
|
|
getPromoCodesFromPromoObjectsWithoutDuplicates,
|
|
getPromoCodeWithoutBundleIdentifier,
|
|
} from "@/helpers/promotions-helper";
|
|
import { deepClone } from "@/helpers/object-helper";
|
|
import baseMixin from "@/mixins/base-mixin.js";
|
|
import { cartItemCategories } from "@/constants/cart-item-categories";
|
|
import store from "@/store";
|
|
import { mapTaxedLineItemsToStoreFormat } from "@/store";
|
|
export default {
|
|
name: "promo-modal-question",
|
|
|
|
data() {
|
|
return {
|
|
promoCode: "",
|
|
promoTextInputId: "",
|
|
errorMessage: "",
|
|
displayInvalidPromoAlert: false,
|
|
displaySimilarPromoAlert: false,
|
|
displayStackingPromoAlert: false,
|
|
displayInShopPromoAlert: false,
|
|
lineItems: deepClone(this.modelValue),
|
|
};
|
|
},
|
|
props: {
|
|
modelValue: Object,
|
|
modalWidgetName: String,
|
|
availableVaps: Object,
|
|
},
|
|
computed: {
|
|
promoLinkText() {
|
|
return this.getCmsContent(this.modalWidgetName, "HeaderText");
|
|
},
|
|
modalHeaderText() {
|
|
return this.getCmsContent("PromoQuestionWidget", "QuestionText");
|
|
},
|
|
modalFooterText() {
|
|
return this.getCmsContent(this.modalWidgetName, "FooterText");
|
|
},
|
|
modalName() {
|
|
return this.modalWidgetName;
|
|
},
|
|
modal() {
|
|
return this.$refs[this.modalName];
|
|
},
|
|
StackPromoText() {
|
|
return this.getCmsContent("AlertStackingPromoWidget", "HeadlineText");
|
|
},
|
|
PromoText() {
|
|
return this.getCmsContent("AlertInvalidPromoWidget", "HeadlineText");
|
|
},
|
|
InShopPromoText() {
|
|
return this.getCmsContent("AlertInshopPromoWidget", "HeadlineText");
|
|
},
|
|
removeLinkText() {
|
|
return this.getCmsContent("RemoveCartItemTextWidget", "Text");
|
|
},
|
|
},
|
|
methods: {
|
|
resetAlerts() {
|
|
this.displayStackingPromoAlert = false;
|
|
this.displayInvalidPromoAlert = false;
|
|
this.displaySimilarPromoAlert = false;
|
|
this.displayInShopPromoAlert = false;
|
|
this.errorMessage = "";
|
|
},
|
|
resetsOnPromoInput() {
|
|
this.resetAlerts();
|
|
},
|
|
getPromoCodeList() {
|
|
const promosToDisplay = getPromoCodesFromPromoObjectsWithoutDuplicates(
|
|
this.lineItems.promos
|
|
);
|
|
return promosToDisplay;
|
|
},
|
|
getConflictingPromoCode(additionalInfo) {
|
|
var conflictingCodes = additionalInfo?.[0];
|
|
if (additionalInfo?.length > 1) {
|
|
if (additionalInfo?.length > 2) conflictingCodes += ",";
|
|
for (let i = 1; i < additionalInfo?.length - 1; i++) {
|
|
conflictingCodes += ` "${additionalInfo[i]},`;
|
|
}
|
|
conflictingCodes += `" and "${additionalInfo?.slice(-1)[0]}`;
|
|
}
|
|
return conflictingCodes;
|
|
},
|
|
onInputIdAssigned(inputId) {
|
|
this.promoTextInputId = inputId;
|
|
},
|
|
openModal() {
|
|
this.modal.openModal();
|
|
},
|
|
onEnter(e) {
|
|
e.preventDefault();
|
|
this.modal.validateAndEmit();
|
|
},
|
|
closeModal() {
|
|
this.modal.closeModal();
|
|
},
|
|
onModalClosed() {
|
|
this.resetAlerts();
|
|
this.$emit("update:modelValue", this.lineItems);
|
|
this.promoCode = "";
|
|
this.modal.resetForm();
|
|
},
|
|
focusOnPromoInput() {
|
|
const input = document.getElementById(this.promoTextInputId);
|
|
input?.focus();
|
|
},
|
|
resetModalButtonStyle() {
|
|
this.modal.resetButtonStyle();
|
|
},
|
|
async getPromoCodeData(promoCode, lineItems, availableVaps, pageNameToLog = null) {
|
|
const pageName = pageNameToLog ?? this.$options?.name;
|
|
const promoValidationResponse = await baseMixin.methods.dispatchStoreActionWithLogging(
|
|
storeActions.VALIDATE_ORDER_PROMO_AND_SAVE_SERVER_DATA,
|
|
{
|
|
promoCode: promoCode,
|
|
lineItemsToUse: lineItems,
|
|
addableVaps: availableVaps,
|
|
},
|
|
pageName,
|
|
false
|
|
);
|
|
|
|
return {
|
|
isValid: !("errorCode" in promoValidationResponse),
|
|
promoCode: promoValidationResponse?.orderPromos,
|
|
errorCode: promoValidationResponse?.errorCode,
|
|
additionalInfo: promoValidationResponse?.additionalInfo,
|
|
};
|
|
},
|
|
removeItem(promo) {
|
|
this.lineItems[cartItemCategories.PROMOS] = this.lineItems[
|
|
cartItemCategories.PROMOS
|
|
].filter(
|
|
(lineItemsToKeep) =>
|
|
getPromoCodeWithoutBundleIdentifier(lineItemsToKeep.promoCode) != promo
|
|
);
|
|
},
|
|
getErrorMessage(error, additionalInfo) {
|
|
switch (error) {
|
|
case promoErrorCodes.PROMO_STACKING_NOT_ALLOWED:
|
|
case promoErrorCodes.SIMILAR_PROMO_ALREADY_ON_ORDER:
|
|
return additionalInfo.length === 1 &&
|
|
additionalInfo[0] === this.promoCode.toUpperCase()
|
|
? (this.displaySimilarPromoAlert = true)
|
|
: ((this.errorMessage = this.StackPromoText?.replace(
|
|
/\{custom:(NEW|OLD)PROMOCODE\}/g,
|
|
(match, group) =>
|
|
group === "NEW"
|
|
? this.promoCode.toUpperCase()
|
|
: this.getConflictingPromoCode(additionalInfo)
|
|
)),
|
|
(this.displayStackingPromoAlert = true));
|
|
case promoErrorCodes.INVALID_PROMO_ON_ORDER:
|
|
if (additionalInfo.some((x) => x.toUpperCase() === "APPOINTMENT_TYPE")) {
|
|
this.errorMessage = this.InShopPromoText?.replaceAll(
|
|
"{custom:INSHOPPROMOCODE}",
|
|
this.promoCode.toUpperCase()
|
|
);
|
|
return (this.displayInShopPromoAlert = true);
|
|
} else {
|
|
this.errorMessage = this.PromoText?.replaceAll(
|
|
"{custom:PROMOCODE}",
|
|
this.promoCode.toUpperCase()
|
|
);
|
|
return (this.displayInvalidPromoAlert = true);
|
|
}
|
|
default:
|
|
this.errorMessage = this.PromoText?.replaceAll(
|
|
"{custom:PROMOCODE}",
|
|
this.promoCode.toUpperCase()
|
|
);
|
|
return (this.displayInvalidPromoAlert = true);
|
|
}
|
|
},
|
|
async addPromoCode() {
|
|
if (this.promoCode) {
|
|
this.resetAlerts();
|
|
|
|
const promoCodeData = await this.getPromoCodeData(
|
|
this.promoCode,
|
|
this.lineItems,
|
|
this.availableVaps,
|
|
"payment-method"
|
|
);
|
|
|
|
if (promoCodeData.isValid) {
|
|
const pricedLineItemsToTax = [];
|
|
pricedLineItemsToTax.push(...promoCodeData.promoCode);
|
|
const taxedLineItems = await baseMixin.methods.dispatchStoreActionWithLogging(
|
|
storeActions.TAX_ORDER_ITEMS_AND_SAVE_SERVER_DATA,
|
|
{
|
|
billToAccountNumber: "87291",
|
|
providerNumber:
|
|
store.getters.order.serviceLocation.provider.providerNumber,
|
|
appointmentType: store.getters.order.serviceLocation.appointmentType,
|
|
serviceLocationCity: store.getters.order.serviceLocation.city,
|
|
serviceLocationState: store.getters.order.serviceLocation.state,
|
|
serviceLocationZipCode: store.getters.order.serviceLocation.zipCode,
|
|
pricedLineItems: pricedLineItemsToTax,
|
|
},
|
|
"payment-method",
|
|
false
|
|
);
|
|
// Match all line items to the line items as they are in the store
|
|
// and rebuild the original structure.
|
|
this.lineItems = mapTaxedLineItemsToStoreFormat(taxedLineItems, this.lineItems);
|
|
const taxedVaps = mapTaxedLineItemsToStoreFormat(
|
|
taxedLineItems,
|
|
this.availableVaps
|
|
);
|
|
|
|
const getVaps = getVapsThatNeedToBeAddedToSatisfyPromos(
|
|
promoCodeData.promoCode,
|
|
taxedVaps,
|
|
this.lineItems
|
|
);
|
|
this.lineItems?.promos.push(...promoCodeData.promoCode);
|
|
this.lineItems.vaps?.push(...getVaps);
|
|
this.closeModal();
|
|
} else {
|
|
this.getErrorMessage(promoCodeData.errorCode, promoCodeData.additionalInfo);
|
|
this.focusOnPromoInput();
|
|
this.resetModalButtonStyle();
|
|
}
|
|
}
|
|
},
|
|
},
|
|
watch: {
|
|
promoCode() {
|
|
this.displayInvalidPromoAlert = false;
|
|
this.displayStackingPromoAlert = false;
|
|
this.displaySimilarPromoAlert = false;
|
|
this.displayInShopPromoAlert = false;
|
|
},
|
|
modelValue: {
|
|
handler(newValue) {
|
|
this.lineItems = deepClone(newValue);
|
|
},
|
|
deep: true,
|
|
},
|
|
},
|
|
|
|
components: {
|
|
textLink,
|
|
promoQuestion,
|
|
modal,
|
|
alert,
|
|
},
|
|
};
|
|
</script>
|
|
<style lang="scss" scoped>
|
|
.applied-promo {
|
|
padding-right: 4rem;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
line-height: 24px;
|
|
color: #0c7e47;
|
|
}
|
|
.alert {
|
|
color: #d4281c;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
line-height: 24px;
|
|
}
|
|
</style>
|