Merge branch 'release/2025.01.16' into rlsmerge/2025.01.16-to-develop

This commit is contained in:
CarlNation 2025-01-14 17:05:51 -05:00
commit a59300daea
23 changed files with 613 additions and 82 deletions

View file

@ -25,17 +25,17 @@
</div>
<div class="modal-body ps-5 pe-5 pb-4 pt-1">
<slot></slot>
</div>
<div class="modal-footer px-5 py-4">
<modalButtonMain
isPrimary
class="w-100"
id="modalbtn"
ref="modalButtonMain"
loaderColor="white"
:buttonText="footerButtonText"
@click-event="validateAndEmit"
:class="isFooterButtonDisabled && 'form-test-invalid'" />
<div class="modal-footer px-5 py-4">
<modalButtonMain
isPrimary
class="w-100"
id="modalbtn"
ref="modalButtonMain"
loaderColor="white"
:buttonText="footerButtonText"
@click-event="validateAndEmit"
:class="isFooterButtonDisabled && 'form-test-invalid'" />
</div>
</div>
</div>
</div>
@ -53,6 +53,7 @@ export default {
props: {
headerText: String,
footerButtonText: String,
suppressPageScroll: Boolean,
onModalOpenedCallback: {
type: Function,
},
@ -107,8 +108,10 @@ export default {
onModalClosed() {
this.resetButtonStyle();
this.onModalClosedCallback?.();
if (this.suppressPageScroll) document.querySelector("body").classList.remove("prevent-modal-scroll");
},
openModal() {
if (this.suppressPageScroll) document.querySelector("body").classList.add("prevent-modal-scroll");
const modal = Modal.getOrCreateInstance(document.getElementById(this.modalId));
modal.show();
this.$emit("isModalOpened", true);
@ -231,6 +234,29 @@ export default {
}
}
}
.save-progress-modal-question {
p.modal-body {
padding: 0;
}
.modal-body {
display: flex;
flex-direction: column;
.textbox-question {
padding: .25rem 0;
label {
text-align: left;
font-weight: 600;
}
}
}
.modal-disclaimer {
font-size: .75rem;
order: 2;
}
.modal-footer {
padding: 1.5rem 0;
}
}
body {
.modal-backdrop {
height: 100%;

View file

@ -25,7 +25,7 @@
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<div class="col-md-6 col-xl-4 question-chain-wrapper">
<div v-for="(questionsDatum, i) in questionsData" :key="questionsDatum.key">
<questionChain
ref="questionChain"
@ -37,6 +37,8 @@
:validationRules="validationRules" />
</div>
<slot></slot>
<navbar
ref="navbar"
cmsWidgetName="FunnelFooterWidget"
@ -112,5 +114,13 @@ export default {
text-align: left;
}
}
.question-chain-wrapper {
display: flex;
flex-direction: column;
.nav-bar {
order: 1;
}
}
}
</style>

View file

@ -0,0 +1,51 @@
import { mount, shallowMount } from "@vue/test-utils";
import saveProgressModalQuestion from "./save-progress-modal-question";
import { getMountOptions } from "@/helpers/unit-test-helper.js";
jest.mock("@/digital-components/modal/modal", () => ({
methods: {
openModal: jest.fn(),
resetButtonStyle: jest.fn(),
},
}));
describe("save-progress-modal-question ", () => {
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();
});
});
});
function setupMocks({ options, props }) {
const mountOptions = getMountOptions({
...options,
});
const mockBaseMixin = {
methods: {
getCmsContent: jest.fn(),
dispatchStoreAction: jest.fn(),
},
};
if (props) mountOptions.propsData = props;
mountOptions.global.mixins = [mockBaseMixin];
const wrapper = mount(saveProgressModalQuestion, mountOptions);
return { wrapper };
}

View file

