Merge pull request #3303 from Safelite/feature/CASH-1524
Feature/CASH 1524
This commit is contained in:
commit
f42aca6636
16 changed files with 602 additions and 70 deletions
10
src/constants/save-progress-cms-widgets.js
Normal file
10
src/constants/save-progress-cms-widgets.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
const saveProgressCmsWidgets = {
|
||||
PHONE_SPECIFIC: "SaveProgressPopupWidget_PhoneSpecific",
|
||||
EMAIL_SPECIFIC: "SaveProgressPopupWidget_EmailSpecific",
|
||||
MODAL_PHONE_QUESTION: "SaveProgressPhoneQuestionWidget",
|
||||
MODAL_EMAIL_QUESTION: "SaveProgressEmailQuestionWidget",
|
||||
POPUP_PHONE_QUESTION: "SaveProgressPopupPhoneQuestionWidget",
|
||||
POPUP_EMAIL_QUESTION: "SaveProgressPopupEmailQuestionWidget",
|
||||
};
|
||||
|
||||
export { saveProgressCmsWidgets };
|
||||
|
|
@ -1,43 +1,261 @@
|
|||
import { mount, shallowMount } from "@vue/test-utils";
|
||||
import { mount } from "@vue/test-utils";
|
||||
import saveProgressModalQuestion from "./save-progress-modal-question";
|
||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||
import { storeActions } from "@/constants/store-actions";
|
||||
import { saveProgressCmsWidgets } from "@/constants/save-progress-cms-widgets";
|
||||
import { saveQuote } from "@/helpers/heritage-integration/order-helper.js";
|
||||
import experimentMixin from "@/mixins/experiment-mixin.js";
|
||||
import { experimentSettings } from "@/constants/experiments";
|
||||
|
||||
jest.mock("@/helpers/heritage-integration/order-helper.js", () => ({
|
||||
saveQuote: jest.fn().mockResolvedValue(undefined),
|
||||
}));
|
||||
|
||||
jest.mock("@/helpers/save-progress-sms-consent/save-progress-sms-consent-helper", () => ({
|
||||
...jest.requireActual("@/helpers/save-progress-sms-consent/save-progress-sms-consent-helper"),
|
||||
getSaveProgressSmsConsentConfig: jest.fn().mockReturnValue({
|
||||
copy: {
|
||||
transactional: "Sign me up for updates about my upcoming service.",
|
||||
marketing: "Sign me up for promotional and product offers.",
|
||||
},
|
||||
value: { transactional: false, marketing: false },
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock("@/digital-components/modal/modal", () => ({
|
||||
methods: {
|
||||
openModal: jest.fn(),
|
||||
closeModal: jest.fn(),
|
||||
resetButtonStyle: jest.fn(),
|
||||
resetForm: jest.fn(),
|
||||
validate: jest.fn().mockResolvedValue({ valid: true }),
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock("@/ux-components/alert/alert", () => ({
|
||||
name: "alert",
|
||||
template: "<div />",
|
||||
}));
|
||||
|
||||
describe("save-progress-modal-question", () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
experimentMixin.methods.hasSettingEqualTo = jest.fn().mockReturnValue(false);
|
||||
});
|
||||
|
||||
describe("when openModal is run", () => {
|
||||
test("the modal should open", () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({
|
||||
props: {
|
||||
modelValue: "",
|
||||
modalWidgetName: "testModal",
|
||||
},
|
||||
});
|
||||
|
||||
// Act
|
||||
wrapper.vm.openModal();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.modal).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("legacy email-only mode", () => {
|
||||
test("should save email to the store", async () => {
|
||||
const { wrapper, dispatchStoreAction } = setupMocks({
|
||||
props: {
|
||||
modalWidgetName: "SaveProgressModalWidget",
|
||||
pageName: "part-questions",
|
||||
},
|
||||
});
|
||||
|
||||
function setupMocks({ options, props }) {
|
||||
wrapper.vm.userInput = "test@example.com";
|
||||
await wrapper.vm.saveProgress();
|
||||
|
||||
expect(dispatchStoreAction).toHaveBeenCalledWith(
|
||||
storeActions.SAVE_EMAIL,
|
||||
"test@example.com",
|
||||
false
|
||||
);
|
||||
expect(saveQuote).toHaveBeenCalledWith({ pageNameToLog: "part-questions" });
|
||||
expect(wrapper.vm.isProgressSaved).toBe(true);
|
||||
});
|
||||
|
||||
test("should use the legacy disclaimer widget", () => {
|
||||
const { wrapper } = setupMocks({
|
||||
props: {
|
||||
modalWidgetName: "SaveProgressModalWidget",
|
||||
},
|
||||
cmsContent: {
|
||||
SaveProgressModalWidget: { FooterText2: "Legacy disclaimer" },
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.vm.modalDisclaimerText).toBe("Legacy disclaimer");
|
||||
});
|
||||
});
|
||||
|
||||
describe("consent management mode", () => {
|
||||
beforeEach(() => {
|
||||
experimentMixin.methods.hasSettingEqualTo = jest
|
||||
.fn()
|
||||
.mockImplementation(
|
||||
(settingName, settingValue) =>
|
||||
settingName === experimentSettings.SHOW_CONSENT_MANAGEMENT &&
|
||||
settingValue === "true"
|
||||
);
|
||||
});
|
||||
|
||||
test("should default to the phone tab when opened", () => {
|
||||
const { wrapper } = setupMocks({
|
||||
props: {
|
||||
modalWidgetName: "SaveProgressModalWidget",
|
||||
},
|
||||
});
|
||||
|
||||
wrapper.vm.openModal();
|
||||
|
||||
expect(wrapper.vm.isPhoneTabSelected).toBe(true);
|
||||
});
|
||||
|
||||
test("should use popup-specific tab labels from CMS widgets", () => {
|
||||
const { wrapper } = setupMocks({
|
||||
props: {
|
||||
modalWidgetName: "SaveProgressModalWidget",
|
||||
},
|
||||
cmsContent: {
|
||||
SaveProgressPopupWidget_PhoneSpecific: { HeadlineText: "PHONE" },
|
||||
SaveProgressPopupWidget_EmailSpecific: { HeadlineText: "EMAIL" },
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.vm.contactTabs).toEqual([
|
||||
{ label: "PHONE", value: "PhoneAnswer" },
|
||||
{ label: "EMAIL", value: "EmailAnswer" },
|
||||
]);
|
||||
});
|
||||
|
||||
test("should use the modal question CMS widgets for phone and email inputs", () => {
|
||||
const { wrapper } = setupMocks({
|
||||
props: {
|
||||
modalWidgetName: "SaveProgressModalWidget",
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.vm.phoneQuestionWidgetName).toBe(
|
||||
saveProgressCmsWidgets.MODAL_PHONE_QUESTION
|
||||
);
|
||||
expect(wrapper.vm.emailQuestionWidgetName).toBe(
|
||||
saveProgressCmsWidgets.MODAL_EMAIL_QUESTION
|
||||
);
|
||||
});
|
||||
|
||||
test("should use the email-specific popup disclaimer on the email tab", async () => {
|
||||
const { wrapper } = setupMocks({
|
||||
props: {
|
||||
modalWidgetName: "SaveProgressModalWidget",
|
||||
},
|
||||
cmsContent: {
|
||||
SaveProgressPopupWidget_EmailSpecific: { BodyText: "Email disclaimer" },
|
||||
SaveProgressPopupWidget_PhoneSpecific: { BodyText: "Phone disclaimer" },
|
||||
},
|
||||
});
|
||||
|
||||
wrapper.vm.selectContactMethod("EmailAnswer");
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(wrapper.vm.modalDisclaimerText).toBe("Email disclaimer");
|
||||
});
|
||||
|
||||
test("should use the phone-specific popup disclaimer on the phone tab", () => {
|
||||
const { wrapper } = setupMocks({
|
||||
props: {
|
||||
modalWidgetName: "SaveProgressModalWidget",
|
||||
},
|
||||
cmsContent: {
|
||||
SaveProgressPopupWidget_EmailSpecific: { BodyText: "Email disclaimer" },
|
||||
SaveProgressPopupWidget_PhoneSpecific: { BodyText: "Phone disclaimer" },
|
||||
},
|
||||
});
|
||||
|
||||
wrapper.vm.contactMethod = "PhoneAnswer";
|
||||
|
||||
expect(wrapper.vm.modalDisclaimerText).toBe("Phone disclaimer");
|
||||
});
|
||||
|
||||
test("should show consent errors when phone is valid but transactional consent is missing", async () => {
|
||||
const { wrapper } = setupMocks({
|
||||
props: {
|
||||
modalWidgetName: "SaveProgressModalWidget",
|
||||
},
|
||||
});
|
||||
|
||||
wrapper.vm.contactMethod = "PhoneAnswer";
|
||||
wrapper.vm.userInput = "5551234567";
|
||||
wrapper.vm.smsConsent = { transactional: false, marketing: false };
|
||||
|
||||
await wrapper.vm.validatePhoneAndSave();
|
||||
|
||||
expect(wrapper.vm.showConsentErrors).toBe(true);
|
||||
});
|
||||
|
||||
test("should save phone and sms consent when valid", async () => {
|
||||
const { wrapper, dispatchStoreAction } = setupMocks({
|
||||
props: {
|
||||
modalWidgetName: "SaveProgressModalWidget",
|
||||
pageName: "vehicle-parts",
|
||||
},
|
||||
});
|
||||
|
||||
wrapper.vm.contactMethod = "PhoneAnswer";
|
||||
wrapper.vm.userInput = "555-123-4567";
|
||||
wrapper.vm.smsConsent = { transactional: true, marketing: true };
|
||||
|
||||
await wrapper.vm.saveProgress();
|
||||
|
||||
expect(dispatchStoreAction).toHaveBeenCalledWith(
|
||||
storeActions.SAVE_PHONE_NUMBER,
|
||||
"5551234567",
|
||||
false
|
||||
);
|
||||
expect(dispatchStoreAction).toHaveBeenCalledWith(
|
||||
storeActions.SAVE_IS_SMS_OPT_IN,
|
||||
true,
|
||||
false
|
||||
);
|
||||
expect(dispatchStoreAction).toHaveBeenCalledWith(
|
||||
storeActions.SAVE_IS_SMS_MARKETING_OPT_IN,
|
||||
true,
|
||||
false
|
||||
);
|
||||
expect(saveQuote).toHaveBeenCalledWith({ pageNameToLog: "vehicle-parts" });
|
||||
});
|
||||
|
||||
test("should reset sms consent when switching tabs", async () => {
|
||||
const { wrapper } = setupMocks({
|
||||
props: {
|
||||
modalWidgetName: "SaveProgressModalWidget",
|
||||
},
|
||||
});
|
||||
|
||||
wrapper.vm.contactMethod = "PhoneAnswer";
|
||||
wrapper.vm.smsConsent = { transactional: true, marketing: true };
|
||||
wrapper.vm.selectContactMethod("EmailAnswer");
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(wrapper.vm.smsConsent).toEqual({ transactional: false, marketing: false });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function setupMocks({ options, props, cmsContent = {} }) {
|
||||
const mountOptions = getMountOptions({
|
||||
...options,
|
||||
});
|
||||
|
||||
const dispatchStoreAction = jest.fn().mockResolvedValue(undefined);
|
||||
|
||||
const mockBaseMixin = {
|
||||
methods: {
|
||||
getCmsContent: jest.fn(),
|
||||
dispatchStoreAction: jest.fn(),
|
||||
getCmsContent: jest.fn((widgetName, fieldName) => cmsContent[widgetName]?.[fieldName]),
|
||||
dispatchStoreAction,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -47,5 +265,5 @@ function setupMocks({ options, props }) {
|
|||
|
||||
const wrapper = mount(saveProgressModalQuestion, mountOptions);
|
||||
|
||||
return { wrapper };
|
||||
return { wrapper, dispatchStoreAction };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,13 +17,66 @@
|
|||
:onModalClosedCallback="onModalClosed"
|
||||
:footerButtonText="modalButtonText"
|
||||
:isFooterButtonPrimary="true"
|
||||
:isFooterButtonSuppressed="isPhoneTabSelected && !isProgressSaved"
|
||||
@footer-button-event="saveProgress">
|
||||
<p class="modal-body-inner" v-html="modalBodyText"></p>
|
||||
<template v-if="showConsentManagement">
|
||||
<fieldset class="save-progress-modal-question__tabs">
|
||||
<div class="save-progress-modal-question__tab-list" role="radiogroup">
|
||||
<label
|
||||
v-for="tab in contactTabs"
|
||||
:key="tab.value"
|
||||
class="save-progress-modal-question__tab"
|
||||
:class="{
|
||||
'save-progress-modal-question__tab--active':
|
||||
contactMethod === tab.value,
|
||||
'save-progress-modal-question__tab--disabled':
|
||||
isContactMethodTabDisabled(tab.value),
|
||||
}">
|
||||
<input
|
||||
type="radio"
|
||||
class="save-progress-modal-question__tab-input"
|
||||
name="saveProgressModalContactMethod"
|
||||
:value="tab.value"
|
||||
:checked="contactMethod === tab.value"
|
||||
:disabled="isContactMethodTabDisabled(tab.value)"
|
||||
@change="selectContactMethod(tab.value)" />
|
||||
<span class="save-progress-modal-question__tab-label">
|
||||
{{ tab.label }}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div v-if="isPhoneTabSelected" class="save-progress-modal-question__phone-fields">
|
||||
<phoneNumberQuestion
|
||||
v-model="userInput"
|
||||
:cmsWidgetName="phoneQuestionWidgetName"
|
||||
placeholderText=""
|
||||
:isDisabled="isProgressSaved"
|
||||
isRequired
|
||||
validationRules="save-progress-modal-phone-number-required" />
|
||||
<saveProgressPopupSmsConsentQuestion
|
||||
v-model="smsConsent"
|
||||
:consentCopy="smsConsentCopy"
|
||||
:isDisabled="isProgressSaved"
|
||||
:showConsentErrors="showConsentErrors"
|
||||
:showConsentManagement="showConsentManagement" />
|
||||
</div>
|
||||
</template>
|
||||
<saveProgressQuestion
|
||||
v-if="!showConsentManagement || !isPhoneTabSelected"
|
||||
ref="saveProgressQuestion"
|
||||
v-model="userInput"
|
||||
cmsWidgetName="SaveProgressQuestionWidget" />
|
||||
:cmsWidgetName="emailQuestionWidgetName"
|
||||
:isDisabled="isProgressSaved" />
|
||||
<template v-slot:modal-footer-slot>
|
||||
<modalButtonMain
|
||||
v-if="showConsentManagement && isPhoneTabSelected && !isProgressSaved"
|
||||
ref="phoneSendButton"
|
||||
:isPrimary="true"
|
||||
class="w-100 modal-footer-button"
|
||||
:buttonText="modalButtonText"
|
||||
@click-event="validatePhoneAndSave" />
|
||||
<p class="modal-disclaimer" v-html="modalDisclaimerText"></p>
|
||||
</template>
|
||||
</modal>
|
||||
|
|
@ -37,11 +90,34 @@
|
|||
|
||||
<script>
|
||||
import saveProgressQuestion from "@/fmg-components/save-progress-modal-question/save-progress-question/save-progress-question";
|
||||
import saveProgressPopupSmsConsentQuestion from "@/fmg-components/save-progress-popup-question/save-progress-popup-sms-consent-question";
|
||||
import phoneNumberQuestion from "@/digital-components/phone-number-question/phone-number-question";
|
||||
import modal from "@/digital-components/modal/modal";
|
||||
import { storeActions } from "@/constants/store-actions.js";
|
||||
import modalButtonMain from "@/digital-components/modal/ux-components/modal-button-main/modal-button-main";
|
||||
import buttonMain from "@/ux-components/button-main/button-main";
|
||||
import alert from "@/ux-components/alert/alert";
|
||||
import { saveQuote } from "@/helpers/heritage-integration/order-helper.js";
|
||||
import {
|
||||
saveProgressPopupContactToStore,
|
||||
saveProgressPopupContactMethods,
|
||||
} from "@/helpers/save-progress-popup-contact-helper";
|
||||
import {
|
||||
defaultSaveProgressSmsConsent,
|
||||
getSaveProgressSmsConsentFallbackCopy,
|
||||
getSaveProgressSmsConsentConfig,
|
||||
hasSaveProgressSmsConsentSelection,
|
||||
} from "@/helpers/save-progress-sms-consent/save-progress-sms-consent-helper";
|
||||
import experimentMixin from "@/mixins/experiment-mixin.js";
|
||||
import { experimentSettings } from "@/constants/experiments";
|
||||
import { saveProgressCmsWidgets } from "@/constants/save-progress-cms-widgets";
|
||||
import { defineRule } from "vee-validate";
|
||||
import { required } from "@/helpers/validation-rules";
|
||||
import { errorMessages } from "@/constants/error-messages";
|
||||
|
||||
const { PHONE_SPECIFIC, EMAIL_SPECIFIC, MODAL_PHONE_QUESTION, MODAL_EMAIL_QUESTION } =
|
||||
saveProgressCmsWidgets;
|
||||
|
||||
defineRule("save-progress-modal-phone-number-required", required(errorMessages.PHONE_REQUIRED));
|
||||
|
||||
export default {
|
||||
name: "save-progress-modal-question",
|
||||
|
|
@ -49,15 +125,35 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
userInput: "",
|
||||
contactMethod: saveProgressPopupContactMethods.EMAIL,
|
||||
smsConsent: defaultSaveProgressSmsConsent(),
|
||||
smsConsentCopy: getSaveProgressSmsConsentFallbackCopy(),
|
||||
isProgressSaved: false,
|
||||
savedContactMethod: null,
|
||||
showConsentErrors: false,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
smsConsent: {
|
||||
handler(consent) {
|
||||
if (hasSaveProgressSmsConsentSelection(consent)) {
|
||||
this.showConsentErrors = false;
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
props: {
|
||||
modelValue: Object,
|
||||
modalWidgetName: String,
|
||||
pageName: String,
|
||||
},
|
||||
computed: {
|
||||
showConsentManagement() {
|
||||
return experimentMixin.methods.hasSettingEqualTo(
|
||||
experimentSettings.SHOW_CONSENT_MANAGEMENT,
|
||||
"true"
|
||||
);
|
||||
},
|
||||
buttonText() {
|
||||
return this.getCmsContent(this.modalWidgetName, "SubheaderText");
|
||||
},
|
||||
|
|
@ -71,7 +167,13 @@ export default {
|
|||
return this.getCmsContent(this.modalWidgetName, "FooterText");
|
||||
},
|
||||
modalDisclaimerText() {
|
||||
if (!this.showConsentManagement) {
|
||||
return this.getCmsContent(this.modalWidgetName, "FooterText2");
|
||||
}
|
||||
|
||||
const disclaimerWidgetName = this.isPhoneTabSelected ? PHONE_SPECIFIC : EMAIL_SPECIFIC;
|
||||
|
||||
return this.getCmsContent(disclaimerWidgetName, "BodyText");
|
||||
},
|
||||
modalName() {
|
||||
return this.modalWidgetName;
|
||||
|
|
@ -79,34 +181,121 @@ export default {
|
|||
modal() {
|
||||
return this.$refs[this.modalName];
|
||||
},
|
||||
contactTabs() {
|
||||
return [
|
||||
{
|
||||
label: this.getCmsContent(PHONE_SPECIFIC, "HeadlineText") || "Phone",
|
||||
value: saveProgressPopupContactMethods.PHONE,
|
||||
},
|
||||
{
|
||||
label: this.getCmsContent(EMAIL_SPECIFIC, "HeadlineText") || "Email",
|
||||
value: saveProgressPopupContactMethods.EMAIL,
|
||||
},
|
||||
];
|
||||
},
|
||||
isPhoneTabSelected() {
|
||||
return this.contactMethod === saveProgressPopupContactMethods.PHONE;
|
||||
},
|
||||
phoneQuestionWidgetName() {
|
||||
return MODAL_PHONE_QUESTION;
|
||||
},
|
||||
emailQuestionWidgetName() {
|
||||
return MODAL_EMAIL_QUESTION;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
loadSmsConsentConfig() {
|
||||
const config = getSaveProgressSmsConsentConfig();
|
||||
this.smsConsentCopy = config.copy;
|
||||
|
||||
if (!this.isProgressSaved) {
|
||||
this.smsConsent = config.value;
|
||||
}
|
||||
},
|
||||
resetContactFormState() {
|
||||
this.userInput = "";
|
||||
this.contactMethod = this.showConsentManagement
|
||||
? saveProgressPopupContactMethods.PHONE
|
||||
: saveProgressPopupContactMethods.EMAIL;
|
||||
this.smsConsent = defaultSaveProgressSmsConsent();
|
||||
this.showConsentErrors = false;
|
||||
this.savedContactMethod = null;
|
||||
},
|
||||
selectContactMethod(contactMethod) {
|
||||
if (this.isProgressSaved || this.contactMethod === contactMethod) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.contactMethod = contactMethod;
|
||||
this.userInput = "";
|
||||
this.smsConsent = defaultSaveProgressSmsConsent();
|
||||
this.showConsentErrors = false;
|
||||
},
|
||||
isContactMethodTabDisabled(contactMethod) {
|
||||
return this.isProgressSaved && contactMethod !== this.savedContactMethod;
|
||||
},
|
||||
openModal() {
|
||||
this.resetContactFormState();
|
||||
this.loadSmsConsentConfig();
|
||||
this.modal.openModal();
|
||||
this.modal.resetButtonStyle();
|
||||
},
|
||||
onModalClosed() {
|
||||
this.userInput = "";
|
||||
this.resetContactFormState();
|
||||
this.modal.resetForm();
|
||||
},
|
||||
async saveProgress() {
|
||||
// save email address to store
|
||||
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, this.userInput, false);
|
||||
async validatePhoneAndSave() {
|
||||
try {
|
||||
const validationResult = await this.modal.validate();
|
||||
|
||||
if (!validationResult.valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasSaveProgressSmsConsentSelection(this.smsConsent)) {
|
||||
this.showConsentErrors = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this.showConsentErrors = false;
|
||||
await this.saveProgress();
|
||||
} finally {
|
||||
this.resetPhoneSendButtonStyle();
|
||||
}
|
||||
},
|
||||
resetPhoneSendButtonStyle() {
|
||||
this.$refs.phoneSendButton?.resetButtonStyle();
|
||||
},
|
||||
async saveProgress() {
|
||||
await saveProgressPopupContactToStore(this.dispatchStoreAction, {
|
||||
contactMethod: this.showConsentManagement
|
||||
? this.contactMethod
|
||||
: saveProgressPopupContactMethods.EMAIL,
|
||||
userInput: this.userInput,
|
||||
smsConsent:
|
||||
this.showConsentManagement && this.isPhoneTabSelected
|
||||
? this.smsConsent
|
||||
: defaultSaveProgressSmsConsent(),
|
||||
});
|
||||
|
||||
// send store call to send to new API (that triggers an email send)
|
||||
await saveQuote({ pageNameToLog: this.pageName });
|
||||
|
||||
// hide save progress button; show success message alert
|
||||
this.isProgressSaved = true;
|
||||
if (this.showConsentManagement) {
|
||||
this.savedContactMethod = this.contactMethod;
|
||||
}
|
||||
|
||||
this.isProgressSaved = true;
|
||||
this.modal.closeModal();
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
modal,
|
||||
modalButtonMain,
|
||||
buttonMain,
|
||||
saveProgressQuestion,
|
||||
phoneNumberQuestion,
|
||||
saveProgressPopupSmsConsentQuestion,
|
||||
alert,
|
||||
},
|
||||
};
|
||||
|
|
@ -178,7 +367,6 @@ export default {
|
|||
border-radius: 0.25rem;
|
||||
}
|
||||
&.delay {
|
||||
// fixes flicker while transitioning between states
|
||||
transition: background 0s 0s ease-in-out;
|
||||
}
|
||||
|
||||
|
|
@ -218,6 +406,90 @@ export default {
|
|||
text-align: center;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.save-progress-modal-question__tabs {
|
||||
border: 0;
|
||||
margin: 0 0 1rem 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.save-progress-modal-question__tab-list {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.save-progress-modal-question__tab {
|
||||
flex: 1 1 50%;
|
||||
display: grid;
|
||||
min-height: 3.25rem;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.save-progress-modal-question__tab-input {
|
||||
grid-area: 1 / 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
opacity: 0;
|
||||
cursor: inherit;
|
||||
|
||||
&:focus-visible + .save-progress-modal-question__tab-label {
|
||||
outline: 2px solid $blue;
|
||||
outline-offset: 2px;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.save-progress-modal-question__tab-label {
|
||||
grid-area: 1 / 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding: 0.875rem 0.5rem;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.25rem;
|
||||
color: $gray-600;
|
||||
border-bottom: 3px solid transparent;
|
||||
transition:
|
||||
color 150ms linear,
|
||||
border-color 150ms linear;
|
||||
}
|
||||
|
||||
.save-progress-modal-question__tab--active .save-progress-modal-question__tab-label {
|
||||
color: $red;
|
||||
border-bottom-color: $red;
|
||||
}
|
||||
|
||||
.save-progress-modal-question__tab--disabled {
|
||||
cursor: not-allowed;
|
||||
|
||||
.save-progress-modal-question__tab-label {
|
||||
color: $gray-400;
|
||||
}
|
||||
}
|
||||
|
||||
.save-progress-modal-question__phone-fields {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
margin: 0 0 1.5rem 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.phone-number-question {
|
||||
margin: 0;
|
||||
|
||||
label {
|
||||
text-align: left;
|
||||
font-weight: 900;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-disclaimer {
|
||||
font-size: 0.75rem;
|
||||
order: 2;
|
||||
|
|
|
|||
|
|
@ -111,11 +111,10 @@ import {
|
|||
import { defineRule } from "vee-validate";
|
||||
import { required } from "@/helpers/validation-rules";
|
||||
import { errorMessages } from "@/constants/error-messages";
|
||||
import { saveProgressCmsWidgets } from "@/constants/save-progress-cms-widgets";
|
||||
|
||||
const PHONE_SPECIFIC_WIDGET = "SaveProgressPopupWidget_PhoneSpecific";
|
||||
const EMAIL_SPECIFIC_WIDGET = "SaveProgressPopupWidget_EmailSpecific";
|
||||
const PHONE_QUESTION_WIDGET = "SaveProgressPopupPhoneQuestionWidget";
|
||||
const EMAIL_QUESTION_WIDGET = "SaveProgressPopupEmailQuestionWidget";
|
||||
const { PHONE_SPECIFIC, EMAIL_SPECIFIC, POPUP_PHONE_QUESTION, POPUP_EMAIL_QUESTION } =
|
||||
saveProgressCmsWidgets;
|
||||
|
||||
defineRule("phone-number-required", required(errorMessages.PHONE_REQUIRED));
|
||||
|
||||
|
|
@ -136,7 +135,7 @@ export default {
|
|||
watch: {
|
||||
smsConsent: {
|
||||
handler(consent) {
|
||||
if (hasSaveProgressSmsConsentSelection(consent, this.showConsentManagement)) {
|
||||
if (hasSaveProgressSmsConsentSelection(consent)) {
|
||||
this.showConsentErrors = false;
|
||||
}
|
||||
},
|
||||
|
|
@ -171,9 +170,7 @@ export default {
|
|||
return this.getCmsContent(this.modalWidgetName, "FooterText");
|
||||
},
|
||||
modalDisclaimerText() {
|
||||
const disclaimerWidgetName = this.isPhoneTabSelected
|
||||
? PHONE_SPECIFIC_WIDGET
|
||||
: EMAIL_SPECIFIC_WIDGET;
|
||||
const disclaimerWidgetName = this.isPhoneTabSelected ? PHONE_SPECIFIC : EMAIL_SPECIFIC;
|
||||
|
||||
return this.getCmsContent(disclaimerWidgetName, "BodyText");
|
||||
},
|
||||
|
|
@ -186,11 +183,11 @@ export default {
|
|||
contactTabs() {
|
||||
return [
|
||||
{
|
||||
label: this.getCmsContent(PHONE_SPECIFIC_WIDGET, "HeadlineText") || "Phone",
|
||||
label: this.getCmsContent(PHONE_SPECIFIC, "HeadlineText") || "Phone",
|
||||
value: saveProgressPopupContactMethods.PHONE,
|
||||
},
|
||||
{
|
||||
label: this.getCmsContent(EMAIL_SPECIFIC_WIDGET, "HeadlineText") || "Email",
|
||||
label: this.getCmsContent(EMAIL_SPECIFIC, "HeadlineText") || "Email",
|
||||
value: saveProgressPopupContactMethods.EMAIL,
|
||||
},
|
||||
];
|
||||
|
|
@ -202,10 +199,10 @@ export default {
|
|||
return this.isProgressSaved || this.isPhoneTabSelected;
|
||||
},
|
||||
phoneQuestionWidgetName() {
|
||||
return PHONE_QUESTION_WIDGET;
|
||||
return POPUP_PHONE_QUESTION;
|
||||
},
|
||||
emailQuestionWidgetName() {
|
||||
return EMAIL_QUESTION_WIDGET;
|
||||
return POPUP_EMAIL_QUESTION;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -245,9 +242,7 @@ export default {
|
|||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!hasSaveProgressSmsConsentSelection(this.smsConsent, this.showConsentManagement)
|
||||
) {
|
||||
if (!hasSaveProgressSmsConsentSelection(this.smsConsent)) {
|
||||
this.showConsentErrors = true;
|
||||
await this.scrollConsentIntoView();
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -68,10 +68,7 @@ export default {
|
|||
},
|
||||
},
|
||||
showConsentError() {
|
||||
return (
|
||||
this.showConsentErrors &&
|
||||
!hasSaveProgressSmsConsentSelection(this.modelValue, this.showConsentManagement)
|
||||
);
|
||||
return this.showConsentErrors && !hasSaveProgressSmsConsentSelection(this.modelValue);
|
||||
},
|
||||
consentErrorMessage() {
|
||||
return this.showConsentManagement
|
||||
|
|
|
|||
|
|
@ -9,6 +9,17 @@ export function isPhoneContactMethod(contactMethod) {
|
|||
return contactMethod === saveProgressPopupContactMethods.PHONE;
|
||||
}
|
||||
|
||||
export function hasSaveProgressContactInStore(customer) {
|
||||
if (!customer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const hasEmail = Boolean(customer.emailAddress?.length > 0);
|
||||
const hasPhone = Boolean(customer.phoneNumber?.length > 0);
|
||||
|
||||
return hasEmail || hasPhone;
|
||||
}
|
||||
|
||||
export function normalizePhoneNumberForStore(phoneNumber) {
|
||||
if (!phoneNumber) {
|
||||
return phoneNumber;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { storeActions } from "@/constants/store-actions";
|
||||
import {
|
||||
hasSaveProgressContactInStore,
|
||||
isPhoneContactMethod,
|
||||
normalizePhoneNumberForStore,
|
||||
saveProgressPopupContactMethods,
|
||||
|
|
@ -11,6 +12,27 @@ describe("save-progress-popup-contact-helper", () => {
|
|||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe("hasSaveProgressContactInStore", () => {
|
||||
it("should return true when email is saved", () => {
|
||||
expect(hasSaveProgressContactInStore({ emailAddress: "test@example.com" })).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true when phone is saved", () => {
|
||||
expect(hasSaveProgressContactInStore({ phoneNumber: "5551234567" })).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false when customer is undefined", () => {
|
||||
expect(hasSaveProgressContactInStore(undefined)).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false when neither email nor phone is saved", () => {
|
||||
expect(hasSaveProgressContactInStore({})).toBe(false);
|
||||
expect(hasSaveProgressContactInStore({ emailAddress: "", phoneNumber: null })).toBe(
|
||||
false
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isPhoneContactMethod", () => {
|
||||
it("should return true for the phone contact method", () => {
|
||||
expect(isPhoneContactMethod(saveProgressPopupContactMethods.PHONE)).toBe(true);
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ export function getSaveProgressSmsConsentFallbackCopy() {
|
|||
return { ...SAVE_PROGRESS_SMS_CONSENT_FALLBACK_COPY };
|
||||
}
|
||||
|
||||
export function hasSaveProgressSmsConsentSelection(consent, showConsentManagement = false) {
|
||||
return Boolean(consent?.transactional || (showConsentManagement && consent?.marketing));
|
||||
export function hasSaveProgressSmsConsentSelection(consent) {
|
||||
return Boolean(consent?.transactional);
|
||||
}
|
||||
|
||||
export function getSaveProgressSmsConsentConfig() {
|
||||
|
|
|
|||
|
|
@ -48,19 +48,16 @@ describe("save-progress-sms-consent-helper", () => {
|
|||
expect(hasSaveProgressSmsConsentSelection(defaultSaveProgressSmsConsent())).toBe(false);
|
||||
});
|
||||
|
||||
it("should return true when either consent option is selected and consent management is enabled", () => {
|
||||
it("should return true only when transactional consent is selected", () => {
|
||||
expect(
|
||||
hasSaveProgressSmsConsentSelection({ transactional: true, marketing: false }, true)
|
||||
hasSaveProgressSmsConsentSelection({ transactional: true, marketing: false })
|
||||
).toBe(true);
|
||||
expect(
|
||||
hasSaveProgressSmsConsentSelection({ transactional: false, marketing: true }, true)
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("should ignore marketing consent when consent management is disabled", () => {
|
||||
expect(
|
||||
hasSaveProgressSmsConsentSelection({ transactional: false, marketing: true }, false)
|
||||
hasSaveProgressSmsConsentSelection({ transactional: false, marketing: true })
|
||||
).toBe(false);
|
||||
expect(
|
||||
hasSaveProgressSmsConsentSelection({ transactional: true, marketing: true })
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
// Components
|
||||
import questionsPageLayout from "@/fmg-components/layouts/questions-page-layout/questions-page-layout";
|
||||
import saveProgressModalQuestion from "@/fmg-components/save-progress-modal-question/save-progress-modal-question";
|
||||
import { hasSaveProgressContactInStore } from "@/helpers/save-progress-popup-contact-helper";
|
||||
|
||||
// Supporting Files
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
|
|
@ -59,12 +60,12 @@ export default {
|
|||
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
|
||||
const emailFromStore = store.getters.order.customer.emailAddress;
|
||||
|
||||
// Call the "next" function to complete the transition to this page.
|
||||
next(async (vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
vm.showSaveProgressModal = !(emailFromStore?.length > 0);
|
||||
vm.showSaveProgressModal = !hasSaveProgressContactInStore(
|
||||
store.getters.order?.customer
|
||||
);
|
||||
if (store.getters.externalParameterState?.isExternalParameter) {
|
||||
if (store.getters.externalParameterServiceZip.zipCode) {
|
||||
await baseMixin.methods.dispatchStoreAction(
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@
|
|||
<saveProgressModalQuestion
|
||||
modalWidgetName="SaveProgressModalWidget"
|
||||
modalName="SaveProgressModal"
|
||||
v-if="showSaveProgressModal"
|
||||
pageName="coverage-statement" />
|
||||
<navbar
|
||||
cmsWidgetName="FunnelFooterWidget"
|
||||
|
|
@ -80,7 +81,8 @@ import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
|||
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||
import customerInstructions from "./customer-instructions/customer-instructions.vue";
|
||||
import priceDisplay from "./price-display/price-display.vue";
|
||||
import saveProgressModalQuestion from "@/fmg-components/save-progress-modal-question/save-progress-modal-question.vue";
|
||||
import saveProgressModalQuestion from "@/fmg-components/save-progress-modal-question/save-progress-modal-question";
|
||||
import { hasSaveProgressContactInStore } from "@/helpers/save-progress-popup-contact-helper";
|
||||
import afterpayBreakout from "../payment-method/afterpay-breakout/afterpay-breakout.vue";
|
||||
import modal from "@/digital-components/modal/modal";
|
||||
import contentGroupModal from "@/fmg-components/content-group-modal/content-group-modal.vue";
|
||||
|
|
@ -105,6 +107,7 @@ export default {
|
|||
isRecalibrationOnOrder: this.isRecalibrationOnOrderFromStore(),
|
||||
isRepair: this.isRepairFromStore(),
|
||||
clientConfig: {},
|
||||
showSaveProgressModal: null,
|
||||
};
|
||||
},
|
||||
components: {
|
||||
|
|
@ -140,9 +143,11 @@ export default {
|
|||
}
|
||||
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
const showSaveProgressModal = !hasSaveProgressContactInStore(store.getters.order?.customer);
|
||||
next((vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
vm.clientConfig = resultMap.clientConfig ?? {};
|
||||
vm.showSaveProgressModal = showSaveProgressModal;
|
||||
debugLog(
|
||||
`coverage-statement clientConfig::${clientConfigPageName}`,
|
||||
JSON.stringify(vm.clientConfig)
|
||||
|
|
|
|||
|
|
@ -151,6 +151,7 @@ import { stateOptionsInternational } from "@/constants/state-options";
|
|||
import { regex } from "@/helpers/validation-rules";
|
||||
import { coverageStatus, coverageStatusEnum } from "@/constants/insurance";
|
||||
import saveProgressModalQuestion from "@/fmg-components/save-progress-modal-question/save-progress-modal-question";
|
||||
import { hasSaveProgressContactInStore } from "@/helpers/save-progress-popup-contact-helper";
|
||||
import { debugLog } from "@/helpers/debug-log-helper";
|
||||
import {
|
||||
flushPagePrereqsLogs,
|
||||
|
|
@ -223,8 +224,7 @@ export default {
|
|||
},
|
||||
];
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
const emailFromStore = store.getters.order.customer.emailAddress;
|
||||
const showSaveProgressModal = !(emailFromStore?.length > 0);
|
||||
const showSaveProgressModal = !hasSaveProgressContactInStore(store.getters.order?.customer);
|
||||
next((vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
vm.showSaveProgressModal = showSaveProgressModal;
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
// Components
|
||||
import questionsPageLayout from "@/fmg-components/layouts/questions-page-layout/questions-page-layout";
|
||||
import saveProgressModalQuestion from "@/fmg-components/save-progress-modal-question/save-progress-modal-question";
|
||||
import { hasSaveProgressContactInStore } from "@/helpers/save-progress-popup-contact-helper";
|
||||
|
||||
// Supporting Files
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
|
|
@ -59,12 +60,12 @@ export default {
|
|||
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
|
||||
const emailFromStore = store.getters.order.customer.emailAddress;
|
||||
|
||||
// Call the "next" function to complete the transition to this page.
|
||||
next(async (vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
vm.showSaveProgressModal = !(emailFromStore?.length > 0);
|
||||
vm.showSaveProgressModal = !hasSaveProgressContactInStore(
|
||||
store.getters.order?.customer
|
||||
);
|
||||
if (store.getters.externalParameterState?.isExternalParameter) {
|
||||
if (store.getters.externalParameterServiceZip.zipCode) {
|
||||
await baseMixin.methods.dispatchStoreAction(
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
// Components
|
||||
import questionsPageLayout from "@/fmg-components/layouts/questions-page-layout/questions-page-layout";
|
||||
import saveProgressModalQuestion from "@/fmg-components/save-progress-modal-question/save-progress-modal-question";
|
||||
import { hasSaveProgressContactInStore } from "@/helpers/save-progress-popup-contact-helper";
|
||||
|
||||
// Supporting Files
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
|
|
@ -59,12 +60,12 @@ export default {
|
|||
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
|
||||
const emailFromStore = store.getters.order.customer.emailAddress;
|
||||
|
||||
// Call the "next" function to complete the transition to this page.
|
||||
next(async (vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
vm.showSaveProgressModal = !(emailFromStore?.length > 0);
|
||||
vm.showSaveProgressModal = !hasSaveProgressContactInStore(
|
||||
store.getters.order?.customer
|
||||
);
|
||||
if (store.getters.externalParameterState?.isExternalParameter) {
|
||||
if (store.getters.externalParameterServiceZip.zipCode) {
|
||||
const serviceState = store.getters.order.serviceLocation.state;
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
|
|||
import navbar from "@/fmg-components/nav-bar/nav-bar";
|
||||
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
|
||||
import saveProgressModalQuestion from "@/fmg-components/save-progress-modal-question/save-progress-modal-question";
|
||||
import { hasSaveProgressContactInStore } from "@/helpers/save-progress-popup-contact-helper";
|
||||
import { Form } from "vee-validate";
|
||||
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
|
|
@ -91,8 +92,7 @@ export default {
|
|||
},
|
||||
];
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
const emailFromStore = store.getters.order.customer.emailAddress;
|
||||
const showSaveProgressModal = !(emailFromStore?.length > 0);
|
||||
const showSaveProgressModal = !hasSaveProgressContactInStore(store.getters.order?.customer);
|
||||
next((vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
vm.clientConfig = resultMap.clientConfig ?? {};
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-heade
|
|||
import navbar from "@/fmg-components/nav-bar/nav-bar";
|
||||
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
|
||||
import saveProgressModalQuestion from "@/fmg-components/save-progress-modal-question/save-progress-modal-question";
|
||||
import { hasSaveProgressContactInStore } from "@/helpers/save-progress-popup-contact-helper";
|
||||
// Supporting Files
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||
|
|
@ -77,11 +78,12 @@ export default {
|
|||
},
|
||||
];
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
const emailFromStore = store.getters.order?.customer?.emailAddress;
|
||||
// Call the "next" function to complete the transition to this page.
|
||||
next((vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
vm.showSaveProgressModal = !(emailFromStore?.length > 0);
|
||||
vm.showSaveProgressModal = !hasSaveProgressContactInStore(
|
||||
store.getters.order?.customer
|
||||
);
|
||||
|
||||
// Glass Part Question dynamic component
|
||||
Object.keys(vm.$refs)
|
||||
|
|
|
|||
Loading…
Reference in a new issue