DigitalConsumer.FixMyGlass/src/layouts/payment-method/promo-modal-question/promo-modal-question.vue
2023-11-29 09:25:57 -05:00

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>