@ -0,0 +1,193 @@
<template>
<div class="save-progress-modal-question">
<buttonMain
ref="buttonMain"
v-if="!isProgressSaved"
:buttonText="buttonText"
loaderColor="white"
:suppressLoader="true"
@click-event="openModal" />
<alert
class="my-4"
v-else
cmsWidgetName="SaveProgressModalAlertWidget"
alertClass="alert-success" />
<modal
:ref="modalName"
:headerText="modalHeaderText"
suppressPageScroll
:footerButtonText="modalButtonText"
@footer-button-event="saveProgress">
<p class="modal-body">{{ modalBodyText }}</p>
<saveProgressQuestion
ref="saveProgressQuestion"
v-model="userInput"
cmsWidgetName="SaveProgressQuestionWidget" />
<p class="modal-disclaimer" v-html="modalDisclaimerText"></p>
</modal>
</div>
</template>
<script>
import saveProgressQuestion from "@/fmg-components/save-progress-modal-question/save-progress-question/save-progress-question";
import modal from "@/digital-components/modal/modal";
import { storeActions } from "@/constants/store-actions.js";
import buttonMain from "@/ux-components/button-main/button-main";
import alert from "@/ux-components/alert/alert";
export default {
name: "save-progress-modal-question",
data() {
return {
userInput: "",
isProgressSaved: false,
};
},
props: {
modelValue: Object,
modalWidgetName: String,
pageName: String,
},
computed: {
buttonText() {
return this.getCmsContent(this.modalWidgetName, "SubheaderText");
},
modalHeaderText() {
return this.getCmsContent(this.modalWidgetName, "HeaderText");
},
modalBodyText() {
return this.getCmsContent(this.modalWidgetName, "BodyText");
},
modalButtonText() {
return this.getCmsContent(this.modalWidgetName, "FooterText");
},
modalDisclaimerText() {
return this.getCmsContent(this.modalWidgetName, "FooterText2");
},
modalName() {
return this.modalWidgetName;
},
modal() {
return this.$refs[this.modalName];
},
},
methods: {
openModal() {
this.modal.openModal();
this.modal.resetButtonStyle();
},
async saveProgress() {
// save email address to store
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, this.userInput, false);
// hide save progress button; show success message alert
this.isProgressSaved = true;
this.modal.closeModal();
// send store call to send to new API (that triggers an email send)
this.$emit("save-progress");
},
},
components: {
modal,
buttonMain,
saveProgressQuestion,
alert,
},
};
</script>
<style lang="scss" scoped>
.save-progress-modal-question {
order: 2;
margin-bottom: 1.5rem;
.btn-secondary {
position: relative;
background: $blue-100;
border: 1px solid transparent;
border-radius: $border-radius-lg;
color: $blue;
font-weight: 500;
font-size: .875rem;
transition: all 150ms linear;
height: 3.5rem;
width: 100%;
text-decoration: underline;
text-underline-offset: .5rem;
&:hover {
background: linear-gradient(270deg, $blue 0%, $blue-800 100%);
color: $white;
}
&:focus, // Mouse, touch, stylus focus
&:focus-visible {
// Keyboard focus for accessibility
outline: none;
box-shadow:
0 0 0 3px $white,
0 0 0 5.5px $blue-700;
color: $white;
background: linear-gradient(270deg, rgba(6, 87, 124, 1) 0%, rgba(6, 87, 124, 1) 100%);
}
&:disabled {
background: transparent;
color: $gray-550 !important;
font-weight: 400;
height: 48px;
border: 1px solid $gray-550;
border-radius: $border-radius-lg;
cursor: pointer;
pointer-events: all;
}
&.has-loader {
color: $blue;
background-color: $blue;
pointer-events: none;
}
&.delay {
// fixes flicker while transitioning between states
transition: background 0s 0s ease-in-out;
}
}
:deep(.alert) {
border-radius: $border-radius-lg;
border: 1px solid $green;
}
:deep(.alert-heading) {
position: relative;
font-weight: 600;
font-size: 1rem;
color: $black;
text-align: left;
padding-left: 1.5rem;
&::before {
position: absolute;
content: "";
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3E%3Cpath fill='%230C7E47' d='M8 0a8 8 0 1 0 0 16A8 8 0 0 0 8 0Zm3.7 6.171-4.449 4.45a.559.559 0 0 1-.8 0l-2.16-2.16a.563.563 0 0 1 .792-.8l1.76 1.76 4.066-4.042a.563.563 0 1 1 .792.8v-.008Z'/%3E%3C/svg%3E");
width: 1rem;
height: 1rem;
top: 6px;
left: 0;
}
}
}
.applied-promo {
font-size: 0.875rem;
font-weight: 500;
line-height: 24px;
color: $green;
.promo-code {
text-transform: uppercase;
}
}
.alert {
color: $red;
font-size: 0.875rem;
font-weight: 500;
line-height: 24px;
}
</style>

View file

@ -0,0 +1,40 @@
import { shallowMount } from "@vue/test-utils";
import saveProgressQuestion from "./save-progress-question";
describe("save-progress-question.vue", () => {
it("Should get the modelValue", async () => {
// Arrange
const text = "test";
const wrapper = shallowMount(saveProgressQuestion, {
props: {
modelValue: text,
},
attachTo: document.body,
});
// Act
const modelValueText = wrapper.vm.value;
wrapper.vm.value = "test also";
// Assert
expect(modelValueText).toEqual("test");
});
it("Should emit to set value", async () => {
// Arrange
const text = "test";
const wrapper = shallowMount(saveProgressQuestion, {
props: {
modelValue: text,
},
attachTo: document.body,
});
// Act
const modelValueText = wrapper.vm.value;
wrapper.vm.value = "test also";
// Assert
expect(wrapper.emitted("update:modelValue")).toEqual([["test also"]]);
});
});

View file

@ -0,0 +1,62 @@
<template>
<textboxQuestion
ref="saveProgressInputTextQuestion"
:cmsWidgetName="cmsWidgetName"
v-model="value"
inputId="saveProgressUserInput"
questionAlignment="center"
cornerStyle="rounded"
:displayQuestionText="true"
isRequired
@input="validateEmail"
validationRules="email-address-required|email-address-format" />
</template>
<script>
import textboxQuestion from "@/digital-components/textbox-question/textbox-question";
//Supporting Files
import { defineRule } from "vee-validate";
import { required, regex } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages";
// Define Validation Rules
defineRule("email-address-required", required(errorMessages.EMAIL_ADDRESS_REQUIRED));
defineRule(
"email-address-format",
regex(
/^([a-zA-Z0-9_\-.+]+)@([a-zA-Z0-9-]+)\.([a-zA-Z]{2,})$/,
errorMessages.EMAIL_ADDRESS_FORMAT
)
);
const EMAIL_REGEX = /[^a-zA-Z0-9_\-.+@]/g;
export default {
name: "save-progress-question",
props: {
modelValue: String,
cmsWidgetName: String,
},
computed: {
value: {
get: function () {
return this.modelValue;
},
set: function (newValue) {
this.$emit("update:modelValue", newValue);
},
},
},
methods: {
validateEmail() {
setTimeout(() => {
this.value = this.value.replace(EMAIL_REGEX, "");
});
},
},
components: {
textboxQuestion,
},
};
</script>

View file

@ -42,13 +42,10 @@ export async function getDirectNavigation(toRoute) {
return fmgPageValues.VEHICLE_DAMAGE;
}
if (
fmgPageValue === fmgPageValues.QUOTE ||
fmgPageValue === fmgPageValues.INSURANCE_COMPANY
) {
if (fmgPageValue === fmgPageValues.QUOTE) {
const skipVin = await skipVinLookup();
if (skipVin) {
if (skipVin || !store.getters.vehicle?.vin) {
return fmgPageValues.ESTIMATE;
} else {
return fmgPageValues.VIN_LOOKUP;

View file

@ -400,26 +400,6 @@ describe("getPageToRouteExistingOrderTo", () => {
// Assert
expect(result).toBe(fmgPageValues.ESTIMATE);
});
test("Replace navigation to `insurance-company` with `estimate`", async () => {
// Arrange
const toRoute = {
query: {
fmgPage: fmgPageValues.INSURANCE_COMPANY,
},
};
store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, true);
store.commit(storeMutations.UPDATE_REFERRAL_NUMBER, "666666");
store.commit(storeMutations.UPDATE_IS_REPAIR, true);
store.commit(storeMutations.UPDATE_MAKE, "acura");
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
// Assert
expect(result).toBe(fmgPageValues.ESTIMATE);
});
});
describe("Vin-Required", () => {
@ -438,29 +418,7 @@ describe("getPageToRouteExistingOrderTo", () => {
store.commit(storeMutations.UPDATE_GLASS_TO_REPLACE, [
{ glassLocation: glassLocations.WINDSHIELD },
]);
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
// Assert
expect(result).toBe(fmgPageValues.VIN_LOOKUP);
});
test("Replace navigation to `insurance-company` with `vin-lookup`", async () => {
// Arrange
const toRoute = {
query: {
fmgPage: fmgPageValues.INSURANCE_COMPANY,
},
};
store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, true);
store.commit(storeMutations.UPDATE_REFERRAL_NUMBER, "666666");
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
store.commit(storeMutations.UPDATE_MAKE, "acura");
store.commit(storeMutations.UPDATE_GLASS_TO_REPLACE, [
{ glassLocation: glassLocations.WINDSHIELD },
]);
store.commit(storeMutations.UPDATE_VEHICLE_VIN, "abcd1234");
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);

View file

@ -99,12 +99,22 @@ export async function submitWorkOrder({
}
export async function saveQuote({ pageNameToLog }) {
return await baseMixin.methods.dispatchStoreActionWithLogging(
await baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.SAVE_QUOTE,
{},
pageNameToLog,
false
);
// save session back to sv2 so the email gets saved just in case they refresh or step away and come back later
await saveSession({
pageNameToLog: pageNameToLog,
shouldAwaitSaveSessionQueue: true,
submitAfterSave: false,
createUnscheduledStatusWorkOrderForPIA: false,
});
return;
}
// PRIVATE FUNCTIONS //

View file

@ -12,13 +12,22 @@
@forwardButtonAction="forwardButtonAction"
@back-click="navigateBack"
:key="currentGlassIndex"
:index="currentGlassIndex" />
:index="currentGlassIndex">
<!-- save progress goes into <slot> on questions-page-layout -->
<saveProgressModalQuestion
modalWidgetName="SaveProgressModalWidget"
v-if="!isEmailInStoreOnPageLoad"
@save-progress="saveProgress" />
</questions-page-layout>
</Form>
</template>
<script>
// 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";
// Supporting Files
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
@ -31,6 +40,7 @@ import { required } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages";
import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin";
import baseMixin from "@/mixins/base-mixin.js";
import { saveQuote } from "@/helpers/heritage-integration/order-helper.js";
// DEFINE VALIDATION RULES
defineRule("questions-required", required(errorMessages.OPTION_REQUIRED));
@ -52,9 +62,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.isEmailInStoreOnPageLoad = emailFromStore?.length > 0;
if (store.getters.externalParameterState?.isExternalParameter) {
if (store.getters.externalParameterServiceZip.zipCode) {
await baseMixin.methods.dispatchStoreAction(
@ -76,6 +89,7 @@ export default {
questionsData: null,
selectedAnswers: {},
currentGlassIndex: 0,
isEmailInStoreOnPageLoad: null,
};
},
computed: {
@ -145,6 +159,9 @@ export default {
this.questionsData
);
},
saveProgress() {
saveQuote({ pageNameToLog: "capability-questions" });
},
async forwardButtonAction() {
const questionAnswersArray = this.questionsData.map((glass) => {
// get answerResult2 of returned answer
@ -205,6 +222,7 @@ export default {
components: {
Form,
questionsPageLayout,
saveProgressModalQuestion,
},
};
</script>

View file

@ -87,6 +87,7 @@ import { required, regex } from "@/helpers/validation-rules";
import { Form, defineRule } from "vee-validate";
import { useField, validate } from "vee-validate";
import store from "@/store";
import { paymentMethods } from "@/constants/payment-method-constants";
// DEFINE VALIDATION RULES
defineRule("first-name-required", required(errorMessages.FIRST_NAME_REQUIRED));
@ -162,7 +163,7 @@ export default {
backButtonAction() {
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
},
forwardButtonAction() {
async forwardButtonAction() {
this.dispatchStoreAction(
this.storeActions.SAVE_CUSTOMER_DETAILS,
{
@ -181,6 +182,17 @@ export default {
false
);
// if the user has resorted to using browser back/forward buttons we could have promises unresolved if they clicked back before pia work order submission.
// so attempt to wait for them to finish.
if (store.getters.order.payment.isPia) {
this.dispatchStoreAction(
storeActions.SAVE_PAYMENT_METHOD_CHOICE,
paymentMethods.NONE,
false
);
await store.getters.applicationUser.saveSessionPromise;
}
this.$router.navigateWithSaving(this.navigationScenarios.CLICKED_FORWARD, this.$route);
},
},

View file

@ -12,13 +12,22 @@
@forwardButtonAction="forwardButtonAction"
@back-click="navigateBack"
:key="currentGlassIndex"
:index="currentGlassIndex" />
:index="currentGlassIndex">
<!-- save progress goes into <slot> on questions-page-layout -->
<saveProgressModalQuestion
modalWidgetName="SaveProgressModalWidget"
v-if="!isEmailInStoreOnPageLoad"
@save-progress="saveProgress" />
</questions-page-layout>
</Form>
</template>
<script>
// 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";
// Supporting Files
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
@ -31,6 +40,7 @@ import { required } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages";
import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin";
import baseMixin from "@/mixins/base-mixin.js";
import { saveQuote } from "@/helpers/heritage-integration/order-helper.js";
// DEFINE VALIDATION RULES
defineRule("questions-required", required(errorMessages.OPTION_REQUIRED));
@ -52,9 +62,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.isEmailInStoreOnPageLoad = emailFromStore?.length > 0;
if (store.getters.externalParameterState?.isExternalParameter) {
if (store.getters.externalParameterServiceZip.zipCode) {
await baseMixin.methods.dispatchStoreAction(
@ -77,6 +90,7 @@ export default {
questionsData: null,
selectedAnswers: {},
currentGlassIndex: 0,
isEmailInStoreOnPageLoad: null,
};
},
computed: {
@ -145,6 +159,9 @@ export default {
this.questionsData
);
},
saveProgress() {
saveQuote({ pageNameToLog: "molding-questions" });
},
async forwardButtonAction() {
const questionAnswersArray = this.questionsData.map((glass) => {
return {
@ -189,6 +206,7 @@ export default {
components: {
Form,
questionsPageLayout,
saveProgressModalQuestion,
},
};
</script>

View file

@ -12,13 +12,22 @@
@forwardButtonAction="forwardButtonAction"
@back-click="navigateBack"
:key="currentGlassIndex"
:index="currentGlassIndex" />
:index="currentGlassIndex">
<!-- save progress goes into <slot> on questions-page-layout -->
<saveProgressModalQuestion
modalWidgetName="SaveProgressModalWidget"
v-if="!isEmailInStoreOnPageLoad"
@save-progress="saveProgress" />
</questions-page-layout>
</Form>
</template>
<script>
// 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";
// Supporting Files
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
@ -31,6 +40,7 @@ import { required } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages";
import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin";
import baseMixin from "@/mixins/base-mixin.js";
import { saveQuote } from "@/helpers/heritage-integration/order-helper.js";
// DEFINE VALIDATION RULES
defineRule("questions-required", required(errorMessages.OPTION_REQUIRED));
@ -52,9 +62,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.isEmailInStoreOnPageLoad = emailFromStore?.length > 0;
if (store.getters.externalParameterState?.isExternalParameter) {
if (store.getters.externalParameterServiceZip.zipCode) {
const serviceState = store.getters.order.serviceLocation.state;
@ -78,6 +91,7 @@ export default {
questionsData: null,
selectedAnswers: {},
currentGlassIndex: 0,
isEmailInStoreOnPageLoad: null,
};
},
computed: {
@ -144,6 +158,9 @@ export default {
this.questionsData
);
},
saveProgress() {
saveQuote({ pageNameToLog: "part-questions" });
},
async forwardButtonAction() {
const questionAnswersArray = this.questionsData.map((glass) => {
return {
@ -185,6 +202,7 @@ export default {
components: {
Form,
questionsPageLayout,
saveProgressModalQuestion,
},
};
</script>

View file

@ -202,6 +202,9 @@ describe("payment.vue", () => {
jobMaxMinutes: "45",
},
},
applicationUser: {
loggingOption: false,
},
policy: {
currentDeductible: 1,
},

View file

@ -532,13 +532,53 @@ export default {
store.getters.order.payment.isPia !== null &&
(store.getters.order.payment.isPia || !!store.getters.order.payment.piaType);
return (
const preReqResult =
serviceLocationReqs &&
isInsuranceSet &&
scheduleReqs &&
customerReqs &&
paymentMethodReqs
);
paymentMethodReqs;
// prettier-ignore
{
if (store.getters.applicationUser.loggingOption || !preReqResult) {
console.log("------------- payment.vue pagePrereqs start -----------------");
console.log(new Date() + "mobileReqs:" + mobileReqs);
console.log(new Date() + "serviceLocation.address:" + serviceLocation.address);
console.log(new Date() + "serviceLocation.city:" + serviceLocation.city);
console.log(new Date() + "serviceLocation.state:" + serviceLocation.state);
console.log(new Date() + "serviceLocation.zipCode:" + serviceLocation.zipCode);
console.log("");
console.log(new Date() + "dropOffInshopReqs:" + dropOffInshopReqs);
console.log(new Date() + "serviceLocationReqs:" + serviceLocationReqs);
console.log(new Date() + "providerLocation.streetAddress:" + providerLocation.streetAddress);
console.log(new Date() + "providerLocation.city:" + providerLocation.city);
console.log(new Date() + "providerLocation.state:" + providerLocation.state);
console.log(new Date() + "providerLocation.zipCode:" + providerLocation.zipCode);
console.log("");
console.log(new Date() + "isInsuranceSet:" + isInsuranceSet);
console.log("");
console.log(new Date() + "scheduleReqs:" + scheduleReqs);
console.log(new Date() + "schedule.date:" + schedule.date);
console.log(new Date() + "schedule.startTime:" + schedule.startTime);
console.log(new Date() + "schedule.endTime:" + schedule.endTime);
console.log(new Date() + "schedule.jobMaxMinutes:" + schedule.jobMaxMinutes);
console.log(new Date() + "schedule.jobMinMinutes:" + schedule.jobMinMinutes);
console.log("");
console.log(new Date() + "customerReqs:" + customerReqs);
console.log(new Date() + "customer.firstName:" + customer.firstName);
console.log(new Date() + "customer.lastName:" + customer.lastName);
console.log(new Date() + "customer.phoneNumber:" + customer.phoneNumber);
console.log(new Date() + "customer.emailAddress:" + customer.emailAddress);
console.log("");
console.log(new Date() + "paymentMethodReqs:" + paymentMethodReqs);
console.log(new Date() + "store.getters.order.payment.isPia:" + store.getters.order.payment.isPia);
console.log(new Date() + "store.getters.order.payment.piaType:" + store.getters.order.payment.piaType);
console.log("------------- payment.vue pagePrereqs end -------------------");
}
}
return preReqResult;
},
getWOrkOrderNumber() {
if (store.getters.order.workOrderNumber) {

View file

@ -121,6 +121,9 @@ store.getters = {
isInsurance: false,
parentAccountNumber: applicationConfig.CASH_PARENT_ACCOUNT_NUMBER,
},
customer: {
emailAddress: "test@test.com",
}
},
};
@ -136,6 +139,9 @@ afterEach(() => {
insuranceCoverage: {},
isInsurance: false,
},
customer: {
emailAddress: "test@test.com",
}
};
});
@ -263,6 +269,9 @@ describe("quote.vue", () => {
glassParts: ["item", "item2"],
},
payment: {},
customer: {
emailAddress: "test@test.com",
},
serviceLocation: {
zipCode: "12345",
zipCodeCtu: "value",
@ -309,6 +318,9 @@ describe("quote.vue", () => {
glassParts: ["item", "item2"],
},
payment: {},
customer: {
emailAddress: "test@test.com",
},
serviceLocation: {
zipCode: "12345",
zipCodeCtu: "value",
@ -355,6 +367,9 @@ describe("quote.vue", () => {
payment: {
isInsurance: null,
},
customer: {
emailAddress: "test@test.com",
},
serviceLocation: {
state: null,
},
@ -396,6 +411,9 @@ describe("quote.vue", () => {
payment: {
isInsurance: null,
},
customer: {
emailAddress: "test@test.com",
},
serviceLocation: {
state: null,
},
@ -438,6 +456,9 @@ describe("quote.vue", () => {
payment: {
isInsurance: true,
},
customer: {
emailAddress: "test@test.com",
},
serviceLocation: {
state: null,
},
@ -480,6 +501,9 @@ describe("quote.vue", () => {
payment: {
isInsurance: false,
},
customer: {
emailAddress: "test@test.com",
},
serviceLocation: {
state: null,
},
@ -523,6 +547,9 @@ describe("quote.vue", () => {
payment: {
isInsurance: null, // Ensure that previous selection isn't overriding selection
},
customer: {
emailAddress: "test@test.com",
},
serviceLocation: {
state: null,
},
@ -568,6 +595,9 @@ describe("quote.vue", () => {
payment: {
isInsurance: null, // Ensure that previous selection isn't overriding selection
},
customer: {
emailAddress: "test@test.com",
},
serviceLocation: {
state: null,
},
@ -612,6 +642,9 @@ describe("quote.vue", () => {
payment: {
isInsurance: null,
},
customer: {
emailAddress: "test@test.com",
},
serviceLocation: {
state: "AZ",
},
@ -717,6 +750,9 @@ describe("quote.vue", () => {
isInsurance: true,
inactivePromos: [],
},
customer: {
emailAddress: "test@test.com",
},
},
externalParameterState: { isExternalParameter: 1 },
externalParameterQuote: {

View file

@ -77,6 +77,11 @@
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" />
<saveProgressModalQuestion
modalWidgetName="SaveProgressModalWidget"
v-if="!isEmailInStoreOnPageLoad"
@save-progress="saveProgress" />
<textBlock
cmsWidgetName="quoteDisclaimer"
justifyText="center"
@ -84,11 +89,6 @@
class="mb-5 quote-disclaimer" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<button @click="handleClick">Click me!</button>
</div>
</div>
</div>
</Form>
</template>
@ -105,6 +105,7 @@ import contentGroupModal from "@/fmg-components/content-group-modal/content-grou
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
import afterpayModalBanner from "@/layouts/quote/afterpay-modal-banner/afterpay-modal-banner";
import recalDisclaimer from "@/layouts/quote/recal-disclaimer/recal-disclaimer.vue";
import saveProgressModalQuestion from "@/fmg-components/save-progress-modal-question/save-progress-modal-question";
// Supporting files
import baseMixin from "@/mixins/base-mixin.js";
@ -189,6 +190,8 @@ export default {
];
const resultMap = await settleAllPromises(promiseResultMap);
const emailFromStore = store.getters.order.customer.emailAddress;
const lineItems = deepClone(store.getters.order.lineItems);
lineItems.vaps = lineItems.vaps ?? [];
const nullSafeGlassParts = lineItems.glassParts ?? [];
@ -269,6 +272,7 @@ export default {
// Call the "next" function to complete the transition to this page.
next(async (vm) => {
vm.setCmsContent(resultMap.cmsContent);
vm.isEmailInStoreOnPageLoad = emailFromStore?.length > 0;
vm.addableVaps = addableVaps;
vm.lineItems = lineItems;
vm.availableLineItems = pricingResults;
@ -415,6 +419,7 @@ export default {
servicePackage: null,
holdParentAccountNumber: null,
holdBillToAccountNumber: null,
isEmailInStoreOnPageLoad: null,
};
},
computed: {
@ -504,6 +509,9 @@ export default {
backButtonAction() {
vehicleQuestionsMixin.methods.navigateBack(this);
},
saveProgress() {
saveQuote({ pageNameToLog: "quote" });
},
forwardButtonAction() {
this.dispatchStoreAction(
this.storeActions.SAVE_PAYMENT_TYPE,
@ -689,6 +697,7 @@ export default {
afterpayModalBanner,
promoModalQuestion,
recalDisclaimer,
saveProgressModalQuestion,
},
};
</script>

View file

@ -402,7 +402,7 @@ export default {
!options.length ||
store.getters.payment?.insuranceCoverage?.isVerified ||
(store.getters.order.payment?.isInsurance &&
getFunnelCookie().HasDelayedClaimRegistration)
getFunnelCookie()?.HasDelayedClaimRegistration)
) {
return true;
} else {

View file

@ -113,6 +113,7 @@ const routes = [
store.commit(storeMutations.UPDATE_AFFILIATE_COOKIES, affiliateCookies);
}
setupAdvertiserTracking();
log(" --load session start");
const loadSessionResponse = await loadSessionIfPresent(
to.query.isInsurance != null
? to.query.isInsurance == "true"
@ -121,6 +122,7 @@ const routes = [
: null,
to.query.fmgPage
);
log(" --load session end");
//Update external parameter state but not when returning from heritage
if (!to.query.fmgPage?.startsWith("service-location")) {
@ -155,7 +157,10 @@ const routes = [
// Reroute around pages that should not be available.
// Exception 1: Integrated Users should not see vehicle or quote pages.
if (store.getters.payment?.insuranceCoverage?.isVerified) {
if (
store.getters.payment?.insuranceCoverage?.isVerified ||
store.getters.order?.referralNumber?.length === 6
) {
if (to.query.fmgPage === fmgPageValues.VEHICLE) {
to.query.fmgPage = fmgPageValues.VEHICLE_DAMAGE;
} else if (
@ -194,6 +199,13 @@ const routes = [
if (!arePagePrerequisitesValid(component)) {
console.log("Page prereq error: " + component.default.name);
if (to.query.fmgPage === fmgPageValues.PAYMENT) {
await store.getters.applicationUser.saveSessionPromise;
window.location = applicationConfig.PIA_CANCEL_URL;
return;
}
GoToFunnelStartOn404(next);
}
@ -380,6 +392,8 @@ router.navigateWithSaving = (
};
router.navigateToExternalUrl = (url, optionalQuery = {}) => {
log("------------- router navigateToExternalUrl -----------------");
log("url:", url);
navigateToUrl(url, optionalQuery);
};
@ -397,6 +411,13 @@ router.overrideNavigation = (
optionalParams = {},
optionalPageData
) => {
log("------------- router overrideNavigation -----------------");
log("scenario:", scenario);
log("currentRoute:", currentRoute);
log("isSavingNavigation:", isSavingNavigation);
log("optionalQuery:", optionalQuery);
log("optionalParams:", optionalParams);
log("optionalPageData:", optionalPageData);
navigate(
scenario,
currentRoute,
@ -508,6 +529,9 @@ async function navigate(
// Get navigation map depending on the scenario and the current 'page' you're on.
function getNavigationMap(scenario, currentRoute) {
const fmgPageValue = currentRoute.query.fmgPage;
log("------------- router index.js getNavigationMap start -----------------");
log("fmgPage: " + fmgPageValue + " scenario: " + scenario, "");
log("------------- router index.js getNavigationMap end -----------------");
const matchedQueryValue = routingTable(store)
.filter(
(item) =>
@ -525,7 +549,7 @@ function log(message, data) {
data = data ?? "";
const outData = typeof data === "object" ? JSON.stringify(data) : data;
if (log === "true") {
if (log === "true" || store.getters.applicationUser.loggingOption) {
console.log(new Date() + message + outData);
}
}

View file

@ -470,6 +470,10 @@ const routingTable = function (store) {
scenario: navigationScenarios.CLICKED_FORWARD,
destinationFmgPageValue: fmgPageValues.PAYMENT_METHOD,
},
{
scenario: navigationScenarios.CLICKED_PAY_NOW,
destinationFmgPageValue: fmgPageValues.PAYMENT_METHOD,
},
],
},
{

View file

@ -1946,7 +1946,7 @@ export const actions = {
referralCorrelationId: order.referralCorrelationId,
referralNumber: order.referralNumber?.toString(),
referralSequenceNumber: order.referralSequenceNumber,
emailAddress: "cn@gm.com", //order.customer.emailAddress,
emailAddress: order.customer.emailAddress,
lastPage: pageNameToLog,
isRepair: damage.isRepair,
firstName: order.customer.firstName,
@ -2509,10 +2509,12 @@ export const actions = {
},
savePaymentMethodChoice(context, paymentMethod) {
const isPia = paymentMethod !== paymentMethods.LATER;
var isPia = null;
if (paymentMethod !== paymentMethods.NONE) {
isPia = paymentMethod !== paymentMethods.LATER;
}
context.commit(storeMutations.UPDATE_IS_PIA, isPia);
context.commit(storeMutations.UPDATE_PIA_TYPE, isPia ? paymentMethod : null);
},

View file

@ -77,7 +77,7 @@ body {
}
}
}
.modal-open {
.modal-open:not(.prevent-modal-scroll) {
.container-fluid {
&.page-container-grouped-styles {
overflow: hidden;

View file

@ -5,7 +5,7 @@
:class="[
isPrimary ? 'btn-primary' : 'btn-secondary',
isFloat ? 'float-end' : '',
isLoaderDisplayed ? 'has-loader' : '',
(isLoaderDisplayed && !suppressLoader) ? 'has-loader' : '',
]"
@click="clicked">
<span class="m-0">{{ this.buttonText }}</span>
@ -47,7 +47,7 @@ export default {
true
);
if (!this.isDisabled) {
this.isLoaderDisplayed = true;
if (!this.suppressLoader) this.isLoaderDisplayed = true;
this.$emit("click-event");
}
},