Merge pull request #1197 from Safelite/rlsmerge/develop-to-submit-cash-30-June
Rlsmerge/develop to submit-cash 30 june
This commit is contained in:
commit
e230fe765a
41 changed files with 20123 additions and 510 deletions
|
|
@ -8,4 +8,9 @@ const PREMIUM_TIME_SLOT_ID_FLAG = "-premium";
|
||||||
|
|
||||||
const PREMIUM_FEE_PART_TYPE = "EARLY BIRD";
|
const PREMIUM_FEE_PART_TYPE = "EARLY BIRD";
|
||||||
|
|
||||||
export { AppointmentTypeStrings, PREMIUM_TIME_SLOT_ID_FLAG, PREMIUM_FEE_PART_TYPE };
|
const RouteCodeFlags = {
|
||||||
|
ALL_DAY_DROP_OFF: "ALL DAY DROP OFF",
|
||||||
|
OVERNIGHT_DROP_OFF: "OVERNIGHT DROP OFF",
|
||||||
|
};
|
||||||
|
|
||||||
|
export { AppointmentTypeStrings, PREMIUM_TIME_SLOT_ID_FLAG, PREMIUM_FEE_PART_TYPE, RouteCodeFlags };
|
||||||
|
|
|
||||||
18896
src/constants/single-windshield-carids.js
Normal file
18896
src/constants/single-windshield-carids.js
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -54,6 +54,7 @@ const storeActions = {
|
||||||
RESET_DAMAGE_STATE_AND_DEPENDENCIES: "resetDamageAndDependencies",
|
RESET_DAMAGE_STATE_AND_DEPENDENCIES: "resetDamageAndDependencies",
|
||||||
RESET_REGISTRATION_STATE_AND_DEPENDENCIES: "resetRegistrationAndDependencies",
|
RESET_REGISTRATION_STATE_AND_DEPENDENCIES: "resetRegistrationAndDependencies",
|
||||||
RESET_PARTS_STATE_AND_DEPENDENCIES: "resetPartsAndDependencies",
|
RESET_PARTS_STATE_AND_DEPENDENCIES: "resetPartsAndDependencies",
|
||||||
|
RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES: "resetServiceLocationAndDependencies",
|
||||||
RESET_STATE: "resetState",
|
RESET_STATE: "resetState",
|
||||||
|
|
||||||
// SAVE COMPONENT STATE
|
// SAVE COMPONENT STATE
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ const storeMutations = {
|
||||||
UPDATE_REGISTRATION_LAST_NAME: "updateRegistrationLastName",
|
UPDATE_REGISTRATION_LAST_NAME: "updateRegistrationLastName",
|
||||||
UPDATE_REGISTRATION: "updateRegistration",
|
UPDATE_REGISTRATION: "updateRegistration",
|
||||||
|
|
||||||
|
UPDATE_SERVICE_ZIP: "updateServiceZip",
|
||||||
UPDATE_SERVICE_LOCATION: "updateServiceLocation",
|
UPDATE_SERVICE_LOCATION: "updateServiceLocation",
|
||||||
UPDATE_SCHEDULE: "updateSchedule",
|
UPDATE_SCHEDULE: "updateSchedule",
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
import { shallowMount, mount } from "@vue/test-utils";
|
import { shallowMount, mount } from "@vue/test-utils";
|
||||||
import buttonQuestion from "./button-question";
|
import buttonQuestion from "./button-question";
|
||||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||||
import crypto from "crypto";
|
|
||||||
|
|
||||||
global.crypto = crypto;
|
|
||||||
|
|
||||||
describe("buttonQuestion.vue", () => {
|
describe("buttonQuestion.vue", () => {
|
||||||
it("Should show overflow classes on fieldset if isOverflowScrollable is true", () => {
|
it("Should show overflow classes on fieldset if isOverflowScrollable is true", () => {
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,7 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
today() {
|
today() {
|
||||||
if (this.todayOverrideDateString) {
|
if (this.todayOverrideDateString) {
|
||||||
return new Date(this.todayOverrideDateString);
|
return new Date(this.todayOverrideDateString + "T00:00:00");
|
||||||
}
|
}
|
||||||
return new Date();
|
return new Date();
|
||||||
},
|
},
|
||||||
|
|
@ -151,6 +151,9 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
initializeComponent(initialData) {
|
||||||
|
this.setCalendarData(initialData);
|
||||||
|
},
|
||||||
fireDateClickedEvent() {
|
fireDateClickedEvent() {
|
||||||
this.$emit("date-clicked");
|
this.$emit("date-clicked");
|
||||||
},
|
},
|
||||||
|
|
@ -177,9 +180,12 @@ export default {
|
||||||
nextSunday.setDate(date.getDate() + daysUntilNextSunday);
|
nextSunday.setDate(date.getDate() + daysUntilNextSunday);
|
||||||
return nextSunday;
|
return nextSunday;
|
||||||
},
|
},
|
||||||
getInitialViewWeeks(today, initialViewRowsToShow) {
|
getInitialViewWeeks(today, initialViewRowsToShow, preSelectedDateString) {
|
||||||
// TODO: this only is for future direction; need to create logic for past direction
|
// TODO: this only is for future direction; need to create logic for past direction
|
||||||
const weeks = [];
|
const weeks = [];
|
||||||
|
|
||||||
|
if (preSelectedDateString) initialViewRowsToShow = 26;
|
||||||
|
|
||||||
let weekStartDate = this.getWeekStartDate(today);
|
let weekStartDate = this.getWeekStartDate(today);
|
||||||
let weekEndDate = this.getWeekEndDate(today);
|
let weekEndDate = this.getWeekEndDate(today);
|
||||||
for (let i = 0; i < initialViewRowsToShow; i++) {
|
for (let i = 0; i < initialViewRowsToShow; i++) {
|
||||||
|
|
@ -192,15 +198,23 @@ export default {
|
||||||
weekStartDate: weekStartDate,
|
weekStartDate: weekStartDate,
|
||||||
weekEndDate: weekEndDate,
|
weekEndDate: weekEndDate,
|
||||||
});
|
});
|
||||||
|
if (
|
||||||
|
preSelectedDateString &&
|
||||||
|
new Date(preSelectedDateString + "T00:00:00") < weekEndDate
|
||||||
|
) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// are any of these weeks split between two months?
|
// are any of these weeks split between two months?
|
||||||
|
// NOTE: a week split between two months counts as 2 weeks
|
||||||
const hasSplitWeek = (week) => {
|
const hasSplitWeek = (week) => {
|
||||||
return week.weekStartDate.getMonth() !== week.weekEndDate.getMonth() ? true : false;
|
return week.weekStartDate.getMonth() !== week.weekEndDate.getMonth() ? true : false;
|
||||||
};
|
};
|
||||||
const splitWeekIndex = weeks.findIndex(hasSplitWeek);
|
const splitWeekIndex = weeks.findIndex(hasSplitWeek);
|
||||||
|
|
||||||
if (splitWeekIndex > -1) {
|
if (!preSelectedDateString && splitWeekIndex > -1) {
|
||||||
|
// a preSelectedDateString precludes split week logic
|
||||||
const week1 = [];
|
const week1 = [];
|
||||||
const week2 = [];
|
const week2 = [];
|
||||||
let switchToWeek2 = false;
|
let switchToWeek2 = false;
|
||||||
|
|
@ -245,7 +259,7 @@ export default {
|
||||||
if (this.today) {
|
if (this.today) {
|
||||||
todayDate = this.today;
|
todayDate = this.today;
|
||||||
} else if (config.todayOverrideDateString) {
|
} else if (config.todayOverrideDateString) {
|
||||||
todayDate = new Date(config.todayOverrideDateString);
|
todayDate = new Date(config.todayOverrideDateString + "T00:00:00");
|
||||||
} else {
|
} else {
|
||||||
todayDate = new Date();
|
todayDate = new Date();
|
||||||
}
|
}
|
||||||
|
|
@ -262,20 +276,19 @@ export default {
|
||||||
|
|
||||||
const initialViewWeeks = this.getInitialViewWeeks(
|
const initialViewWeeks = this.getInitialViewWeeks(
|
||||||
todayDate,
|
todayDate,
|
||||||
config.initialViewRowsToShow
|
config.initialViewRowsToShow,
|
||||||
|
config.preSelectedDate
|
||||||
);
|
);
|
||||||
|
|
||||||
const initialViewStartDate = todayDate;
|
const initialViewStartDate = todayDate;
|
||||||
const initialViewEndDate = initialViewWeeks[initialViewWeeks.length - 1].weekEndDate;
|
const initialViewEndDate = initialViewWeeks[initialViewWeeks.length - 1].weekEndDate;
|
||||||
const firstSaturdayMonth = initialViewWeeks[0].weekEndDate.getMonth();
|
const firstSaturdayMonth = initialViewWeeks[0].weekEndDate.getMonth();
|
||||||
const lastSundayMonth =
|
const lastSundayMonth =
|
||||||
initialViewWeeks[initialViewWeeks.length - 1].weekStartDate.getMonth();
|
initialViewWeeks[initialViewWeeks.length - 1].weekStartDate.getMonth();
|
||||||
|
|
||||||
let hideSomeDaysForInitialView = false;
|
let hideSomeDaysForInitialView = false;
|
||||||
let hideSecondMonth = false;
|
let hideSecondMonth = false;
|
||||||
|
|
||||||
// TODO - NEED TO CREATE OPPOSITE LOGIC FOR "past" DIRECTION LIKE BELOW vvvvv
|
// TODO - NEED TO CREATE OPPOSITE LOGIC FOR "past" DIRECTION LIKE BELOW v v v
|
||||||
if (calendarViewDirection === "future") {
|
if (calendarViewDirection === "future" && !config.preSelectedDate) {
|
||||||
if (firstSaturdayMonth !== lastSundayMonth) {
|
if (firstSaturdayMonth !== lastSundayMonth) {
|
||||||
hideSomeDaysForInitialView = true;
|
hideSomeDaysForInitialView = true;
|
||||||
}
|
}
|
||||||
|
|
@ -288,7 +301,7 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const myPromise = new Promise((resolve, reject) => {
|
const loadInitialDataPromise = new Promise((resolve, reject) => {
|
||||||
const response = config.customSelectableDatesCallback(
|
const response = config.customSelectableDatesCallback(
|
||||||
initialViewStartDate.toISOString().split("T")[0],
|
initialViewStartDate.toISOString().split("T")[0],
|
||||||
initialViewEndDate.toISOString().split("T")[0],
|
initialViewEndDate.toISOString().split("T")[0],
|
||||||
|
|
@ -298,7 +311,7 @@ export default {
|
||||||
resolve(response);
|
resolve(response);
|
||||||
});
|
});
|
||||||
|
|
||||||
return myPromise.then((response) => {
|
return loadInitialDataPromise.then((response) => {
|
||||||
const initialData = {
|
const initialData = {
|
||||||
todayDate: todayDate,
|
todayDate: todayDate,
|
||||||
initialViewStartDate: initialViewStartDate,
|
initialViewStartDate: initialViewStartDate,
|
||||||
|
|
@ -307,50 +320,11 @@ export default {
|
||||||
initialShopTimeSlotsResponse: response,
|
initialShopTimeSlotsResponse: response,
|
||||||
hideSomeDaysForInitialView: hideSomeDaysForInitialView,
|
hideSomeDaysForInitialView: hideSomeDaysForInitialView,
|
||||||
hideSecondMonth: hideSecondMonth,
|
hideSecondMonth: hideSecondMonth,
|
||||||
|
preSelectedDate: config.preSelectedDate,
|
||||||
};
|
};
|
||||||
return initialData;
|
return initialData;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
initializeComponent(initialData) {
|
|
||||||
this.setCalendarData(initialData);
|
|
||||||
},
|
|
||||||
scrollToElement(elementId, speed, easing) {
|
|
||||||
// TODO - needs to be cleaned up & refactored
|
|
||||||
function scrollTopSmooth(wrapper, target, duration = 300, timingName = "linear") {
|
|
||||||
const initY = wrapper.scrollTop;
|
|
||||||
const wrapperRect = wrapper.getBoundingClientRect();
|
|
||||||
const targetRect = target.getBoundingClientRect();
|
|
||||||
const targetY = targetRect.top - wrapperRect.top - BUFFER_OFFSET;
|
|
||||||
const timingFunc = TIMINGFUNC_MAP[timingName];
|
|
||||||
|
|
||||||
let start = null;
|
|
||||||
|
|
||||||
const step = (timestamp) => {
|
|
||||||
start = start || timestamp;
|
|
||||||
const progress = timestamp - start,
|
|
||||||
// Growing from 0 to 1
|
|
||||||
time = Math.min(1, (timestamp - start) / duration);
|
|
||||||
|
|
||||||
const percentageNew = timingFunc(time);
|
|
||||||
const distanceToGo = targetY;
|
|
||||||
const thisDistance = percentageNew * distanceToGo;
|
|
||||||
|
|
||||||
wrapper.scrollTo(0, initY + thisDistance);
|
|
||||||
|
|
||||||
if (percentageNew < 1) {
|
|
||||||
window.requestAnimationFrame(step);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.requestAnimationFrame(step);
|
|
||||||
}
|
|
||||||
|
|
||||||
const wrapper = this.$refs.datePickerFieldset;
|
|
||||||
const targetMonth = document.getElementById(elementId);
|
|
||||||
|
|
||||||
scrollTopSmooth(wrapper, targetMonth, 800, "ease-in-out");
|
|
||||||
},
|
|
||||||
|
|
||||||
async setCalendarData(config = {}) {
|
async setCalendarData(config = {}) {
|
||||||
this.hideSomeDaysForInitialView = config.hideSomeDaysForInitialView;
|
this.hideSomeDaysForInitialView = config.hideSomeDaysForInitialView;
|
||||||
const hideSecondMonth = config.hideSecondMonth;
|
const hideSecondMonth = config.hideSecondMonth;
|
||||||
|
|
@ -370,6 +344,7 @@ export default {
|
||||||
initialViewStartDate: config.initialViewStartDate,
|
initialViewStartDate: config.initialViewStartDate,
|
||||||
initialViewEndDate: config.initialViewEndDate,
|
initialViewEndDate: config.initialViewEndDate,
|
||||||
hideSecondMonth: hideSecondMonth,
|
hideSecondMonth: hideSecondMonth,
|
||||||
|
preSelectedDate: config.preSelectedDate,
|
||||||
};
|
};
|
||||||
if (direction === "future") {
|
if (direction === "future") {
|
||||||
// first 0, then 1
|
// first 0, then 1
|
||||||
|
|
@ -389,8 +364,17 @@ export default {
|
||||||
}
|
}
|
||||||
this.months = months;
|
this.months = months;
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
},
|
|
||||||
|
|
||||||
|
if (config.preSelectedDate) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
//Advance to month
|
||||||
|
const monthToShow = this.months.find((month) =>
|
||||||
|
month.monthClass.includes("month-preselected")
|
||||||
|
);
|
||||||
|
this.scrollToElement(monthToShow.monthString);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
async getMonthData(offset = requiredParameter(), options) {
|
async getMonthData(offset = requiredParameter(), options) {
|
||||||
/* options will contain:
|
/* options will contain:
|
||||||
calendarViewDirection (string)
|
calendarViewDirection (string)
|
||||||
|
|
@ -399,6 +383,7 @@ export default {
|
||||||
monthsBeforeToLoadOffset (number),
|
monthsBeforeToLoadOffset (number),
|
||||||
monthsAfterToLoadOffset (number),
|
monthsAfterToLoadOffset (number),
|
||||||
hideSecondMonth (boolean),
|
hideSecondMonth (boolean),
|
||||||
|
preSelectedDate (string)
|
||||||
|
|
||||||
data used:
|
data used:
|
||||||
todayDate (date object)
|
todayDate (date object)
|
||||||
|
|
@ -410,7 +395,10 @@ export default {
|
||||||
const calendarViewDirection = options.calendarViewDirection;
|
const calendarViewDirection = options.calendarViewDirection;
|
||||||
const initialViewStartDate = options.initialViewStartDate;
|
const initialViewStartDate = options.initialViewStartDate;
|
||||||
const initialViewEndDate = options.initialViewEndDate;
|
const initialViewEndDate = options.initialViewEndDate;
|
||||||
const hideSecondMonth = options.hideSecondMonth; // <<<<<<<<<<<<<
|
const hideSecondMonth = options.hideSecondMonth;
|
||||||
|
const preSelectedDateObj = options.preSelectedDate
|
||||||
|
? new Date(options.preSelectedDate + "T00:00:00")
|
||||||
|
: null;
|
||||||
const dates = [];
|
const dates = [];
|
||||||
let monthClass = "";
|
let monthClass = "";
|
||||||
let isMonthThatHidesSomeDaysForInitialView;
|
let isMonthThatHidesSomeDaysForInitialView;
|
||||||
|
|
@ -447,11 +435,23 @@ export default {
|
||||||
const startDateDayIndex = monthStartDate.getDay();
|
const startDateDayIndex = monthStartDate.getDay();
|
||||||
const endDateDayIndex = monthEndDate.getDay();
|
const endDateDayIndex = monthEndDate.getDay();
|
||||||
|
|
||||||
if (Math.abs(offset) === 1 && hideSecondMonth) {
|
if (preSelectedDateObj) {
|
||||||
monthClass = monthClass + " month-hidden";
|
if (
|
||||||
} else if (Math.abs(offset) > 1) {
|
monthStartDate.getFullYear() === preSelectedDateObj.getFullYear() &&
|
||||||
monthClass = monthClass + " month-hidden";
|
monthStartDate.getMonth() === preSelectedDateObj.getMonth()
|
||||||
|
) {
|
||||||
|
monthClass = monthClass + " month-preselected";
|
||||||
|
} else if (monthStartDate > preSelectedDateObj) {
|
||||||
|
monthClass = monthClass + " month-hidden";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (Math.abs(offset) === 1 && hideSecondMonth) {
|
||||||
|
monthClass = monthClass + " month-hidden";
|
||||||
|
} else if (Math.abs(offset) > 1) {
|
||||||
|
monthClass = monthClass + " month-hidden";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
Math.abs(offset) === options.monthsAfterToLoadOffset &&
|
Math.abs(offset) === options.monthsAfterToLoadOffset &&
|
||||||
calendarViewDirection === "future"
|
calendarViewDirection === "future"
|
||||||
|
|
@ -581,6 +581,38 @@ export default {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
scrollToElement(elementId, speed, easing) {
|
||||||
|
// TODO - needs to be cleaned up & refactored
|
||||||
|
function scrollTopSmooth(wrapper, target, duration = 300, timingName = "linear") {
|
||||||
|
const initY = wrapper.scrollTop;
|
||||||
|
const wrapperRect = wrapper.getBoundingClientRect();
|
||||||
|
const targetRect = target.getBoundingClientRect();
|
||||||
|
const targetY = targetRect.top - wrapperRect.top - BUFFER_OFFSET;
|
||||||
|
const timingFunc = TIMINGFUNC_MAP[timingName];
|
||||||
|
let start = null;
|
||||||
|
|
||||||
|
const step = (timestamp) => {
|
||||||
|
start = start || timestamp;
|
||||||
|
const progress = timestamp - start,
|
||||||
|
// Growing from 0 to 1
|
||||||
|
time = Math.min(1, (timestamp - start) / duration);
|
||||||
|
const percentageNew = timingFunc(time);
|
||||||
|
const distanceToGo = targetY;
|
||||||
|
const thisDistance = percentageNew * distanceToGo;
|
||||||
|
|
||||||
|
wrapper.scrollTo(0, initY + thisDistance);
|
||||||
|
if (percentageNew < 1) {
|
||||||
|
window.requestAnimationFrame(step);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.requestAnimationFrame(step);
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapper = document.getElementById("date-picker-fieldset");
|
||||||
|
const targetMonth = document.getElementById(elementId);
|
||||||
|
|
||||||
|
scrollTopSmooth(wrapper, targetMonth, 800, "ease-in-out");
|
||||||
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
loader,
|
loader,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { shallowMount } from "@vue/test-utils";
|
import { shallowMount } from "@vue/test-utils";
|
||||||
import dropdownQuestion from "./dropdown-question";
|
import dropdownQuestion from "./dropdown-question";
|
||||||
import crypto from "crypto";
|
|
||||||
|
|
||||||
// Mock CMS content
|
// Mock CMS content
|
||||||
const questionText = "Question Text";
|
const questionText = "Question Text";
|
||||||
|
|
@ -12,8 +11,6 @@ const mockMixin = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
global.crypto = crypto;
|
|
||||||
|
|
||||||
// TODO: Remove the following from dropdown-question.vue -> :class="(errors && errors.length) || hasError ? 'has-error' : ''"
|
// TODO: Remove the following from dropdown-question.vue -> :class="(errors && errors.length) || hasError ? 'has-error' : ''"
|
||||||
// It is not being used.
|
// It is not being used.
|
||||||
describe("dropdownQuestion.vue", () => {
|
describe("dropdownQuestion.vue", () => {
|
||||||
|
|
|
||||||
|
|
@ -30,28 +30,26 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { useField } from "vee-validate";
|
import { useField } from "vee-validate";
|
||||||
|
import { v4 as uuidv4 } from "uuid";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "dropdown-question",
|
name: "dropdown-question",
|
||||||
props: {
|
props: {
|
||||||
modelValue: String,
|
customDropdownId: String,
|
||||||
customInputId: String,
|
|
||||||
options: {
|
options: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
modelValue: String,
|
||||||
isDisabled: Boolean,
|
isDisabled: Boolean,
|
||||||
isRequired: Boolean,
|
isRequired: Boolean,
|
||||||
validationRules: String,
|
validationRules: String,
|
||||||
cmsWidgetName: String,
|
cmsWidgetName: String,
|
||||||
hasError: Boolean,
|
hasError: Boolean,
|
||||||
placeHolderText: String,
|
|
||||||
customDropdownId: String,
|
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const dropdownId = !props.customDropdownId
|
const uuid = uuidv4();
|
||||||
? `dropdown-${crypto.randomUUID()}`
|
const dropdownId = !props.customDropdownId ? `dropdown-${uuid}` : props.customDropdownId;
|
||||||
: props.customDropdownId;
|
|
||||||
|
|
||||||
const propsClone = Object.assign({}, props);
|
const propsClone = Object.assign({}, props);
|
||||||
const modelValue = propsClone.modelValue;
|
const modelValue = propsClone.modelValue;
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,13 @@ const mockMeta = (returnValue) => jest.fn(async () => Promise.resolve(returnValu
|
||||||
|
|
||||||
import { shallowMount } from "@vue/test-utils";
|
import { shallowMount } from "@vue/test-utils";
|
||||||
import modal from "./modal";
|
import modal from "./modal";
|
||||||
import crypto from "crypto";
|
|
||||||
import { useForm } from "vee-validate";
|
import { useForm } from "vee-validate";
|
||||||
import { Modal } from "bootstrap";
|
import { Modal } from "bootstrap";
|
||||||
|
|
||||||
const footerButtonText = "Sample footer text here.";
|
const footerButtonText = "Sample footer text here.";
|
||||||
const headerText = "Sample header text here.";
|
const headerText = "Sample header text here.";
|
||||||
|
|
||||||
global.crypto = crypto;
|
|
||||||
|
|
||||||
describe("modal.vue", () => {
|
describe("modal.vue", () => {
|
||||||
it("Should display modal header text when headerText is defined", async () => {
|
it("Should display modal header text when headerText is defined", async () => {
|
||||||
// Arrange / Act
|
// Arrange / Act
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@
|
||||||
import modalButtonMain from "@/digital-components/modal/ux-components/modal-button-main/modal-button-main";
|
import modalButtonMain from "@/digital-components/modal/ux-components/modal-button-main/modal-button-main";
|
||||||
import { Modal } from "bootstrap";
|
import { Modal } from "bootstrap";
|
||||||
import { useForm } from "vee-validate";
|
import { useForm } from "vee-validate";
|
||||||
|
import { v4 as uuidv4 } from "uuid";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "modal",
|
name: "modal",
|
||||||
|
|
@ -58,7 +59,9 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const modalId = `modal-${crypto.randomUUID()}`;
|
const uuid = uuidv4();
|
||||||
|
const modalId = `modal-${uuid}`;
|
||||||
|
|
||||||
const { meta, validate, resetForm } = useForm();
|
const { meta, validate, resetForm } = useForm();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -90,10 +93,12 @@ export default {
|
||||||
openModal() {
|
openModal() {
|
||||||
const modal = Modal.getOrCreateInstance(document.getElementById(this.modalId));
|
const modal = Modal.getOrCreateInstance(document.getElementById(this.modalId));
|
||||||
modal.show();
|
modal.show();
|
||||||
|
this.$emit("isModalOpened", true);
|
||||||
},
|
},
|
||||||
closeModal() {
|
closeModal() {
|
||||||
const modal = Modal.getInstance(document.getElementById(this.modalId));
|
const modal = Modal.getInstance(document.getElementById(this.modalId));
|
||||||
modal.hide();
|
modal.hide();
|
||||||
|
this.$emit("isModalOpened", false);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,7 @@
|
||||||
:text="getRouterLinkDisplayTextFromCopy(copy)"
|
:text="getRouterLinkDisplayTextFromCopy(copy)"
|
||||||
href="#!"
|
href="#!"
|
||||||
@click-event="$emit('textLinkClicked', getRouterLinkRouteFromCopy(copy))"
|
@click-event="$emit('textLinkClicked', getRouterLinkRouteFromCopy(copy))"
|
||||||
:data-bs-target="'#' + getRouterLinkRouteFromCopy(copy)"
|
:data-bs-target="'#' + getRouterLinkRouteFromCopy(copy)" />
|
||||||
aria-label="Modal window" />
|
|
||||||
</span>
|
</span>
|
||||||
<span v-else v-html="copy"></span>
|
<span v-else v-html="copy"></span>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { shallowMount } from "@vue/test-utils";
|
import { shallowMount } from "@vue/test-utils";
|
||||||
import textboxQuestion from "./textbox-question";
|
import textboxQuestion from "./textbox-question";
|
||||||
import crypto from "crypto";
|
|
||||||
|
|
||||||
// Mock CMS content
|
// Mock CMS content
|
||||||
const questionText = "Question Text";
|
const questionText = "Question Text";
|
||||||
|
|
@ -13,8 +12,6 @@ const mockMixin = {
|
||||||
};
|
};
|
||||||
const maska = jest.fn();
|
const maska = jest.fn();
|
||||||
|
|
||||||
global.crypto = crypto;
|
|
||||||
|
|
||||||
describe("textboxQuestion.vue", () => {
|
describe("textboxQuestion.vue", () => {
|
||||||
it("Should render a text input", async () => {
|
it("Should render a text input", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|
|
||||||
|
|
@ -78,13 +78,14 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { useField, validate } from "vee-validate";
|
import { useField, validate } from "vee-validate";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
|
||||||
import loader from "@/ux-components/loader/loader.vue";
|
import loader from "@/ux-components/loader/loader.vue";
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
|
import { v4 as uuidv4 } from "uuid";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "textbox-question",
|
name: "textbox-question",
|
||||||
props: {
|
props: {
|
||||||
|
customInputId: String,
|
||||||
type: {
|
type: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "text",
|
default: "text",
|
||||||
|
|
@ -98,7 +99,6 @@ export default {
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
modelValue: String,
|
modelValue: String,
|
||||||
customInputId: String,
|
|
||||||
isDisabled: Boolean,
|
isDisabled: Boolean,
|
||||||
isRequired: Boolean,
|
isRequired: Boolean,
|
||||||
hasIcon: Boolean, // If input has an icon
|
hasIcon: Boolean, // If input has an icon
|
||||||
|
|
@ -122,7 +122,8 @@ export default {
|
||||||
keyDownHandler: Function,
|
keyDownHandler: Function,
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const inputId = !props.customInputId ? `input-${crypto.randomUUID()}` : props.customInputId;
|
const uuid = uuidv4();
|
||||||
|
const inputId = !props.customInputId ? `input-${uuid}` : props.customInputId;
|
||||||
|
|
||||||
const propsClone = Object.assign({}, props);
|
const propsClone = Object.assign({}, props);
|
||||||
const modelValue = propsClone.modelValue;
|
const modelValue = propsClone.modelValue;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// Components
|
// Components
|
||||||
import addressQuestions from "@/layouts/address-lookup/customer-questions/address-questions/address-questions";
|
import addressQuestions from "@/fmg-components/address-questions/address-questions";
|
||||||
import alert from "@/ux-components/alert/alert";
|
import alert from "@/ux-components/alert/alert";
|
||||||
|
|
||||||
// Supporting Files
|
// Supporting Files
|
||||||
|
|
@ -7,7 +7,6 @@ import { mount, shallowMount } from "@vue/test-utils";
|
||||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||||
import { storeMutations } from "@/constants/store-mutations";
|
import { storeMutations } from "@/constants/store-mutations";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
import { createImportSpecifier } from "typescript";
|
|
||||||
|
|
||||||
let autocompleteElement;
|
let autocompleteElement;
|
||||||
describe("address-questions.vue", () => {
|
describe("address-questions.vue", () => {
|
||||||
|
|
@ -134,22 +133,19 @@ describe("address-questions.vue", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Should set this.showAddressFields to true when the model is prepopulated", async () => {
|
test("Should set this.showAddressFields to true when the model is prepopulated", async () => {
|
||||||
// Arrange
|
// Arrange / Act
|
||||||
// Act
|
const { wrapper } = setupMocks({
|
||||||
const newAddressModel = {
|
props: {
|
||||||
streetAddress: "foo",
|
modelValue: {
|
||||||
city: "foo",
|
streetAddress: "foo",
|
||||||
state: "foo",
|
city: "foo",
|
||||||
zipCode: "55555",
|
state: "foo",
|
||||||
};
|
zipCode: "55555",
|
||||||
const wrapper = shallowMount(addressQuestions, {
|
},
|
||||||
propsData: {
|
|
||||||
modelValue: newAddressModel,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Act
|
await wrapper.vm.$nextTick();
|
||||||
wrapper.vm.setupAddressLookup();
|
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.vm.showAddressFields).toBe(true);
|
expect(wrapper.vm.showAddressFields).toBe(true);
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="address-questions" role="application">
|
<div class="address-questions">
|
||||||
<div class="row mb-4">
|
<div class="row mb-4" aria-live="polite">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<textboxQuestion
|
<textboxQuestion
|
||||||
id="streetAddressField"
|
id="streetAddressField"
|
||||||
|
|
@ -122,14 +122,16 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
autocomplete: null,
|
||||||
|
autocompleteListener: null,
|
||||||
showAddressFields: false,
|
showAddressFields: false,
|
||||||
|
matchFound: null, // null = no attempted match, true = match was found, false = match was not found
|
||||||
displayVerificationWarning: false,
|
displayVerificationWarning: false,
|
||||||
displayNoMatchWarning: false,
|
displayNoMatchWarning: false,
|
||||||
alertHeadlineVerificationWarning: "",
|
alertHeadlineVerificationWarning: "",
|
||||||
alertCopyVerificationWarning: "",
|
alertCopyVerificationWarning: "",
|
||||||
alertHeadlineNoMatchWarning: "",
|
alertHeadlineNoMatchWarning: "",
|
||||||
alertCopyNoMatchWarning: "",
|
alertCopyNoMatchWarning: "",
|
||||||
matchFound: null, // null = no attempted match, true = match was found, false = match was not found
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -203,182 +205,202 @@ export default {
|
||||||
return this.captureApartmentNumberOrBusinessName;
|
return this.captureApartmentNumberOrBusinessName;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
addressField1: {
|
||||||
|
get: function () {
|
||||||
|
return document.getElementById("autocomplete");
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setupAddressLookup() {
|
loadGooglePlacesAutocompleteScript() {
|
||||||
this.showAddressFields = false;
|
// Load the Google Places Autocomplete script
|
||||||
if (
|
|
||||||
this.addressModel.streetAddress &&
|
|
||||||
this.addressModel.city &&
|
|
||||||
this.addressModel.state &&
|
|
||||||
this.addressModel.zipCode
|
|
||||||
) {
|
|
||||||
this.showAddressFields = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const addressField1 = document.getElementById("autocomplete");
|
|
||||||
const self = this;
|
|
||||||
|
|
||||||
const apiKey = applicationConfig.GOOGLE_PLACES_API_KEY;
|
const apiKey = applicationConfig.GOOGLE_PLACES_API_KEY;
|
||||||
|
|
||||||
this.$loadScript(
|
this.$loadScript(
|
||||||
`https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places&callback=Function.prototype`
|
`https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places&callback=Function.prototype`
|
||||||
)
|
).then(() => {
|
||||||
.then(() => {
|
// When loaded, trigger the setup
|
||||||
// Script is loaded, initialize the autocomplete textbox
|
this.initializeAutocomplete();
|
||||||
const autocomplete = new window.google.maps.places.Autocomplete(addressField1, {
|
});
|
||||||
componentRestrictions: { country: ["us"] },
|
},
|
||||||
fields: ["address_components"],
|
initializeAutocomplete() {
|
||||||
types: ["geocode"],
|
// Initialize the Google Places Autocomplete
|
||||||
});
|
this.autocomplete = new window.google.maps.places.Autocomplete(this.addressField1, {
|
||||||
|
componentRestrictions: { country: ["us"] },
|
||||||
|
fields: ["address_components"],
|
||||||
|
types: ["geocode"],
|
||||||
|
});
|
||||||
|
|
||||||
// Standard place_changed event handling
|
// Set up the Autocomplete place_changed event to call our method to fill in the address
|
||||||
const autocompleteListener = window.google.maps.event.addListener(
|
this.autocompleteListener = window.google.maps.event.addListener(
|
||||||
autocomplete,
|
this.autocomplete,
|
||||||
"place_changed",
|
"place_changed",
|
||||||
fillInAddress
|
this.fillInAddress
|
||||||
|
);
|
||||||
|
|
||||||
|
// When the Street Address textbox receives focus,
|
||||||
|
// append the search results list container to the bottom of the textbox
|
||||||
|
// and disable browser autofill
|
||||||
|
this.addressField1.addEventListener("focus", (e) => {
|
||||||
|
// Make place results box stick to the input on scroll
|
||||||
|
const streetAddressField = document.getElementById("streetAddressField");
|
||||||
|
const autocompleteResultsContainer =
|
||||||
|
document.getElementsByClassName("pac-container")[0];
|
||||||
|
if (autocompleteResultsContainer) {
|
||||||
|
streetAddressField.appendChild(autocompleteResultsContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unfortunately this is the only place we can set the autocomplete attribute without the
|
||||||
|
// Google Places object resetting it to "off" which does nothing to prevent browser autofill
|
||||||
|
this.addressField1.setAttribute("autocomplete", "do-not-autofill");
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addressField1.addEventListener("keydown", (e) => {
|
||||||
|
// If a match has been previously attempted then do nothing
|
||||||
|
if (this.matchFound !== null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const event = new Event("place_changed");
|
||||||
|
|
||||||
|
// When either of the two enter keys or the tab key are pressed
|
||||||
|
if (e.code === "Enter" || e.code === "NumpadEnter" || e.code === "Tab") {
|
||||||
|
// Grab the selected item
|
||||||
|
const selectedItem = document.querySelector(
|
||||||
|
".pac-container .pac-item-selected"
|
||||||
);
|
);
|
||||||
|
|
||||||
addressField1.addEventListener("focus", () => {
|
if (selectedItem !== null) {
|
||||||
// Wrapping the addressField1 element in the Google Address Autocomplete object
|
// If an item was selected then fill in the address with the selected item
|
||||||
// will cause "autocomplete='off'" which Chrome completely ignores. This event
|
// by triggering the "place_changed" event of the Autocomplete object
|
||||||
// handler will set the value to something arbitrary so autofill doesn't work.
|
this.autocomplete.dispatchEvent(event);
|
||||||
// https://stackoverflow.com/a/30976223
|
} else {
|
||||||
addressField1.setAttribute("autocomplete", "do-not-autofill");
|
// Otherwise fill-in the address using first item from the list.
|
||||||
|
this.fillInAddressUsingFirstItem();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Make place results box stick to the input on scroll
|
this.addressField1.addEventListener("change", () => {
|
||||||
const streetAddressField = document.getElementById("streetAddressField");
|
// If a match has been previously attempted then do nothing
|
||||||
const autocompleteResultsContainer =
|
if (this.matchFound !== null) {
|
||||||
document.getElementsByClassName("pac-container")[0];
|
return;
|
||||||
if (autocompleteResultsContainer) {
|
}
|
||||||
streetAddressField.appendChild(autocompleteResultsContainer);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
addressField1.addEventListener("keydown", (e) => {
|
// Get the address that the user clicked on (if any)
|
||||||
const autocomplete = document.getElementById("autocomplete");
|
const clickedAddress = document.querySelector(".pac-container .pac-item:hover");
|
||||||
const event = new Event("place_changed");
|
|
||||||
|
|
||||||
if (e.code === "Enter" || e.code === "NumpadEnter" || e.code === "Tab") {
|
// If the Street Address field changed without clicking (i.e. by pressing Tab, or clicking outside the field)
|
||||||
const selectedItem = document.querySelector(
|
if (clickedAddress === null) {
|
||||||
".pac-container .pac-item-selected"
|
// Fill-in the address using first item in the list.
|
||||||
);
|
this.fillInAddressUsingFirstItem();
|
||||||
if (selectedItem !== null) {
|
}
|
||||||
// Fill-in the address using selected item in the list.
|
});
|
||||||
autocomplete.dispatchEvent(event);
|
},
|
||||||
//fillInAddress(selectedItem.textContent);
|
fillInAddress(place) {
|
||||||
} else {
|
if (!place) {
|
||||||
// Fill-in the address using first item in the list.
|
place = this.autocomplete.getPlace();
|
||||||
fillInAddressUsingFirstItem();
|
}
|
||||||
|
|
||||||
|
if (place && place.address_components) {
|
||||||
|
this.matchFound = true;
|
||||||
|
|
||||||
|
const self = this;
|
||||||
|
this.$nextTick(function () {
|
||||||
|
self.showAddressFields = true;
|
||||||
|
|
||||||
|
for (const component of place.address_components) {
|
||||||
|
const componentType = component.types[0];
|
||||||
|
|
||||||
|
switch (componentType) {
|
||||||
|
case "street_number": {
|
||||||
|
self.addressModel.streetAddress = component.long_name;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "route": {
|
||||||
|
self.addressModel.streetAddress += " " + component.short_name;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "locality": {
|
||||||
|
self.addressModel.city = component.long_name;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "administrative_area_level_1": {
|
||||||
|
self.addressModel.state = component.short_name;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "postal_code": {
|
||||||
|
self.addressModel.zipCode = component.long_name;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
addressField1.addEventListener("change", () => {
|
|
||||||
// If a match has been previously attempted then do nothing
|
|
||||||
if (self.matchFound !== null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the address that the user clicked on (if any)
|
|
||||||
const clickedAddress = document.querySelector(
|
|
||||||
".pac-container .pac-item:hover"
|
|
||||||
);
|
|
||||||
|
|
||||||
// If the Street Address field changed without clicking (i.e. by pressing Tab, or clicking outside the field)
|
|
||||||
if (clickedAddress === null) {
|
|
||||||
// Fill-in the address using first item in the list.
|
|
||||||
fillInAddressUsingFirstItem();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function fillInAddressUsingFirstItem() {
|
|
||||||
// Fill-in the address using first item in the list.
|
|
||||||
const item = document.querySelector(".pac-container .pac-item");
|
|
||||||
if (item != null) {
|
|
||||||
const firstResult = item.textContent;
|
|
||||||
const geocoder = new window.google.maps.Geocoder();
|
|
||||||
geocoder.geocode(
|
|
||||||
{
|
|
||||||
address: firstResult,
|
|
||||||
},
|
|
||||||
function (results, status) {
|
|
||||||
if (status === window.google.maps.GeocoderStatus.OK) {
|
|
||||||
fillInAddress(results[0]);
|
|
||||||
self.displayVerificationWarning = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
self.matchFound = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fillInAddress(place) {
|
// After filling in the address fields, disable the address autocomplete
|
||||||
if (!place) {
|
this.unloadAutocomplete();
|
||||||
place = autocomplete.getPlace();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (place && place.address_components) {
|
// Restore focus to the first address field
|
||||||
self.matchFound = true;
|
this.addressField1.focus();
|
||||||
|
|
||||||
self.$nextTick(function () {
|
|
||||||
self.showAddressFields = true;
|
|
||||||
|
|
||||||
for (const component of place.address_components) {
|
|
||||||
const componentType = component.types[0];
|
|
||||||
|
|
||||||
switch (componentType) {
|
|
||||||
case "street_number": {
|
|
||||||
self.addressModel.streetAddress = component.long_name;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "route": {
|
|
||||||
self.addressModel.streetAddress +=
|
|
||||||
" " + component.short_name;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "locality": {
|
|
||||||
self.addressModel.city = component.long_name;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "administrative_area_level_1": {
|
|
||||||
self.addressModel.state = component.short_name;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "postal_code": {
|
|
||||||
self.addressModel.zipCode = component.long_name;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// after showing the address fields, disable the address autocomplete
|
|
||||||
window.google.maps.event.removeListener(autocompleteListener);
|
|
||||||
window.google.maps.event.clearInstanceListeners(autocomplete);
|
|
||||||
addressField1.onchange = null;
|
|
||||||
const pacContainer = document.querySelector(".pac-container");
|
|
||||||
if (pacContainer) {
|
|
||||||
pacContainer.remove();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
// Failed to fetch script
|
|
||||||
console.log("Unable to load Google Places API script");
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fillInAddressUsingFirstItem() {
|
||||||
|
// Fill-in the address using first item in the list.
|
||||||
|
const item = document.querySelector(".pac-container .pac-item");
|
||||||
|
if (item != null) {
|
||||||
|
const firstResult = item.textContent;
|
||||||
|
const geocoder = new window.google.maps.Geocoder();
|
||||||
|
const self = this;
|
||||||
|
geocoder.geocode(
|
||||||
|
{
|
||||||
|
address: firstResult,
|
||||||
|
},
|
||||||
|
function (results, status) {
|
||||||
|
if (status === window.google.maps.GeocoderStatus.OK) {
|
||||||
|
self.fillInAddress(results[0]);
|
||||||
|
self.displayVerificationWarning = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.matchFound = false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
resetAlerts() {
|
resetAlerts() {
|
||||||
this.displayVerificationWarning = false;
|
this.displayVerificationWarning = false;
|
||||||
},
|
},
|
||||||
|
unloadAutocomplete() {
|
||||||
|
if (this.autocompleteListener && this.autocomplete) {
|
||||||
|
window.google.maps.event.removeListener(this.autocompleteListener);
|
||||||
|
this.autocompleteListener = null;
|
||||||
|
|
||||||
|
window.google.maps.event.clearInstanceListeners(this.autocomplete);
|
||||||
|
this.autocomplete = null;
|
||||||
|
|
||||||
|
this.addressField1.onchange = null;
|
||||||
|
|
||||||
|
const pacContainer = document.querySelector(".pac-container");
|
||||||
|
if (pacContainer) {
|
||||||
|
pacContainer.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.setupAddressLookup();
|
// If we already have a full address, show it
|
||||||
|
this.showAddressFields =
|
||||||
|
(this.addressModel.streetAddress ?? "") !== "" &&
|
||||||
|
(this.addressModel.city ?? "") !== "" &&
|
||||||
|
(this.addressModel.state ?? "") !== "" &&
|
||||||
|
(this.addressModel.zipCode ?? "") !== "";
|
||||||
|
|
||||||
|
if (!this.showAddressFields) {
|
||||||
|
this.loadGooglePlacesAutocompleteScript();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
unmounted() {
|
||||||
|
this.unloadAutocomplete();
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
matchFound: {
|
matchFound: {
|
||||||
|
|
@ -398,7 +420,7 @@ export default {
|
||||||
this.addressModel.state = "";
|
this.addressModel.state = "";
|
||||||
this.addressModel.zipCode = "";
|
this.addressModel.zipCode = "";
|
||||||
}
|
}
|
||||||
this.showAddressFields = true;
|
|
||||||
this.displayVerificationWarning = false;
|
this.displayVerificationWarning = false;
|
||||||
|
|
||||||
// Only deep watch the Address Model after a failed match
|
// Only deep watch the Address Model after a failed match
|
||||||
|
|
@ -414,11 +436,6 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
modelValue: {
|
|
||||||
handler() {
|
|
||||||
this.setupAddressLookup();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
textboxQuestion,
|
textboxQuestion,
|
||||||
|
|
@ -1,8 +1,5 @@
|
||||||
import { mount } from "@vue/test-utils";
|
import { mount } from "@vue/test-utils";
|
||||||
import contentGroupModal from "./content-group-modal";
|
import contentGroupModal from "./content-group-modal";
|
||||||
import crypto from "crypto";
|
|
||||||
|
|
||||||
global.crypto = crypto;
|
|
||||||
|
|
||||||
describe("content-group-modal.vue", () => {
|
describe("content-group-modal.vue", () => {
|
||||||
it("Should display header text when HeaderText is defined in the CMS", async () => {
|
it("Should display header text when HeaderText is defined in the CMS", async () => {
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,19 @@
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
v-on="{ 'show.bs.modal': show, 'hide.bs.modal': hide }"
|
v-on="{ 'show.bs.modal': show, 'hide.bs.modal': hide }"
|
||||||
:style="`height: calc(100% - ${currentFooterAndHeaderHeight}px);`">
|
:style="`height: calc(100% - ${currentFooterAndHeaderHeight}px);`">
|
||||||
|
<div class="menu-modal-container">
|
||||||
|
<button
|
||||||
|
class="menu-button"
|
||||||
|
type="button"
|
||||||
|
:class="[isActive ? 'active' : '']"
|
||||||
|
data-bs-toggle="modal"
|
||||||
|
data-bs-target="#footerModal"
|
||||||
|
aria-label="Hamburger Menu (modal window)">
|
||||||
|
<div class="bar1"></div>
|
||||||
|
<div class="bar2"></div>
|
||||||
|
<div class="bar3"></div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div class="modal-dialog modal-fullscreen">
|
<div class="modal-dialog modal-fullscreen">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header visually-hidden">
|
<div class="modal-header visually-hidden">
|
||||||
|
|
|
||||||
|
|
@ -13,19 +13,19 @@
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="text-container slide">
|
<div class="text-container slide">
|
||||||
<p>
|
<p>
|
||||||
Finding shops near you
|
Tidying up the shop
|
||||||
<span class="dot-1">.</span>
|
<span class="dot-1">.</span>
|
||||||
<span class="dot-2">.</span>
|
<span class="dot-2">.</span>
|
||||||
<span class="dot-3">.</span>
|
<span class="dot-3">.</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Looking for dates
|
Getting all the glass shined up
|
||||||
<span class="dot-1">.</span>
|
<span class="dot-1">.</span>
|
||||||
<span class="dot-2">.</span>
|
<span class="dot-2">.</span>
|
||||||
<span class="dot-3">.</span>
|
<span class="dot-3">.</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Searching for times
|
Planning your new view of the road
|
||||||
<span class="dot-1">.</span>
|
<span class="dot-1">.</span>
|
||||||
<span class="dot-2">.</span>
|
<span class="dot-2">.</span>
|
||||||
<span class="dot-3">.</span>
|
<span class="dot-3">.</span>
|
||||||
|
|
@ -164,19 +164,19 @@ export default {
|
||||||
transform: translateX(-600px);
|
transform: translateX(-600px);
|
||||||
}
|
}
|
||||||
55% {
|
55% {
|
||||||
transform: translateX(-1110px);
|
transform: translateX(-1195px);
|
||||||
}
|
}
|
||||||
66% {
|
66% {
|
||||||
transform: translateX(-1110px);
|
transform: translateX(-1195px);
|
||||||
}
|
}
|
||||||
77% {
|
77% {
|
||||||
transform: translateX(-1600px);
|
transform: translateX(-1750px);
|
||||||
}
|
}
|
||||||
88% {
|
88% {
|
||||||
transform: translateX(-1600px);
|
transform: translateX(-1750px);
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
transform: translateX(-2050px);
|
transform: translateX(-2200px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,24 @@
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import analyticsMixIn from "@/mixins/analytics-mixin.js";
|
import analyticsMixIn from "@/mixins/analytics-mixin.js";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
import router from "@/router";
|
||||||
|
|
||||||
import { applicationConfig } from "@/constants/application-config.js";
|
import { applicationConfig } from "@/constants/application-config.js";
|
||||||
import { GaCategories, GaActions, GaLabels } from "@/constants/analytics";
|
import { GaCategories, GaActions, GaLabels } from "@/constants/analytics";
|
||||||
import { headerKeys } from "@/constants/header-keys";
|
import { headerKeys } from "@/constants/header-keys";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
callHttpClient({ method, endpoint, payload, logApiCall = true, isFormData = false }) {
|
callHttpClient({
|
||||||
|
method,
|
||||||
|
endpoint,
|
||||||
|
payload,
|
||||||
|
logApiCall = true,
|
||||||
|
isFormData = false,
|
||||||
|
additionalSuccessEventDataHandler,
|
||||||
|
}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const cfDistroUrl = applicationConfig.CONSUMER_CF_DISTRO;
|
const cfDistroUrl = applicationConfig.CONSUMER_CF_DISTRO;
|
||||||
let payloadAndAnalyticsData = {};
|
const payloadAndAnalyticsData = Object.assign({}, payload, { AppName: "FixMyGlass" });
|
||||||
if (isFormData) {
|
|
||||||
payloadAndAnalyticsData = payload;
|
|
||||||
payloadAndAnalyticsData.append("AppName", "FixMyGlass");
|
|
||||||
} else {
|
|
||||||
Object.assign(payloadAndAnalyticsData, payload, { AppName: "FixMyGlass" });
|
|
||||||
}
|
|
||||||
const headers = {
|
const headers = {
|
||||||
[headerKeys.EXPERIMENT]: JSON.stringify(store.getters.experimentSettings),
|
[headerKeys.EXPERIMENT]: JSON.stringify(store.getters.experimentSettings),
|
||||||
};
|
};
|
||||||
|
|
@ -31,10 +33,16 @@ export default {
|
||||||
}).then(
|
}).then(
|
||||||
(response) => {
|
(response) => {
|
||||||
if (logApiCall) {
|
if (logApiCall) {
|
||||||
|
let additionalEventData = "";
|
||||||
|
if (additionalSuccessEventDataHandler) {
|
||||||
|
additionalEventData = "_" + additionalSuccessEventDataHandler(response);
|
||||||
|
}
|
||||||
|
const pageName = analyticsMixIn.methods.getPageName();
|
||||||
|
const nextPageName = router.lastNavigationPage || pageName;
|
||||||
analyticsMixIn.methods.pushEventToGA(
|
analyticsMixIn.methods.pushEventToGA(
|
||||||
GaCategories.API_RESPONSE,
|
GaCategories.API_RESPONSE,
|
||||||
GaActions.RESULT,
|
`${nextPageName}_${endpoint}`,
|
||||||
`${GaLabels.SUCCESS}_${endpoint}`,
|
`${GaLabels.SUCCESS}${additionalEventData}`,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@
|
||||||
cmsWidgetName="ServiceZipQuestionWidget"
|
cmsWidgetName="ServiceZipQuestionWidget"
|
||||||
v-model="serviceZipCode"
|
v-model="serviceZipCode"
|
||||||
ref="serviceZip"
|
ref="serviceZip"
|
||||||
inputId="7add1b26df344f2caf1678de5797803f"
|
customInputId="serviceZip"
|
||||||
aria-haspopup=""
|
aria-haspopup=""
|
||||||
mask="#####"
|
mask="#####"
|
||||||
validationRules="service-zip-required|service-zip-format" />
|
validationRules="service-zip-required|service-zip-format" />
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
cmsWidgetName="FirstNameQuestionWidget"
|
cmsWidgetName="FirstNameQuestionWidget"
|
||||||
v-model="customerModel.firstName"
|
v-model="customerModel.firstName"
|
||||||
ref="firstName"
|
ref="firstName"
|
||||||
inputId="08497a2efd9a4a73a70360ab47b4838d"
|
customInputId="firstName"
|
||||||
validationRules="first-name-required" />
|
validationRules="first-name-required" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
cmsWidgetName="LastNameQuestionWidget"
|
cmsWidgetName="LastNameQuestionWidget"
|
||||||
v-model="customerModel.lastName"
|
v-model="customerModel.lastName"
|
||||||
ref="lastName"
|
ref="lastName"
|
||||||
inputId="0030e56a57e74a4ab92de7fb8e97fec5"
|
customInputId="lastName"
|
||||||
validationRules="last-name-required" />
|
validationRules="last-name-required" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -26,8 +26,8 @@
|
||||||
cmsWidgetName="EmailAddressQuestionWidget"
|
cmsWidgetName="EmailAddressQuestionWidget"
|
||||||
v-model="customerModel.emailAddress"
|
v-model="customerModel.emailAddress"
|
||||||
ref="emailAddress"
|
ref="emailAddress"
|
||||||
inputId="00450a91b8964a768ce3992e6feb890f"
|
customInputId="emailAddress"
|
||||||
validationRules="email-address-required|email-address-format" />
|
validationRules="email-address-format" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-0">
|
<div class="row mb-0">
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import addressQuestions from "@/layouts/address-lookup/customer-questions/address-questions/address-questions";
|
import addressQuestions from "@/fmg-components/address-questions/address-questions";
|
||||||
import textboxQuestion from "@/digital-components/textbox-question/textbox-question";
|
import textboxQuestion from "@/digital-components/textbox-question/textbox-question";
|
||||||
import { defineRule } from "vee-validate";
|
import { defineRule } from "vee-validate";
|
||||||
import { required } from "@/helpers/validation-rules";
|
import { required } from "@/helpers/validation-rules";
|
||||||
|
|
@ -49,7 +49,6 @@ import textBlock from "@/digital-components/text-block/text-block";
|
||||||
// DEFINE VALIDATION RULES
|
// DEFINE VALIDATION RULES
|
||||||
defineRule("first-name-required", required(errorMessages.FIRST_NAME_REQUIRED));
|
defineRule("first-name-required", required(errorMessages.FIRST_NAME_REQUIRED));
|
||||||
defineRule("last-name-required", required(errorMessages.LAST_NAME_REQUIRED));
|
defineRule("last-name-required", required(errorMessages.LAST_NAME_REQUIRED));
|
||||||
defineRule("email-address-required", required(errorMessages.EMAIL_ADDRESS_REQUIRED));
|
|
||||||
defineRule(
|
defineRule(
|
||||||
"email-address-format",
|
"email-address-format",
|
||||||
regex(
|
regex(
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,6 @@ export default {
|
||||||
if (
|
if (
|
||||||
store.getters.order.vehicle.carId &&
|
store.getters.order.vehicle.carId &&
|
||||||
store.getters.order.serviceLocation.zipCode &&
|
store.getters.order.serviceLocation.zipCode &&
|
||||||
store.getters.order.customer.emailAddress &&
|
|
||||||
store.getters.pageData(fmgPageValues.ADDRESS_VEHICLES)
|
store.getters.pageData(fmgPageValues.ADDRESS_VEHICLES)
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,8 @@
|
||||||
cmsWidgetName="EmailAddressQuestionWidget"
|
cmsWidgetName="EmailAddressQuestionWidget"
|
||||||
v-model="emailAddress"
|
v-model="emailAddress"
|
||||||
inputId="emailAddress"
|
inputId="emailAddress"
|
||||||
isRequired
|
disableAutoFill
|
||||||
validationRules="email-address-required|email-address-format" />
|
validationRules="email-address-format" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-2">
|
<div class="row mb-2">
|
||||||
|
|
@ -117,7 +117,6 @@ import { queryStrings } from "@/constants/query-strings";
|
||||||
// Define Validation Rules
|
// Define Validation Rules
|
||||||
defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
|
defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
|
||||||
defineRule("zip-format", regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, errorMessages.SERVICE_ZIP_FORMAT));
|
defineRule("zip-format", regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, errorMessages.SERVICE_ZIP_FORMAT));
|
||||||
defineRule("email-address-required", required(errorMessages.EMAIL_ADDRESS_REQUIRED));
|
|
||||||
defineRule(
|
defineRule(
|
||||||
"email-address-format",
|
"email-address-format",
|
||||||
regex(
|
regex(
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
cmsWidgetName="LicensePlateNumberQuestionWidget"
|
cmsWidgetName="LicensePlateNumberQuestionWidget"
|
||||||
v-model="licensePlate"
|
v-model="licensePlate"
|
||||||
isRequired
|
isRequired
|
||||||
inputId="license_plate"
|
customInputId="licensePlate"
|
||||||
validationRules="license-plate-required" />
|
validationRules="license-plate-required" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -22,9 +22,9 @@
|
||||||
<textboxQuestion
|
<textboxQuestion
|
||||||
cmsWidgetName="RegistrationZipQuestionWidget"
|
cmsWidgetName="RegistrationZipQuestionWidget"
|
||||||
v-model="registrationZipCode"
|
v-model="registrationZipCode"
|
||||||
inputId="zip"
|
customInputId="zip"
|
||||||
mask="#####"
|
mask="#####"
|
||||||
validationRules="zip-required|zip-format" />
|
validationRules="registration-zip-required|zip-format" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mt-0">
|
<div class="row mt-0">
|
||||||
|
|
@ -32,8 +32,8 @@
|
||||||
<textboxQuestion
|
<textboxQuestion
|
||||||
cmsWidgetName="EmailAddressQuestionWidget"
|
cmsWidgetName="EmailAddressQuestionWidget"
|
||||||
v-model="email"
|
v-model="email"
|
||||||
inputId="email"
|
customInputId="email"
|
||||||
validationRules="email-address-required|email-address-format" />
|
validationRules="email-address-format" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-2">
|
<div class="row mb-2">
|
||||||
|
|
@ -113,9 +113,8 @@ import vinPagesMixin from "@/mixins/vin-pages-mixin";
|
||||||
|
|
||||||
// DEFINE VALIDATION RULES
|
// DEFINE VALIDATION RULES
|
||||||
defineRule("license-plate-required", required(errorMessages.LICENSE_PLATE_REQUIRED));
|
defineRule("license-plate-required", required(errorMessages.LICENSE_PLATE_REQUIRED));
|
||||||
defineRule("zip-required", required(errorMessages.REGISTRATION_ZIP_REQUIRED));
|
defineRule("registration-zip-required", required(errorMessages.REGISTRATION_ZIP_REQUIRED));
|
||||||
defineRule("zip-format", regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, errorMessages.SERVICE_ZIP_FORMAT));
|
defineRule("zip-format", regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, errorMessages.SERVICE_ZIP_FORMAT));
|
||||||
defineRule("email-address-required", required(errorMessages.EMAIL_ADDRESS_REQUIRED));
|
|
||||||
defineRule(
|
defineRule(
|
||||||
"email-address-format",
|
"email-address-format",
|
||||||
regex(
|
regex(
|
||||||
|
|
@ -300,11 +299,12 @@ export default {
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, this.email, false);
|
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, this.email, false);
|
||||||
|
|
||||||
await this.dispatchStoreAction(
|
await this.dispatchStoreAction(
|
||||||
storeActions.SAVE_SERVICE_LOCATION,
|
storeActions.SAVE_SERVICE_ZIP_CODE_INFO,
|
||||||
{
|
{
|
||||||
zipCode: this.serviceZipCode,
|
|
||||||
state: resultMap.serviceZipValidationResponse.state,
|
state: resultMap.serviceZipValidationResponse.state,
|
||||||
|
zipCode: this.serviceZipCode,
|
||||||
zipCodeCtu: resultMap.serviceZipValidationResponse.zipCodeCtu,
|
zipCodeCtu: resultMap.serviceZipValidationResponse.zipCodeCtu,
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,7 @@
|
||||||
args: getRouterLinkRouteFromCopy(copy),
|
args: getRouterLinkRouteFromCopy(copy),
|
||||||
})
|
})
|
||||||
"
|
"
|
||||||
:data-bs-target="'#' + getRouterLinkRouteFromCopy(copy)"
|
:data-bs-target="'#' + getRouterLinkRouteFromCopy(copy)" />
|
||||||
aria-label="Modal window" />
|
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
||||||
|
|
@ -11,3 +11,10 @@ export async function getAlertReasons(ctu) {
|
||||||
|
|
||||||
return Promise.resolve(alertReasons);
|
return Promise.resolve(alertReasons);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function calcDaysBetweenDates(dateString1, dateString2) {
|
||||||
|
const date1 = new Date(dateString1);
|
||||||
|
const date2 = new Date(dateString2);
|
||||||
|
const timeDifference = Math.abs(date2 - date1); // Calculate the time difference in milliseconds
|
||||||
|
return Math.ceil(timeDifference / (1000 * 60 * 60 * 24)); // Convert milliseconds to days
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
mobileCmsWidgetName="MobileTimeSlotModal"
|
mobileCmsWidgetName="MobileTimeSlotModal"
|
||||||
dropoffCmsWidgetName="DropOffTimeSlotModal"
|
dropoffCmsWidgetName="DropOffTimeSlotModal"
|
||||||
sameDayDropOffCmsWidgetName="SameDayDropOffTimeSlotModal"
|
sameDayDropOffCmsWidgetName="SameDayDropOffTimeSlotModal"
|
||||||
|
overnightDropOffCmsWidgetName="OvernightDropOffTimeSlotModal"
|
||||||
v-model="selectedTimeSlotData"
|
v-model="selectedTimeSlotData"
|
||||||
@time-slot-modal-closed="timeSlotModalClosed"
|
@time-slot-modal-closed="timeSlotModalClosed"
|
||||||
:appointmentType="appointmentType"
|
:appointmentType="appointmentType"
|
||||||
|
|
@ -62,6 +63,7 @@ import { storeActions } from "@/constants/store-actions";
|
||||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||||
import { splitCopyOnCMSPlaceHolder } from "@/helpers/cms-content-helper";
|
import { splitCopyOnCMSPlaceHolder } from "@/helpers/cms-content-helper";
|
||||||
|
import { calcDaysBetweenDates } from "@/layouts/schedule/helpers/schedule-helper";
|
||||||
import { AppointmentTypeStrings, PREMIUM_FEE_PART_TYPE } from "@/constants/schedule-constants";
|
import { AppointmentTypeStrings, PREMIUM_FEE_PART_TYPE } from "@/constants/schedule-constants";
|
||||||
import { errorMessages } from "@/constants/error-messages";
|
import { errorMessages } from "@/constants/error-messages";
|
||||||
import { required } from "@/helpers/validation-rules";
|
import { required } from "@/helpers/validation-rules";
|
||||||
|
|
@ -70,33 +72,98 @@ import store from "@/store";
|
||||||
defineRule("date-required", required(errorMessages.DATE_REQUIRED));
|
defineRule("date-required", required(errorMessages.DATE_REQUIRED));
|
||||||
defineRule("time-slot-selection-required", required(errorMessages.DATE_REQUIRED));
|
defineRule("time-slot-selection-required", required(errorMessages.DATE_REQUIRED));
|
||||||
|
|
||||||
const getAvailableDates = async (startDate, endDate, appointmentType, providerNumber) => {
|
// Define constants
|
||||||
// USING DATES PASSED, MAKE AN API CALL
|
const TIME_SLOTS_CALL_DAYS_LIMIT = 34; // needs to be 34 for API limits (35 does not consistently work)
|
||||||
|
|
||||||
let newTimeSlotsResponse;
|
const getAvailableDates = async (
|
||||||
if (appointmentType === AppointmentTypeStrings.MOBILE) {
|
startDateString,
|
||||||
newTimeSlotsResponse = await baseMixin.methods.dispatchStoreAction(
|
endDateString,
|
||||||
storeActions.GET_MOBILE_TIME_SLOTS,
|
appointmentType,
|
||||||
{
|
providerNumber
|
||||||
startDate: startDate,
|
) => {
|
||||||
endDate: endDate,
|
const apiEndDateLimit = new Date(startDateString + "T00:00:00");
|
||||||
},
|
const endDate = new Date(endDateString + "T00:00:00");
|
||||||
false
|
apiEndDateLimit.setDate(apiEndDateLimit.getDate() + TIME_SLOTS_CALL_DAYS_LIMIT);
|
||||||
);
|
|
||||||
} else {
|
// how many days are between startDate and endDate?
|
||||||
newTimeSlotsResponse = await baseMixin.methods.dispatchStoreAction(
|
const difference = calcDaysBetweenDates(startDateString, endDateString);
|
||||||
storeActions.GET_SHOP_TIME_SLOTS,
|
const apiCallsCount = Math.ceil(difference / TIME_SLOTS_CALL_DAYS_LIMIT);
|
||||||
{
|
const storeActionConfigs = [];
|
||||||
startDate: startDate,
|
const timeSlotsData = {};
|
||||||
endDate: endDate,
|
let apiStartDate = new Date(startDateString + "T00:00:00");
|
||||||
shopAppointmentType: appointmentType,
|
let apiEndDate = apiEndDateLimit;
|
||||||
providerNumber: providerNumber,
|
timeSlotsData.days = [];
|
||||||
},
|
|
||||||
false
|
for (let i = 1; i <= apiCallsCount; i++) {
|
||||||
);
|
let storeActionConfig;
|
||||||
|
|
||||||
|
if (i > 1) {
|
||||||
|
apiStartDate = new Date(apiEndDate);
|
||||||
|
apiStartDate.setDate(apiStartDate.getDate() + 1);
|
||||||
|
apiEndDate = new Date(apiStartDate);
|
||||||
|
apiEndDate.setDate(apiEndDate.getDate() + TIME_SLOTS_CALL_DAYS_LIMIT);
|
||||||
|
}
|
||||||
|
if (i === apiCallsCount) {
|
||||||
|
apiEndDate = new Date(endDateString + "T00:00:00");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (appointmentType === AppointmentTypeStrings.MOBILE) {
|
||||||
|
storeActionConfig = {
|
||||||
|
storeAction: storeActions.GET_MOBILE_TIME_SLOTS,
|
||||||
|
payload: {
|
||||||
|
startDate: apiStartDate.toISOString().split("T")[0],
|
||||||
|
endDate: apiEndDate.toISOString().split("T")[0],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
storeActionConfig = {
|
||||||
|
storeAction: storeActions.GET_SHOP_TIME_SLOTS,
|
||||||
|
payload: {
|
||||||
|
startDate: apiStartDate.toISOString().split("T")[0],
|
||||||
|
endDate: apiEndDate.toISOString().split("T")[0],
|
||||||
|
shopAppointmentType: appointmentType,
|
||||||
|
providerNumber: providerNumber,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
storeActionConfigs.push(storeActionConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
return newTimeSlotsResponse.data;
|
// ASYNC METHOD
|
||||||
|
const timeSlotsResponsesData = {
|
||||||
|
days: [],
|
||||||
|
};
|
||||||
|
function compareDayStrings(a, b) {
|
||||||
|
if (a.date < b.date) return -1;
|
||||||
|
if (a.date > b.date) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const makeParallelCalls = async () => {
|
||||||
|
await Promise.all(
|
||||||
|
storeActionConfigs.map(async (storeAction) => {
|
||||||
|
const timeSlotsResponse = await baseMixin.methods.dispatchStoreAction(
|
||||||
|
storeAction.storeAction,
|
||||||
|
storeAction.payload,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
timeSlotsResponsesData.estimatedServiceMinutesMinimum =
|
||||||
|
timeSlotsResponse.data.estimatedServiceMinutesMinimum;
|
||||||
|
timeSlotsResponsesData.estimatedServiceMinutesMaximum =
|
||||||
|
timeSlotsResponse.data.estimatedServiceMinutesMaximum;
|
||||||
|
timeSlotsResponsesData.days = [
|
||||||
|
...timeSlotsResponsesData.days,
|
||||||
|
...timeSlotsResponse.data.days,
|
||||||
|
];
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return makeParallelCalls().then(() => {
|
||||||
|
// sort days chronologically
|
||||||
|
timeSlotsResponsesData.days.sort(compareDayStrings);
|
||||||
|
return timeSlotsResponsesData;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -106,7 +173,7 @@ export default {
|
||||||
selectedDate: this.getSelectedDate(),
|
selectedDate: this.getSelectedDate(),
|
||||||
selectedTimeSlotData: {
|
selectedTimeSlotData: {
|
||||||
id: this.getSelectedRouteCode(),
|
id: this.getSelectedRouteCode(),
|
||||||
isPremiumAppointment: null,
|
isPremiumAppointment: this.isMobilePremiumFeeOnOrderInVuex(),
|
||||||
},
|
},
|
||||||
selectableDatesData: [],
|
selectableDatesData: [],
|
||||||
mobilePremiumAppointmentFee: null,
|
mobilePremiumAppointmentFee: null,
|
||||||
|
|
@ -115,11 +182,17 @@ export default {
|
||||||
async beforeRouteEnter(to, from, next) {
|
async beforeRouteEnter(to, from, next) {
|
||||||
// Call APIs
|
// Call APIs
|
||||||
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
||||||
const datePickerInitialDataPromise = datePicker.methods.loadInitialData({
|
let preSelectedDate = await store.getters.order.schedule.date;
|
||||||
|
if (!preSelectedDate || preSelectedDate.startTime === null) {
|
||||||
|
preSelectedDate = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const datePickerInitialDataPromise = await datePicker.methods.loadInitialData({
|
||||||
// setup config options for date-picker
|
// setup config options for date-picker
|
||||||
selectableDatesSetting: "custom",
|
selectableDatesSetting: "custom",
|
||||||
initialViewRowsToShow: 5,
|
initialViewRowsToShow: 5,
|
||||||
customSelectableDatesCallback: getAvailableDates,
|
customSelectableDatesCallback: getAvailableDates,
|
||||||
|
preSelectedDate: preSelectedDate,
|
||||||
});
|
});
|
||||||
|
|
||||||
const premiumFeePromise = baseMixin.methods.dispatchStoreAction(
|
const premiumFeePromise = baseMixin.methods.dispatchStoreAction(
|
||||||
|
|
@ -142,7 +215,7 @@ export default {
|
||||||
|
|
||||||
const alertReasonsPromise = locationAlerts.methods.loadInitialData(
|
const alertReasonsPromise = locationAlerts.methods.loadInitialData(
|
||||||
store.getters.order.serviceLocation.zipCodeCtu,
|
store.getters.order.serviceLocation.zipCodeCtu,
|
||||||
store.getters.order.serviceLocation.provider?.address?.zipCtu
|
store.getters.order.serviceLocation.provider?.address?.zipCodeCtu
|
||||||
);
|
);
|
||||||
// Settle promises and get results
|
// Settle promises and get results
|
||||||
const promiseResultMap = [
|
const promiseResultMap = [
|
||||||
|
|
@ -258,8 +331,8 @@ export default {
|
||||||
getTimeSlotObjectFromTimeSlotId(timeSlotId) {
|
getTimeSlotObjectFromTimeSlotId(timeSlotId) {
|
||||||
const timeSlots = this.selectableDatesData.days.find(
|
const timeSlots = this.selectableDatesData.days.find(
|
||||||
(selectableDate) => selectableDate.date === this.selectedDate
|
(selectableDate) => selectableDate.date === this.selectedDate
|
||||||
).timeSlots;
|
)?.timeSlots;
|
||||||
return timeSlots.find((timeSlot) => timeSlot.id === timeSlotId);
|
if (timeSlots) return timeSlots.find((timeSlot) => timeSlot.id === timeSlotId);
|
||||||
},
|
},
|
||||||
getSelectedDate() {
|
getSelectedDate() {
|
||||||
return store.getters.order.schedule.date;
|
return store.getters.order.schedule.date;
|
||||||
|
|
@ -267,6 +340,12 @@ export default {
|
||||||
getSelectedRouteCode() {
|
getSelectedRouteCode() {
|
||||||
return store.getters.order.schedule.routeCode;
|
return store.getters.order.schedule.routeCode;
|
||||||
},
|
},
|
||||||
|
isMobilePremiumFeeOnOrderInVuex() {
|
||||||
|
const supportingItemsFromVuex = store.getters.lineItems.supportingItems;
|
||||||
|
return !!supportingItemsFromVuex.filter(
|
||||||
|
(lineItem) => lineItem.partType === PREMIUM_FEE_PART_TYPE
|
||||||
|
).length;
|
||||||
|
},
|
||||||
timeSlotModalClosed() {
|
timeSlotModalClosed() {
|
||||||
// Clear the selectedDate if no timeSlot has been selected
|
// Clear the selectedDate if no timeSlot has been selected
|
||||||
if (!this.selectedTimeSlotData.id) {
|
if (!this.selectedTimeSlotData.id) {
|
||||||
|
|
@ -275,7 +354,7 @@ export default {
|
||||||
},
|
},
|
||||||
updateFooterButtonText(timeSlotData) {
|
updateFooterButtonText(timeSlotData) {
|
||||||
let funnelFooterButtonText;
|
let funnelFooterButtonText;
|
||||||
if (!timeSlotData.id) {
|
if (!timeSlotData.id || !this.appointmentDateAndTime) {
|
||||||
funnelFooterButtonText = "Continue";
|
funnelFooterButtonText = "Continue";
|
||||||
} else {
|
} else {
|
||||||
funnelFooterButtonText =
|
funnelFooterButtonText =
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,8 @@
|
||||||
v-if="supplementalInformationBlock"
|
v-if="supplementalInformationBlock"
|
||||||
v-html="supplementalInformationBlock"></div>
|
v-html="supplementalInformationBlock"></div>
|
||||||
<textBlock
|
<textBlock
|
||||||
v-show="shouldShowDropoffDisclaimerText"
|
v-show="disclaimerTextBlockCopy"
|
||||||
:customText="dropoffDisclaimerText"
|
:customText="disclaimerTextBlockCopy"
|
||||||
justifyText="left"
|
justifyText="left"
|
||||||
typeStyle="caption"
|
typeStyle="caption"
|
||||||
class="mb-2" />
|
class="mb-2" />
|
||||||
|
|
@ -53,6 +53,7 @@ import {
|
||||||
AppointmentTypeStrings,
|
AppointmentTypeStrings,
|
||||||
PREMIUM_TIME_SLOT_ID_FLAG,
|
PREMIUM_TIME_SLOT_ID_FLAG,
|
||||||
PREMIUM_FEE_PART_TYPE,
|
PREMIUM_FEE_PART_TYPE,
|
||||||
|
RouteCodeFlags,
|
||||||
} from "@/constants/schedule-constants";
|
} from "@/constants/schedule-constants";
|
||||||
|
|
||||||
// Validation for the modal button
|
// Validation for the modal button
|
||||||
|
|
@ -69,6 +70,7 @@ export default {
|
||||||
mobilePremiumCmsWidgetName: String,
|
mobilePremiumCmsWidgetName: String,
|
||||||
dropoffCmsWidgetName: String,
|
dropoffCmsWidgetName: String,
|
||||||
sameDayDropOffCmsWidgetName: String,
|
sameDayDropOffCmsWidgetName: String,
|
||||||
|
overnightDropOffCmsWidgetName: String,
|
||||||
appointmentType: String,
|
appointmentType: String,
|
||||||
dateAndTimeSlotData: Object,
|
dateAndTimeSlotData: Object,
|
||||||
premiumAppointmentFee: Object,
|
premiumAppointmentFee: Object,
|
||||||
|
|
@ -78,7 +80,7 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
selectedTimeSlotId: this.modelValue.id,
|
selectedTimeSlotId: this.getModifiedSelectedTimeSlotId(),
|
||||||
timeSlotModalListButton: timeSlotModalListButton,
|
timeSlotModalListButton: timeSlotModalListButton,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
@ -92,12 +94,8 @@ export default {
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
modelValue() {
|
modelValue() {
|
||||||
|
this.selectedTimeSlotId = this.getModifiedSelectedTimeSlotId();
|
||||||
// Run component validation that is used at parent level
|
// Run component validation that is used at parent level
|
||||||
if (this.modelValue.isPremiumAppointment) {
|
|
||||||
this.selectedTimeSlotId = this.addPremiumFlagToInput(this.modelValue.id);
|
|
||||||
} else {
|
|
||||||
this.selectedTimeSlotId = this.modelValue.id;
|
|
||||||
}
|
|
||||||
this.handleChange(this.modelValue.id);
|
this.handleChange(this.modelValue.id);
|
||||||
},
|
},
|
||||||
availableTimeSlots(newValue) {
|
availableTimeSlots(newValue) {
|
||||||
|
|
@ -116,9 +114,15 @@ export default {
|
||||||
? this.mobilePremiumCmsWidgetName
|
? this.mobilePremiumCmsWidgetName
|
||||||
: this.mobileCmsWidgetName;
|
: this.mobileCmsWidgetName;
|
||||||
} else {
|
} else {
|
||||||
appointmentTypeCmsWidgetName = this.isSameDay
|
if (!this.selectedTimeSlotId) {
|
||||||
? this.sameDayDropOffCmsWidgetName
|
return null;
|
||||||
: this.dropoffCmsWidgetName;
|
} else {
|
||||||
|
appointmentTypeCmsWidgetName =
|
||||||
|
this.getRelevantDropOffCmsWidgetNameForSelectedTimeSlot(
|
||||||
|
this.selectedTimeSlotId,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return this.getCmsContent(appointmentTypeCmsWidgetName, "BodyText");
|
return this.getCmsContent(appointmentTypeCmsWidgetName, "BodyText");
|
||||||
},
|
},
|
||||||
|
|
@ -131,12 +135,36 @@ export default {
|
||||||
dropoffButtonText() {
|
dropoffButtonText() {
|
||||||
return this.getCmsContent(this.dropoffCmsWidgetName, "HeaderText");
|
return this.getCmsContent(this.dropoffCmsWidgetName, "HeaderText");
|
||||||
},
|
},
|
||||||
|
overnightDropoffButtonText() {
|
||||||
|
return this.getCmsContent(this.overnightDropOffCmsWidgetName, "HeaderText");
|
||||||
|
},
|
||||||
dropoffDisclaimerText() {
|
dropoffDisclaimerText() {
|
||||||
return this.getCmsContent(this.dropoffCmsWidgetName, "FooterText");
|
return this.getCmsContent(this.dropoffCmsWidgetName, "FooterText");
|
||||||
},
|
},
|
||||||
|
overnightDropOffDisclaimerText() {
|
||||||
|
return this.getCmsContent(this.overnightDropOffCmsWidgetName, "FooterText");
|
||||||
|
},
|
||||||
|
disclaimerTextBlockCopy() {
|
||||||
|
if (this.appointmentType === AppointmentTypeStrings.DROP_OFF) {
|
||||||
|
if (this.isSameDay) {
|
||||||
|
return null;
|
||||||
|
} else if (this.selectedTimeSlotId?.includes(RouteCodeFlags.ALL_DAY_DROP_OFF)) {
|
||||||
|
return this.dropoffDisclaimerText;
|
||||||
|
} else if (this.selectedTimeSlotId?.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) {
|
||||||
|
return this.overnightDropOffDisclaimerText;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
dropOffDurationText() {
|
dropOffDurationText() {
|
||||||
return this.getCmsContent(this.dropoffCmsWidgetName, "SubheaderText");
|
return this.getCmsContent(this.dropoffCmsWidgetName, "SubheaderText");
|
||||||
},
|
},
|
||||||
|
overnightDropoffDurationText() {
|
||||||
|
return this.getCmsContent(this.overnightDropOffCmsWidgetName, "SubheaderText");
|
||||||
|
},
|
||||||
inshopDurationText() {
|
inshopDurationText() {
|
||||||
const inshopDurationTextWithoutTime = this.getCmsContent(
|
const inshopDurationTextWithoutTime = this.getCmsContent(
|
||||||
this.cmsWidgetName,
|
this.cmsWidgetName,
|
||||||
|
|
@ -154,15 +182,27 @@ export default {
|
||||||
} else if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) {
|
} else if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) {
|
||||||
return this.inshopDurationText;
|
return this.inshopDurationText;
|
||||||
} else {
|
} else {
|
||||||
return this.dropOffDurationText;
|
if (this.selectedTimeSlotId?.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) {
|
||||||
|
return this.overnightDropoffDurationText;
|
||||||
|
} else if (this.selectedTimeSlotId?.includes(RouteCodeFlags.ALL_DAY_DROP_OFF)) {
|
||||||
|
return this.dropOffDurationText;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
shouldShowDropoffDisclaimerText() {
|
shouldShowDropoffDisclaimerText() {
|
||||||
return this.appointmentType === AppointmentTypeStrings.DROP_OFF && !this.isSameDay;
|
return this.appointmentType === AppointmentTypeStrings.DROP_OFF && !this.isSameDay;
|
||||||
},
|
},
|
||||||
isSameDay() {
|
isSameDay() {
|
||||||
return false;
|
if (!this.dateAndTimeSlotData) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const selectedDate = this.dateAndTimeSlotData.date;
|
||||||
|
const todaysDate = new Date().toISOString().split("T")[0];
|
||||||
|
return selectedDate === todaysDate;
|
||||||
},
|
},
|
||||||
|
|
||||||
dateSelectedReadableDate() {
|
dateSelectedReadableDate() {
|
||||||
if (!this.dateAndTimeSlotData) {
|
if (!this.dateAndTimeSlotData) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -212,6 +252,13 @@ export default {
|
||||||
onModalClosed() {
|
onModalClosed() {
|
||||||
this.$emit("time-slot-modal-closed");
|
this.$emit("time-slot-modal-closed");
|
||||||
},
|
},
|
||||||
|
getModifiedSelectedTimeSlotId() {
|
||||||
|
if (this.modelValue.isPremiumAppointment) {
|
||||||
|
return this.addPremiumFlagToInput(this.modelValue.id);
|
||||||
|
} else {
|
||||||
|
return this.modelValue.id;
|
||||||
|
}
|
||||||
|
},
|
||||||
// Expected input: "HH:MM"
|
// Expected input: "HH:MM"
|
||||||
getDisplayTextForMilitaryTime(militaryTimeInput) {
|
getDisplayTextForMilitaryTime(militaryTimeInput) {
|
||||||
let hours = parseInt(militaryTimeInput.split(":")[0]);
|
let hours = parseInt(militaryTimeInput.split(":")[0]);
|
||||||
|
|
@ -233,6 +280,18 @@ export default {
|
||||||
}
|
}
|
||||||
return displayTextForDurationLength;
|
return displayTextForDurationLength;
|
||||||
},
|
},
|
||||||
|
getRelevantDropOffCmsWidgetNameForSelectedTimeSlot(
|
||||||
|
selectedTimeSlotId,
|
||||||
|
isSameDayRelevant = false
|
||||||
|
) {
|
||||||
|
if (selectedTimeSlotId.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) {
|
||||||
|
return this.overnightDropOffCmsWidgetName;
|
||||||
|
} else if (selectedTimeSlotId.includes(RouteCodeFlags.ALL_DAY_DROP_OFF)) {
|
||||||
|
return this.isSameDay && isSameDayRelevant
|
||||||
|
? this.sameDayDropOffCmsWidgetName
|
||||||
|
: this.dropoffCmsWidgetName;
|
||||||
|
}
|
||||||
|
},
|
||||||
getAvailableTimeSlotsForInshop(timeSlotsForSelectedDate) {
|
getAvailableTimeSlotsForInshop(timeSlotsForSelectedDate) {
|
||||||
return timeSlotsForSelectedDate.map((timeSlot) => {
|
return timeSlotsForSelectedDate.map((timeSlot) => {
|
||||||
const readableTime = this.getDisplayTextForMilitaryTime(timeSlot.startTime);
|
const readableTime = this.getDisplayTextForMilitaryTime(timeSlot.startTime);
|
||||||
|
|
@ -243,12 +302,16 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getAvailableTimeSlotsForDropOff(timeSlotsForSelectedDate) {
|
getAvailableTimeSlotsForDropOff(timeSlotsForSelectedDate) {
|
||||||
return [
|
const availableTimeSlots = timeSlotsForSelectedDate.map((timeSlot) => {
|
||||||
{
|
const buttonLabelValue = timeSlot.id.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)
|
||||||
value: timeSlotsForSelectedDate[0].id,
|
? this.overnightDropoffButtonText
|
||||||
buttonLabel: this.dropoffButtonText,
|
: this.dropoffButtonText;
|
||||||
},
|
return {
|
||||||
];
|
value: timeSlot.id,
|
||||||
|
buttonLabel: buttonLabelValue,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return availableTimeSlots;
|
||||||
},
|
},
|
||||||
getAvailableTimeSlotsForMobile(timeSlotsForSelectedDate) {
|
getAvailableTimeSlotsForMobile(timeSlotsForSelectedDate) {
|
||||||
const availableTimeSlots = timeSlotsForSelectedDate.map((timeSlot) => {
|
const availableTimeSlots = timeSlotsForSelectedDate.map((timeSlot) => {
|
||||||
|
|
|
||||||
|
|
@ -95,8 +95,10 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.list-card img {
|
.appointment-type-question {
|
||||||
height: auto;
|
.list-card img {
|
||||||
width: 3.417rem;
|
height: auto;
|
||||||
|
width: 3.417rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,8 @@ jest.mock("@/store", () => ({
|
||||||
streetAddress: null,
|
streetAddress: null,
|
||||||
city: null,
|
city: null,
|
||||||
state: null,
|
state: null,
|
||||||
zip: null,
|
zipCode: null,
|
||||||
|
zipCodeCtu: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,6 @@ import mobileLocationModalQuestions from "./mobile-location-modal-questions";
|
||||||
import { mount, shallowMount } from "@vue/test-utils";
|
import { mount, shallowMount } from "@vue/test-utils";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
import { storeActions } from "@/constants/store-actions";
|
||||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||||
import modal from "@/digital-components/modal/modal";
|
|
||||||
|
|
||||||
import crypto from "crypto";
|
|
||||||
|
|
||||||
global.crypto = crypto;
|
|
||||||
|
|
||||||
const linkWidgetName = "linkWidgetName";
|
const linkWidgetName = "linkWidgetName";
|
||||||
const modalWidgetName = "modalWidgetName";
|
const modalWidgetName = "modalWidgetName";
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,7 @@
|
||||||
linkType="text"
|
linkType="text"
|
||||||
:text="mobileLocationLinkText"
|
:text="mobileLocationLinkText"
|
||||||
href="#!"
|
href="#!"
|
||||||
@click-event="openModal"
|
@click-event="openModal" />
|
||||||
aria-label="Modal window" />
|
|
||||||
</div>
|
</div>
|
||||||
<div v-show="errorMessage" class="row my-1 form-test-error">
|
<div v-show="errorMessage" class="row my-1 form-test-error">
|
||||||
<span class="d-inline-flex small mt-0 center-error-message" role="alert">
|
<span class="d-inline-flex small mt-0 center-error-message" role="alert">
|
||||||
|
|
@ -33,24 +32,27 @@
|
||||||
:footerButtonText="modalFooterText"
|
:footerButtonText="modalFooterText"
|
||||||
:onModalOpenedCallback="onModalOpened"
|
:onModalOpenedCallback="onModalOpened"
|
||||||
:onModalClosedCallback="onModalClosed"
|
:onModalClosedCallback="onModalClosed"
|
||||||
|
@isModalOpened="setModalStatus"
|
||||||
@footer-button-event="setMobileLocation">
|
@footer-button-event="setMobileLocation">
|
||||||
<addressQuestions
|
<template v-if="isModalOpened">
|
||||||
ref="addressQuestions"
|
<addressQuestions
|
||||||
v-model="internalModel.addressQuestions"
|
ref="addressQuestions"
|
||||||
captureApartmentNumberOrBusinessName="true"
|
v-model="internalModel.addressQuestions"
|
||||||
preserveCityAndStateOnReset="true" />
|
captureApartmentNumberOrBusinessName="true"
|
||||||
<vehicleProtectedQuestion
|
preserveCityAndStateOnReset="true" />
|
||||||
ref="vehicleProtectedQuestion"
|
<vehicleProtectedQuestion
|
||||||
v-model="internalModel.isVehicleProtected"
|
ref="vehicleProtectedQuestion"
|
||||||
cmsWidgetName="VehicleProtectedQuestionWidget" />
|
v-model="internalModel.isVehicleProtected"
|
||||||
<textBlock cmsWidgetName="WorkspaceRequirementsWidget" typeStyle="caption" />
|
cmsWidgetName="VehicleProtectedQuestionWidget" />
|
||||||
<alert
|
<textBlock cmsWidgetName="WorkspaceRequirementsWidget" typeStyle="caption" />
|
||||||
ref="alertInvalidZip"
|
<alert
|
||||||
v-if="displayInvalidZipAlert"
|
ref="alertInvalidZip"
|
||||||
class="my-4"
|
v-if="displayInvalidZipAlert"
|
||||||
cmsWidgetName="AlertInvalidZipWidget"
|
class="my-4"
|
||||||
alertClass="alert-danger"
|
cmsWidgetName="AlertInvalidZipWidget"
|
||||||
v-bind:isDismissible="false" />
|
alertClass="alert-danger"
|
||||||
|
v-bind:isDismissible="false" />
|
||||||
|
</template>
|
||||||
</modal>
|
</modal>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
|
|
@ -62,7 +64,7 @@ import textLink from "@/ux-components/text-link/text-link";
|
||||||
import textBlock from "@/digital-components/text-block/text-block";
|
import textBlock from "@/digital-components/text-block/text-block";
|
||||||
import modal from "@/digital-components/modal/modal";
|
import modal from "@/digital-components/modal/modal";
|
||||||
import alert from "@/ux-components/alert/alert";
|
import alert from "@/ux-components/alert/alert";
|
||||||
import addressQuestions from "@/layouts/address-lookup/customer-questions/address-questions/address-questions";
|
import addressQuestions from "@/fmg-components/address-questions/address-questions";
|
||||||
import vehicleProtectedQuestion from "@/layouts/service-location/mobile-location-modal-questions/vehicle-protected-question/vehicle-protected-question";
|
import vehicleProtectedQuestion from "@/layouts/service-location/mobile-location-modal-questions/vehicle-protected-question/vehicle-protected-question";
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
|
|
@ -74,6 +76,7 @@ import {
|
||||||
|
|
||||||
// Validation
|
// Validation
|
||||||
import { useField } from "vee-validate";
|
import { useField } from "vee-validate";
|
||||||
|
import { v4 as uuidv4 } from "uuid";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "mobile-location-modal-questions",
|
name: "mobile-location-modal-questions",
|
||||||
|
|
@ -82,11 +85,13 @@ export default {
|
||||||
return {
|
return {
|
||||||
internalModel: deepClone(this.modelValue),
|
internalModel: deepClone(this.modelValue),
|
||||||
displayInvalidZipAlert: false,
|
displayInvalidZipAlert: false,
|
||||||
|
isModalOpened: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
|
const uuid = uuidv4();
|
||||||
const componentId = !props.customComponentId
|
const componentId = !props.customComponentId
|
||||||
? `component-${crypto.randomUUID()}`
|
? `component-${uuid}`
|
||||||
: props.customComponentId;
|
: props.customComponentId;
|
||||||
|
|
||||||
// Integrate this component as a single field with an object for it's value into the page level validation
|
// Integrate this component as a single field with an object for it's value into the page level validation
|
||||||
|
|
@ -185,6 +190,9 @@ export default {
|
||||||
modalFooterText() {
|
modalFooterText() {
|
||||||
return this.getCmsContent(this.modalWidgetName, "FooterText");
|
return this.getCmsContent(this.modalWidgetName, "FooterText");
|
||||||
},
|
},
|
||||||
|
modal() {
|
||||||
|
return this.$refs[this.modalName];
|
||||||
|
},
|
||||||
addressModel: {
|
addressModel: {
|
||||||
get: function () {
|
get: function () {
|
||||||
return this.modelValue.addressQuestions;
|
return this.modelValue.addressQuestions;
|
||||||
|
|
@ -193,48 +201,23 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
openModal() {
|
openModal() {
|
||||||
this.$refs[this.modalName].openModal();
|
this.modal.openModal();
|
||||||
},
|
|
||||||
closeModal() {
|
|
||||||
this.$refs[this.modalName].closeModal();
|
|
||||||
},
|
},
|
||||||
onModalOpened() {
|
onModalOpened() {
|
||||||
this.internalModel = deepClone(this.modelValue);
|
this.internalModel = deepClone(this.modelValue);
|
||||||
},
|
},
|
||||||
|
setModalStatus(isOpened) {
|
||||||
|
this.isModalOpened = isOpened;
|
||||||
|
},
|
||||||
|
closeModal() {
|
||||||
|
this.modal.closeModal();
|
||||||
|
},
|
||||||
onModalClosed() {
|
onModalClosed() {
|
||||||
this.displayInvalidZipAlert = false;
|
this.displayInvalidZipAlert = false;
|
||||||
this.internalModel = deepClone(this.modelValue);
|
this.internalModel = deepClone(this.modelValue);
|
||||||
this.resetValidation();
|
|
||||||
},
|
|
||||||
resetComponent(updatedServiceZipCodeInfo) {
|
|
||||||
// Reset the validation form, setting the initial values
|
|
||||||
// for the state and zipCode to those that were entered
|
|
||||||
// on the service-zip-modal-question component
|
|
||||||
this.$refs[this.modalName].resetForm({
|
|
||||||
values: {
|
|
||||||
autocomplete: updatedServiceZipCodeInfo.streetAddress,
|
|
||||||
city: updatedServiceZipCodeInfo.city,
|
|
||||||
state: updatedServiceZipCodeInfo.state,
|
|
||||||
zipCode: updatedServiceZipCodeInfo.zipCode,
|
|
||||||
isVehicleProtected: updatedServiceZipCodeInfo.isVehicleProtected,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
resetModalButtonStyle() {
|
resetModalButtonStyle() {
|
||||||
this.$refs[this.modalName].resetButtonStyle();
|
this.modal.resetButtonStyle();
|
||||||
},
|
|
||||||
resetValidation() {
|
|
||||||
this.$refs.addressQuestions.resetAlerts();
|
|
||||||
|
|
||||||
this.$refs[this.modalName].resetForm({
|
|
||||||
values: {
|
|
||||||
autocomplete: this.internalModel.addressQuestions.streetAddress,
|
|
||||||
city: this.internalModel.addressQuestions.city,
|
|
||||||
state: this.internalModel.addressQuestions.state,
|
|
||||||
zipCode: this.internalModel.addressQuestions.zipCode,
|
|
||||||
isVehicleProtected: this.internalModel.isVehicleProtected,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
async setMobileLocation() {
|
async setMobileLocation() {
|
||||||
if (
|
if (
|
||||||
|
|
@ -284,14 +267,6 @@ export default {
|
||||||
this.internalModel = deepClone(newValue);
|
this.internalModel = deepClone(newValue);
|
||||||
|
|
||||||
this.handleChange(newValue);
|
this.handleChange(newValue);
|
||||||
|
|
||||||
this.resetComponent({
|
|
||||||
streetAddress: newValue.addressQuestions.streetAddress,
|
|
||||||
city: newValue.addressQuestions.city,
|
|
||||||
state: newValue.addressQuestions.state,
|
|
||||||
zipCode: newValue.addressQuestions.zipCode,
|
|
||||||
isVehicleProtected: newValue.isVehicleProtected,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
deep: true,
|
deep: true,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -68,15 +68,13 @@
|
||||||
linkWidgetName="MobileLocationLinkWidget"
|
linkWidgetName="MobileLocationLinkWidget"
|
||||||
modalWidgetName="MobileLocationModalWidget"
|
modalWidgetName="MobileLocationModalWidget"
|
||||||
:onZipUpdateCallback="reloadShopData" />
|
:onZipUpdateCallback="reloadShopData" />
|
||||||
<Transition name="fade" mode="out-in">
|
<shopQuestion
|
||||||
<shopQuestion
|
ref="shopQuestion"
|
||||||
ref="shopQuestion"
|
v-show="isShopQuestionDisplayed"
|
||||||
v-show="isShopQuestionDisplayed"
|
v-model="selectedProvider"
|
||||||
v-model="selectedProvider"
|
:selectedAppointmentType="selectedAppointmentType"
|
||||||
:selectedAppointmentType="selectedAppointmentType"
|
:isDisplayed="isShopQuestionDisplayed"
|
||||||
:isDisplayed="isShopQuestionDisplayed"
|
cmsWidgetName="ShopQuestionWidget" />
|
||||||
cmsWidgetName="ShopQuestionWidget" />
|
|
||||||
</Transition>
|
|
||||||
<contentGroupModal ref="RecalModal" cmsWidgetName="RecalModal" />
|
<contentGroupModal ref="RecalModal" cmsWidgetName="RecalModal" />
|
||||||
<funnel-footer
|
<funnel-footer
|
||||||
cmsWidgetName="FunnelFooterWidget"
|
cmsWidgetName="FunnelFooterWidget"
|
||||||
|
|
@ -108,7 +106,6 @@ import baseMixin from "@/mixins/base-mixin.js";
|
||||||
|
|
||||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
|
||||||
import {
|
import {
|
||||||
getPricedMobileFeePart,
|
getPricedMobileFeePart,
|
||||||
getServiceabilityDetails,
|
getServiceabilityDetails,
|
||||||
|
|
@ -396,8 +393,8 @@ export default {
|
||||||
streetAddress: this.selectedProvider?.address?.streetAddress,
|
streetAddress: this.selectedProvider?.address?.streetAddress,
|
||||||
city: this.selectedProvider?.address?.city,
|
city: this.selectedProvider?.address?.city,
|
||||||
state: this.selectedProvider?.address?.state,
|
state: this.selectedProvider?.address?.state,
|
||||||
zip: this.selectedProvider?.address?.zipCode,
|
zipCode: this.selectedProvider?.address?.zipCode,
|
||||||
zipCtu: this.selectedProvider?.address?.zipCodeCtu,
|
zipCodeCtu: this.selectedProvider?.address?.zipCodeCtu,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
import { mount, shallowMount } from "@vue/test-utils";
|
import { mount, shallowMount } from "@vue/test-utils";
|
||||||
import serviceZipModalQuestion from "./service-zip-modal-question";
|
import serviceZipModalQuestion from "./service-zip-modal-question";
|
||||||
import crypto from "crypto";
|
|
||||||
global.crypto = crypto;
|
|
||||||
|
|
||||||
jest.mock("@/digital-components/textbox-question/textbox-question", () => ({
|
jest.mock("@/digital-components/textbox-question/textbox-question", () => ({
|
||||||
getCmsContent: jest.fn((widgetName, cmsFieldName) => {
|
getCmsContent: jest.fn((widgetName, cmsFieldName) => {
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,7 @@
|
||||||
linkType="text"
|
linkType="text"
|
||||||
:text="serviceZipLinkText"
|
:text="serviceZipLinkText"
|
||||||
href="#!"
|
href="#!"
|
||||||
@click-event="openModal"
|
@click-event="openModal" />
|
||||||
aria-label="Modal window" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<modal
|
<modal
|
||||||
|
|
@ -22,12 +21,12 @@
|
||||||
customInputId="serviceZipCode"
|
customInputId="serviceZipCode"
|
||||||
v-model="internalModel.zipCode"
|
v-model="internalModel.zipCode"
|
||||||
v-on="{ 'textboxQuestionEvent.inputIdAssigned': onInputIdAssigned }"
|
v-on="{ 'textboxQuestionEvent.inputIdAssigned': onInputIdAssigned }"
|
||||||
:cmsWidgetName="textboxQuestionWidgetName" />
|
cmsWidgetName="ServiceZipQuestionWidget" />
|
||||||
<alert
|
<alert
|
||||||
ref="alertInvalidZip"
|
ref="alertInvalidZip"
|
||||||
v-if="displayInvalidZipAlert"
|
v-if="displayInvalidZipAlert"
|
||||||
class="my-4"
|
class="my-4"
|
||||||
:cmsWidgetName="alertInvalidZipWidgetName"
|
cmsWidgetName="AlertInvalidZipWidget"
|
||||||
alertClass="alert-danger"
|
alertClass="alert-danger"
|
||||||
v-bind:isDismissible="false" />
|
v-bind:isDismissible="false" />
|
||||||
</modal>
|
</modal>
|
||||||
|
|
@ -73,15 +72,6 @@ export default {
|
||||||
type: Function,
|
type: Function,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
setup() {
|
|
||||||
const textboxQuestionWidgetName = "ServiceZipQuestionWidget";
|
|
||||||
const alertInvalidZipWidgetName = "AlertInvalidZipWidget";
|
|
||||||
|
|
||||||
return {
|
|
||||||
textboxQuestionWidgetName,
|
|
||||||
alertInvalidZipWidgetName,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
serviceZipLinkText() {
|
serviceZipLinkText() {
|
||||||
if (this.modelValue.zipCode && this.modelValue.zipCode.length > 0) {
|
if (this.modelValue.zipCode && this.modelValue.zipCode.length > 0) {
|
||||||
|
|
@ -90,7 +80,7 @@ export default {
|
||||||
return this.getCmsContent(this.linkWidgetName, "BodyText");
|
return this.getCmsContent(this.linkWidgetName, "BodyText");
|
||||||
},
|
},
|
||||||
modalHeaderText() {
|
modalHeaderText() {
|
||||||
return this.getCmsContent(this.textboxQuestionWidgetName, "QuestionText");
|
return this.getCmsContent("ServiceZipQuestionWidget", "QuestionText");
|
||||||
},
|
},
|
||||||
modalFooterText() {
|
modalFooterText() {
|
||||||
return this.getCmsContent(this.modalWidgetName, "FooterText");
|
return this.getCmsContent(this.modalWidgetName, "FooterText");
|
||||||
|
|
@ -98,6 +88,9 @@ export default {
|
||||||
modalName() {
|
modalName() {
|
||||||
return this.modalWidgetName;
|
return this.modalWidgetName;
|
||||||
},
|
},
|
||||||
|
modal() {
|
||||||
|
return this.$refs[this.modalName];
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
resetAlerts() {
|
resetAlerts() {
|
||||||
|
|
@ -117,13 +110,13 @@ export default {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
openModal() {
|
openModal() {
|
||||||
this.$refs[this.modalName].openModal();
|
this.modal.openModal();
|
||||||
},
|
},
|
||||||
closeModal() {
|
closeModal() {
|
||||||
this.$refs[this.modalName].closeModal();
|
this.modal.closeModal();
|
||||||
},
|
},
|
||||||
resetModalButtonStyle() {
|
resetModalButtonStyle() {
|
||||||
this.$refs[this.modalName].resetButtonStyle();
|
this.modal.resetButtonStyle();
|
||||||
},
|
},
|
||||||
onInputIdAssigned(inputId) {
|
onInputIdAssigned(inputId) {
|
||||||
this.serviceZipCodeTextInputId = inputId;
|
this.serviceZipCodeTextInputId = inputId;
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
<textboxQuestion
|
<textboxQuestion
|
||||||
cmsWidgetName="VinNumberQuestionWidget"
|
cmsWidgetName="VinNumberQuestionWidget"
|
||||||
v-model="vin"
|
v-model="vin"
|
||||||
inputId="vin"
|
customInputId="vin"
|
||||||
isRequired
|
isRequired
|
||||||
validationRules="vin-required|vin-format"
|
validationRules="vin-required|vin-format"
|
||||||
:isDisabled="vinPopulatedOnPageLoad"
|
:isDisabled="vinPopulatedOnPageLoad"
|
||||||
|
|
@ -46,7 +46,7 @@
|
||||||
<textboxQuestion
|
<textboxQuestion
|
||||||
cmsWidgetName="ServiceZipQuestionWidget"
|
cmsWidgetName="ServiceZipQuestionWidget"
|
||||||
v-model="serviceZipCode"
|
v-model="serviceZipCode"
|
||||||
inputId="serviceZipCode"
|
customInputId="serviceZipCode"
|
||||||
mask="#####"
|
mask="#####"
|
||||||
isRequired
|
isRequired
|
||||||
validationRules="zip-required|zip-format" />
|
validationRules="zip-required|zip-format" />
|
||||||
|
|
@ -57,9 +57,9 @@
|
||||||
<textboxQuestion
|
<textboxQuestion
|
||||||
cmsWidgetName="EmailAddressQuestionWidget"
|
cmsWidgetName="EmailAddressQuestionWidget"
|
||||||
v-model="emailAddress"
|
v-model="emailAddress"
|
||||||
inputId="emailAddress"
|
customInputId="emailAddress"
|
||||||
isRequired
|
isRequired
|
||||||
validationRules="email-address-required|email-address-format" />
|
validationRules="email-address-format" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-0">
|
<div class="row mb-0">
|
||||||
|
|
@ -152,7 +152,6 @@ import vinPagesMixin from "@/mixins/vin-pages-mixin";
|
||||||
// DEFINE VALIDATION RULES
|
// DEFINE VALIDATION RULES
|
||||||
defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
|
defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
|
||||||
defineRule("zip-format", regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, errorMessages.SERVICE_ZIP_FORMAT));
|
defineRule("zip-format", regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, errorMessages.SERVICE_ZIP_FORMAT));
|
||||||
defineRule("email-address-required", required(errorMessages.EMAIL_ADDRESS_REQUIRED));
|
|
||||||
defineRule(
|
defineRule(
|
||||||
"email-address-format",
|
"email-address-format",
|
||||||
regex(
|
regex(
|
||||||
|
|
@ -325,8 +324,8 @@ export default {
|
||||||
await this.dispatchStoreAction(
|
await this.dispatchStoreAction(
|
||||||
storeActions.SAVE_SERVICE_ZIP_CODE_INFO,
|
storeActions.SAVE_SERVICE_ZIP_CODE_INFO,
|
||||||
{
|
{
|
||||||
zipCode: this.serviceZipCode,
|
|
||||||
state: resultMap.zipCodeData.state,
|
state: resultMap.zipCodeData.state,
|
||||||
|
zipCode: this.serviceZipCode,
|
||||||
zipCodeCtu: resultMap.zipCodeData.zipCodeCtu,
|
zipCodeCtu: resultMap.zipCodeData.zipCodeCtu,
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
|
|
@ -352,8 +351,8 @@ export default {
|
||||||
{
|
{
|
||||||
address: vehicleRegistrationInfo.address,
|
address: vehicleRegistrationInfo.address,
|
||||||
city: vehicleRegistrationInfo.city,
|
city: vehicleRegistrationInfo.city,
|
||||||
zipCode: vehicleRegistrationInfo.zipCode,
|
|
||||||
state: vehicleRegistrationInfo.state,
|
state: vehicleRegistrationInfo.state,
|
||||||
|
zipCode: vehicleRegistrationInfo.zipCode,
|
||||||
zipCodeCtu: zipCodeData.zipCodeCtu,
|
zipCodeCtu: zipCodeData.zipCodeCtu,
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
|
|
@ -362,8 +361,8 @@ export default {
|
||||||
await this.dispatchStoreAction(
|
await this.dispatchStoreAction(
|
||||||
storeActions.SAVE_SERVICE_ZIP_CODE_INFO,
|
storeActions.SAVE_SERVICE_ZIP_CODE_INFO,
|
||||||
{
|
{
|
||||||
zipCode: this.serviceZipCode,
|
|
||||||
state: zipCodeData.state,
|
state: zipCodeData.state,
|
||||||
|
zipCode: this.serviceZipCode,
|
||||||
zipCodeCtu: zipCodeData.zipCodeCtu,
|
zipCodeCtu: zipCodeData.zipCodeCtu,
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,10 @@ import { applicationConfig } from "../constants/application-config";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
methods: {
|
methods: {
|
||||||
|
getPageName() {
|
||||||
|
return getPageNameByQueryString();
|
||||||
|
},
|
||||||
|
|
||||||
logPageView(pageEvent) {
|
logPageView(pageEvent) {
|
||||||
const currentPageName = getPageNameByQueryString();
|
const currentPageName = getPageNameByQueryString();
|
||||||
var payload = {
|
var payload = {
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,14 @@ const router = createRouter({
|
||||||
|
|
||||||
//---------------------------------------------------------- Router Functions ----------------------------------------------------------
|
//---------------------------------------------------------- Router Functions ----------------------------------------------------------
|
||||||
|
|
||||||
|
router.beforeEach(async (to, from, next) => {
|
||||||
|
// set lastNavigationPage here to capture state before API calls for analytics.
|
||||||
|
// use current page url query string name when to.name is "root" (due to unresolved navigation in beforeEach)
|
||||||
|
router.lastNavigationPage = to.name == "root" ? analyticsMixin.methods.getPageName() : to.name;
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
router.afterEach(async (to, from) => {
|
router.afterEach(async (to, from) => {
|
||||||
// Update lastPageVisited in the store
|
// Update lastPageVisited in the store
|
||||||
store.commit(storeMutations.UPDATE_LAST_PAGE_VISITED, to.name);
|
store.commit(storeMutations.UPDATE_LAST_PAGE_VISITED, to.name);
|
||||||
|
|
@ -228,6 +236,12 @@ router.overrideNavigation = (
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
router.getNextPage = () => nextPageName;
|
||||||
|
|
||||||
|
// PRIVATE VARIABLES
|
||||||
|
|
||||||
|
var nextPageName;
|
||||||
|
|
||||||
// PRIVATE FUNCTIONS
|
// PRIVATE FUNCTIONS
|
||||||
|
|
||||||
// Navigate to the next route, depending on the scenario.
|
// Navigate to the next route, depending on the scenario.
|
||||||
|
|
@ -251,6 +265,8 @@ async function navigate(
|
||||||
if (destinationFmgPageValue !== undefined) {
|
if (destinationFmgPageValue !== undefined) {
|
||||||
// We're always pushing the same path, just changing query strings. Make sure our optional query strings get combined with our fmgPage one.
|
// We're always pushing the same path, just changing query strings. Make sure our optional query strings get combined with our fmgPage one.
|
||||||
|
|
||||||
|
nextPageName = destinationFmgPageValue;
|
||||||
|
|
||||||
// Update page data to the store for next page if provided. Otherwise, keep existing page data or set to empty object
|
// Update page data to the store for next page if provided. Otherwise, keep existing page data or set to empty object
|
||||||
const existingPageDataForPage = store.getters.pageData(destinationFmgPageValue);
|
const existingPageDataForPage = store.getters.pageData(destinationFmgPageValue);
|
||||||
baseMixin.methods.savePageDataToStore(
|
baseMixin.methods.savePageDataToStore(
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import { storeActions } from "@/constants/store-actions";
|
||||||
import { applicationConfig } from "@/constants/application-config";
|
import { applicationConfig } from "@/constants/application-config";
|
||||||
import { experimentTriggers } from "@/constants/experiments";
|
import { experimentTriggers } from "@/constants/experiments";
|
||||||
import { damageLocationsSelected } from "@/constants/damage-locations-selected";
|
import { damageLocationsSelected } from "@/constants/damage-locations-selected";
|
||||||
|
import { singleWindshieldCarIds } from "@/constants/single-windshield-carids";
|
||||||
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||||
import { deleteFunnelCookie } from "@/helpers/heritage-integration/cookie-helper.js";
|
import { deleteFunnelCookie } from "@/helpers/heritage-integration/cookie-helper.js";
|
||||||
import { deepEqual } from "@/helpers/object-helper";
|
import { deepEqual } from "@/helpers/object-helper";
|
||||||
|
|
@ -53,7 +54,8 @@ const getDefaultState = () => {
|
||||||
streetAddress: null,
|
streetAddress: null,
|
||||||
city: null,
|
city: null,
|
||||||
state: null,
|
state: null,
|
||||||
zip: null,
|
zipCode: null,
|
||||||
|
zipCodeCtu: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -243,6 +245,11 @@ export const mutations = {
|
||||||
state.order.vehicle.registration.firstName = registrationInfo?.firstName;
|
state.order.vehicle.registration.firstName = registrationInfo?.firstName;
|
||||||
state.order.vehicle.registration.lastName = registrationInfo?.lastName;
|
state.order.vehicle.registration.lastName = registrationInfo?.lastName;
|
||||||
},
|
},
|
||||||
|
updateServiceZip(state, serviceZipInfo) {
|
||||||
|
state.order.serviceLocation.state = serviceZipInfo.state;
|
||||||
|
state.order.serviceLocation.zipCode = serviceZipInfo.zipCode;
|
||||||
|
state.order.serviceLocation.zipCodeCtu = serviceZipInfo.zipCodeCtu;
|
||||||
|
},
|
||||||
updateServiceLocation(state, serviceLocationInfo) {
|
updateServiceLocation(state, serviceLocationInfo) {
|
||||||
state.order.serviceLocation.address = serviceLocationInfo.address;
|
state.order.serviceLocation.address = serviceLocationInfo.address;
|
||||||
state.order.serviceLocation.address2 = serviceLocationInfo.address2;
|
state.order.serviceLocation.address2 = serviceLocationInfo.address2;
|
||||||
|
|
@ -253,9 +260,16 @@ export const mutations = {
|
||||||
state.order.serviceLocation.appointmentType = serviceLocationInfo.appointmentType;
|
state.order.serviceLocation.appointmentType = serviceLocationInfo.appointmentType;
|
||||||
state.order.serviceLocation.isVehicleProtected = serviceLocationInfo.isVehicleProtected;
|
state.order.serviceLocation.isVehicleProtected = serviceLocationInfo.isVehicleProtected;
|
||||||
|
|
||||||
if (serviceLocationInfo.provider) {
|
state.order.serviceLocation.provider = {
|
||||||
state.order.serviceLocation.provider = serviceLocationInfo.provider;
|
providerNumber: serviceLocationInfo.provider?.providerNumber,
|
||||||
}
|
address: {
|
||||||
|
streetAddress: serviceLocationInfo.provider?.address?.streetAddress,
|
||||||
|
city: serviceLocationInfo.provider?.address?.city,
|
||||||
|
state: serviceLocationInfo.provider?.address?.state,
|
||||||
|
zipCode: serviceLocationInfo.provider?.address?.zipCode,
|
||||||
|
zipCodeCtu: serviceLocationInfo.provider?.address?.zipCodeCtu,
|
||||||
|
},
|
||||||
|
};
|
||||||
},
|
},
|
||||||
updateSchedule(state, scheduleInfo) {
|
updateSchedule(state, scheduleInfo) {
|
||||||
if (scheduleInfo) {
|
if (scheduleInfo) {
|
||||||
|
|
@ -351,14 +365,14 @@ export const mutations = {
|
||||||
state.order.schedule.routeCode = null;
|
state.order.schedule.routeCode = null;
|
||||||
state.order.schedule.jobMaxMinutes = null;
|
state.order.schedule.jobMaxMinutes = null;
|
||||||
|
|
||||||
//early bird fee used on schedule page also needs reset when schedule is reset
|
//premium appointment fee used on schedule page also needs reset when schedule is reset
|
||||||
const supportingItems = state.order.lineItems.supportingItems;
|
const supportingItems = state.order.lineItems.supportingItems;
|
||||||
const removeEarlyBirdIndex = supportingItems?.findIndex(
|
const premiumAppointmentFeeIndex = supportingItems?.findIndex(
|
||||||
(item) => item.partType == PREMIUM_FEE_PART_TYPE
|
(item) => item.partType == PREMIUM_FEE_PART_TYPE
|
||||||
);
|
);
|
||||||
|
|
||||||
if (removeEarlyBirdIndex >= 0) {
|
if (premiumAppointmentFeeIndex >= 0) {
|
||||||
supportingItems.splice(removeEarlyBirdIndex, 1);
|
supportingItems.splice(premiumAppointmentFeeIndex, 1);
|
||||||
state.order.lineItems.supportingItems = supportingItems;
|
state.order.lineItems.supportingItems = supportingItems;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -372,14 +386,22 @@ export const mutations = {
|
||||||
state.order.serviceLocation.appointmentType = null;
|
state.order.serviceLocation.appointmentType = null;
|
||||||
},
|
},
|
||||||
resetServiceLocationProvider(state) {
|
resetServiceLocationProvider(state) {
|
||||||
state.order.serviceLocation.provider = null;
|
state.order.serviceLocation.provider = {
|
||||||
|
providerNumber: null,
|
||||||
|
address: {
|
||||||
|
streetAddress: null,
|
||||||
|
city: null,
|
||||||
|
state: null,
|
||||||
|
zipCode: null,
|
||||||
|
zipCodeCtu: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
},
|
},
|
||||||
resetServiceLocationMobileAddress(state) {
|
resetServiceLocationMobileAddress(state) {
|
||||||
state.order.serviceLocation.address = null;
|
state.order.serviceLocation.address = null;
|
||||||
state.order.serviceLocation.address2 = null;
|
state.order.serviceLocation.address2 = null;
|
||||||
state.order.serviceLocation.city = null;
|
state.order.serviceLocation.city = null;
|
||||||
state.order.serviceLocation.state = null;
|
state.order.serviceLocation.state = null;
|
||||||
state.order.serviceLocation.zipCode = null;
|
|
||||||
state.order.serviceLocation.isVehicleProtected = null;
|
state.order.serviceLocation.isVehicleProtected = null;
|
||||||
},
|
},
|
||||||
// Misc Mutations
|
// Misc Mutations
|
||||||
|
|
@ -460,8 +482,10 @@ export const mutations = {
|
||||||
sessionInformation.order.serviceLocation.provider?.address?.city;
|
sessionInformation.order.serviceLocation.provider?.address?.city;
|
||||||
state.order.serviceLocation.provider.address.state =
|
state.order.serviceLocation.provider.address.state =
|
||||||
sessionInformation.order.serviceLocation.provider?.address?.state;
|
sessionInformation.order.serviceLocation.provider?.address?.state;
|
||||||
state.order.serviceLocation.provider.address.zip =
|
state.order.serviceLocation.provider.address.zipCode =
|
||||||
sessionInformation.order.serviceLocation.provider?.address?.zip;
|
sessionInformation.order.serviceLocation.provider?.address?.zipCode;
|
||||||
|
state.order.serviceLocation.provider.address.zipCodeCtu =
|
||||||
|
sessionInformation.order.serviceLocation.provider?.address?.zipCodeCtu;
|
||||||
|
|
||||||
state.order.payment.isInsurance = sessionInformation.order.payment.isInsurance;
|
state.order.payment.isInsurance = sessionInformation.order.payment.isInsurance;
|
||||||
state.order.payment.insuranceCoverage.isVerified =
|
state.order.payment.insuranceCoverage.isVerified =
|
||||||
|
|
@ -639,13 +663,28 @@ export const actions = {
|
||||||
},
|
},
|
||||||
|
|
||||||
lookupVinByImage(context, image) {
|
lookupVinByImage(context, image) {
|
||||||
const data = new FormData();
|
return new Promise((resolve, reject) => {
|
||||||
data.append("vinImage", image);
|
let reader = new FileReader();
|
||||||
return globalMethods.callHttpClient({
|
reader.onload = (e) => {
|
||||||
method: endpoints.LookupVinByImage.method,
|
resolve(reader.result);
|
||||||
endpoint: endpoints.LookupVinByImage.url,
|
};
|
||||||
payload: data,
|
reader.readAsDataURL(image);
|
||||||
isFormData: true,
|
}).then((result) => {
|
||||||
|
const components = result.split(",");
|
||||||
|
const contentType = image.type;
|
||||||
|
const imageBase64 = components[1];
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
imageData: imageBase64,
|
||||||
|
contentType: contentType,
|
||||||
|
fileName: image.name,
|
||||||
|
};
|
||||||
|
|
||||||
|
return globalMethods.callHttpClient({
|
||||||
|
method: endpoints.LookupVinByImage.method,
|
||||||
|
endpoint: endpoints.LookupVinByImage.url,
|
||||||
|
payload: data,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -722,20 +761,30 @@ export const actions = {
|
||||||
// Dependency Actions
|
// Dependency Actions
|
||||||
resetDamageAndDependencies(context) {
|
resetDamageAndDependencies(context) {
|
||||||
context.commit(storeMutations.RESET_DAMAGE_STATE);
|
context.commit(storeMutations.RESET_DAMAGE_STATE);
|
||||||
context.commit(storeMutations.RESET_GLASS_PARTS_STATE);
|
|
||||||
context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null);
|
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||||
|
|
||||||
context.commit(storeMutations.UPDATE_VAPS, null);
|
context.commit(storeMutations.UPDATE_VAPS, null);
|
||||||
},
|
},
|
||||||
|
|
||||||
resetRegistrationAndDependencies(context) {
|
resetRegistrationAndDependencies(context) {
|
||||||
context.commit(storeMutations.RESET_REGISTRATION_STATE);
|
context.commit(storeMutations.RESET_REGISTRATION_STATE);
|
||||||
context.commit(storeMutations.RESET_GLASS_PARTS_STATE);
|
|
||||||
context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null);
|
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||||
},
|
},
|
||||||
|
|
||||||
resetPartsAndDependencies(context) {
|
resetPartsAndDependencies(context) {
|
||||||
context.commit(storeMutations.RESET_GLASS_PARTS_STATE);
|
context.commit(storeMutations.RESET_GLASS_PARTS_STATE);
|
||||||
context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null);
|
context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null);
|
||||||
|
|
||||||
|
context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
|
||||||
|
},
|
||||||
|
|
||||||
|
resetServiceLocationAndDependencies(context) {
|
||||||
|
context.commit(storeMutations.RESET_SERVICE_LOCATION_APPOINTMENT_TYPE);
|
||||||
|
context.commit(storeMutations.RESET_SERVICE_LOCATION_PROVIDER);
|
||||||
|
|
||||||
|
context.commit(storeMutations.RESET_SCHEDULE);
|
||||||
},
|
},
|
||||||
|
|
||||||
resetState(context) {
|
resetState(context) {
|
||||||
|
|
@ -1333,7 +1382,7 @@ export const actions = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
customer: {
|
customer: {
|
||||||
emailAddress: order.customer.emailAddress,
|
emailAddress: order.customer.emailAddress || null,
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
numberOfChips: damage.numberOfChips,
|
numberOfChips: damage.numberOfChips,
|
||||||
|
|
@ -1372,8 +1421,8 @@ export const actions = {
|
||||||
order.serviceLocation.provider?.address?.streetAddress,
|
order.serviceLocation.provider?.address?.streetAddress,
|
||||||
city: order.serviceLocation.provider?.address?.city,
|
city: order.serviceLocation.provider?.address?.city,
|
||||||
state: order.serviceLocation.provider?.address?.state,
|
state: order.serviceLocation.provider?.address?.state,
|
||||||
zip: order.serviceLocation.provider?.address?.zip,
|
zipCode: order.serviceLocation.provider?.address?.zipCode,
|
||||||
zipCtu: order.serviceLocation.provider?.address?.zipCtu,
|
zipCodeCtu: order.serviceLocation.provider?.address?.zipCodeCtu,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -1392,6 +1441,8 @@ export const actions = {
|
||||||
eon: order.eon,
|
eon: order.eon,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
additionalSuccessEventDataHandler: (response) =>
|
||||||
|
"Email provided: " + (order.customer.emailAddress ? "true" : "false"),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -1607,7 +1658,6 @@ export const actions = {
|
||||||
|
|
||||||
if (!isSelectedGlassAvailableForVehicle) {
|
if (!isSelectedGlassAvailableForVehicle) {
|
||||||
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||||
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Save new values
|
//Save new values
|
||||||
|
|
@ -1633,7 +1683,6 @@ export const actions = {
|
||||||
|
|
||||||
if (!isSelectedGlassAvailableForVehicle) {
|
if (!isSelectedGlassAvailableForVehicle) {
|
||||||
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||||
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Save new values
|
//Save new values
|
||||||
|
|
@ -1659,6 +1708,8 @@ export const actions = {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (havePartQuestionAnswersChanged) {
|
if (havePartQuestionAnswersChanged) {
|
||||||
|
context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
|
||||||
|
|
||||||
context.commit(storeMutations.UPDATE_GLASS_PARTS, null);
|
context.commit(storeMutations.UPDATE_GLASS_PARTS, null);
|
||||||
context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null);
|
context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null);
|
||||||
context.commit(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, null);
|
context.commit(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, null);
|
||||||
|
|
@ -1706,6 +1757,8 @@ export const actions = {
|
||||||
previouslySelectedPartNumbers !== currentlySelectedPartNumbers;
|
previouslySelectedPartNumbers !== currentlySelectedPartNumbers;
|
||||||
|
|
||||||
if (haveSelectedVehiclePartsChanged) {
|
if (haveSelectedVehiclePartsChanged) {
|
||||||
|
context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
|
||||||
|
|
||||||
context.commit(storeMutations.UPDATE_GLASS_PARTS, null);
|
context.commit(storeMutations.UPDATE_GLASS_PARTS, null);
|
||||||
context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null);
|
context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null);
|
||||||
context.commit(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, null);
|
context.commit(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, null);
|
||||||
|
|
@ -1737,6 +1790,8 @@ export const actions = {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (haveMoldingQuestionAnswersChanged) {
|
if (haveMoldingQuestionAnswersChanged) {
|
||||||
|
context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
|
||||||
|
|
||||||
context.commit(storeMutations.UPDATE_GLASS_PARTS, null);
|
context.commit(storeMutations.UPDATE_GLASS_PARTS, null);
|
||||||
context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null);
|
context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null);
|
||||||
context.commit(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null);
|
context.commit(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null);
|
||||||
|
|
@ -1766,6 +1821,8 @@ export const actions = {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (haveCapabilityQuestionAnswersChanged) {
|
if (haveCapabilityQuestionAnswersChanged) {
|
||||||
|
context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
|
||||||
|
|
||||||
context.commit(storeMutations.UPDATE_GLASS_PARTS, null);
|
context.commit(storeMutations.UPDATE_GLASS_PARTS, null);
|
||||||
context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null);
|
context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null);
|
||||||
}
|
}
|
||||||
|
|
@ -1787,8 +1844,7 @@ export const actions = {
|
||||||
|
|
||||||
saveSupportingItems(context, supportingItems) {
|
saveSupportingItems(context, supportingItems) {
|
||||||
if (!deepEqual(supportingItems, context.state.order.lineItems.supportingItems)) {
|
if (!deepEqual(supportingItems, context.state.order.lineItems.supportingItems)) {
|
||||||
context.commit(storeMutations.RESET_SERVICE_LOCATION_APPOINTMENT_TYPE);
|
context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
|
||||||
context.commit(storeMutations.RESET_SERVICE_LOCATION_PROVIDER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, supportingItems);
|
context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, supportingItems);
|
||||||
|
|
@ -1868,15 +1924,29 @@ export const actions = {
|
||||||
context.state.order.serviceLocation &&
|
context.state.order.serviceLocation &&
|
||||||
serviceZipCodeInfo.zipCode !== context.state.order.serviceLocation.zipCode
|
serviceZipCodeInfo.zipCode !== context.state.order.serviceLocation.zipCode
|
||||||
) {
|
) {
|
||||||
context.commit(storeMutations.RESET_SERVICE_LOCATION_APPOINTMENT_TYPE);
|
context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
|
||||||
context.commit(storeMutations.RESET_SERVICE_LOCATION_PROVIDER);
|
|
||||||
context.commit(storeMutations.RESET_SERVICE_LOCATION_MOBILE_ADDRESS);
|
context.commit(storeMutations.RESET_SERVICE_LOCATION_MOBILE_ADDRESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.commit(storeMutations.UPDATE_SERVICE_LOCATION, serviceZipCodeInfo);
|
context.commit(storeMutations.UPDATE_SERVICE_ZIP, serviceZipCodeInfo);
|
||||||
},
|
},
|
||||||
|
|
||||||
saveServiceLocation(context, serviceLocationInfo) {
|
saveServiceLocation(context, serviceLocationInfo) {
|
||||||
|
if (context.state.order.serviceLocation) {
|
||||||
|
if (
|
||||||
|
serviceLocationInfo.zipCode !== context.state.order.serviceLocation.zipCode ||
|
||||||
|
!providersEqual(
|
||||||
|
serviceLocationInfo.provider,
|
||||||
|
context.state.order.serviceLocation.provider
|
||||||
|
) ||
|
||||||
|
serviceLocationInfo.appointmentType !==
|
||||||
|
context.state.order.serviceLocation.appointmentType
|
||||||
|
) {
|
||||||
|
context.commit(storeMutations.RESET_SCHEDULE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
context.commit(storeMutations.UPDATE_SERVICE_LOCATION, serviceLocationInfo);
|
context.commit(storeMutations.UPDATE_SERVICE_LOCATION, serviceLocationInfo);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -1889,7 +1959,6 @@ export const actions = {
|
||||||
if (vehicleInfo.vin !== context.state.order.vehicle.vin) {
|
if (vehicleInfo.vin !== context.state.order.vehicle.vin) {
|
||||||
if (!isSelectedGlassAvailableForVehicle) {
|
if (!isSelectedGlassAvailableForVehicle) {
|
||||||
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||||
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Save new values
|
//Save new values
|
||||||
|
|
@ -1899,8 +1968,7 @@ export const actions = {
|
||||||
|
|
||||||
saveGlassParts(context, parts) {
|
saveGlassParts(context, parts) {
|
||||||
if (!deepEqual(parts, context.state.order.lineItems.glassParts)) {
|
if (!deepEqual(parts, context.state.order.lineItems.glassParts)) {
|
||||||
context.commit(storeMutations.RESET_SERVICE_LOCATION_APPOINTMENT_TYPE);
|
context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
|
||||||
context.commit(storeMutations.RESET_SERVICE_LOCATION_PROVIDER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context.commit(storeMutations.UPDATE_GLASS_PARTS, parts);
|
context.commit(storeMutations.UPDATE_GLASS_PARTS, parts);
|
||||||
|
|
@ -1915,6 +1983,19 @@ export const actions = {
|
||||||
},
|
},
|
||||||
|
|
||||||
isVinOptionalVehicle(context) {
|
isVinOptionalVehicle(context) {
|
||||||
|
//Optional for carIds with only a single windshield
|
||||||
|
if (
|
||||||
|
singleWindshieldCarIds.find((item) => item === context.state.order.vehicle.carId) &&
|
||||||
|
context.state.order.damage.glassToReplace.length == 1 &&
|
||||||
|
context.state.order.damage.glassToReplace.find(
|
||||||
|
(glassToReplace) =>
|
||||||
|
glassToReplace.glassLocation.toLowerCase() ===
|
||||||
|
damageLocationsSelected.WINDSHIELD.toLowerCase()
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//Optional for specific YMMSs
|
||||||
switch (context.state.order.vehicle.make.toLowerCase()) {
|
switch (context.state.order.vehicle.make.toLowerCase()) {
|
||||||
case "mercedes benz":
|
case "mercedes benz":
|
||||||
case "volkswagen":
|
case "volkswagen":
|
||||||
|
|
@ -1923,14 +2004,12 @@ export const actions = {
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
context.state.order.vehicle.make.toLowerCase() === "ford" &&
|
context.state.order.vehicle.make.toLowerCase() === "ford" &&
|
||||||
context.state.order.vehicle.year >= 2018
|
context.state.order.vehicle.year >= 2018
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
context.state.order.vehicle.make.toLowerCase() === "bmw" &&
|
context.state.order.vehicle.make.toLowerCase() === "bmw" &&
|
||||||
context.state.order.vehicle.year <= 2017
|
context.state.order.vehicle.year <= 2017
|
||||||
|
|
@ -2087,6 +2166,17 @@ function convertGlassPieceToBackEndCompatibleFormat(glassPieces) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function providersEqual(providerA, providerB) {
|
||||||
|
return (
|
||||||
|
providerA.providerNumber === providerB.providerNumber &&
|
||||||
|
providerA.address?.city === providerB.address?.city &&
|
||||||
|
providerA.address?.state === providerB.address?.state &&
|
||||||
|
providerA.address?.streetAddress === providerB.address?.streetAddress &&
|
||||||
|
providerA.address?.zipCode === providerB.address?.zipCode
|
||||||
|
);
|
||||||
|
//TODO: Change back to deepEqual once zipCodeCtu is added to saveSession.
|
||||||
|
}
|
||||||
|
|
||||||
// This function will verify schedule info is still valid.
|
// This function will verify schedule info is still valid.
|
||||||
// check to see if we have an appointment date on the order object.
|
// check to see if we have an appointment date on the order object.
|
||||||
// if so, make sure it's not in the past. if in the past, clear schedule info in store.
|
// if so, make sure it's not in the past. if in the past, clear schedule info in store.
|
||||||
|
|
|
||||||
|
|
@ -406,14 +406,16 @@ describe("Actions", () => {
|
||||||
it("lookupVinByImage action, should return list of vins", async () => {
|
it("lookupVinByImage action, should return list of vins", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const context = state;
|
const context = state;
|
||||||
const dummyImage = {};
|
const image = new File([], "test.jpg", {
|
||||||
|
type: "image/jpeg",
|
||||||
|
});
|
||||||
|
|
||||||
globalMethods.callHttpClient.mockImplementation(() => {
|
globalMethods.callHttpClient.mockImplementation(() => {
|
||||||
return Promise.resolve({ data: ["1C6JJTAG3NL134044"] });
|
return Promise.resolve({ data: ["1C6JJTAG3NL134044"] });
|
||||||
});
|
});
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const response = await actions.lookupVinByImage(context, dummyImage);
|
const response = await actions.lookupVinByImage(context, image);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(response.data).toEqual(["1C6JJTAG3NL134044"]);
|
expect(response.data).toEqual(["1C6JJTAG3NL134044"]);
|
||||||
|
|
@ -422,7 +424,9 @@ describe("Actions", () => {
|
||||||
it("lookupVinByImage action, should reject if error in calling API", async () => {
|
it("lookupVinByImage action, should reject if error in calling API", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const context = state;
|
const context = state;
|
||||||
const dummyImage = {};
|
const image = new File([], "test.jpg", {
|
||||||
|
type: "image/jpeg",
|
||||||
|
});
|
||||||
|
|
||||||
globalMethods.callHttpClient.mockImplementation(() => {
|
globalMethods.callHttpClient.mockImplementation(() => {
|
||||||
return Promise.reject("An error occurred");
|
return Promise.reject("An error occurred");
|
||||||
|
|
@ -431,9 +435,7 @@ describe("Actions", () => {
|
||||||
// Act
|
// Act
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
await expect(actions.lookupVinByImage(context, dummyImage)).rejects.toEqual(
|
await expect(actions.lookupVinByImage(context, image)).rejects.toEqual("An error occurred");
|
||||||
"An error occurred"
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("getVehicleMakes action, should return makes list", async () => {
|
it("getVehicleMakes action, should return makes list", async () => {
|
||||||
|
|
@ -547,41 +549,48 @@ describe("Actions", () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const context = state;
|
const context = state;
|
||||||
const commit = jest.fn();
|
const commit = jest.fn();
|
||||||
|
const dispatch = jest.fn();
|
||||||
|
|
||||||
context.commit = commit;
|
context.commit = commit;
|
||||||
|
context.dispatch = dispatch;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await actions.resetDamageAndDependencies(context);
|
await actions.resetDamageAndDependencies(context);
|
||||||
|
|
||||||
expect(commit).toBeCalledWith(storeMutations.RESET_DAMAGE_STATE);
|
expect(commit).toBeCalledWith(storeMutations.RESET_DAMAGE_STATE);
|
||||||
expect(commit).toBeCalledWith(storeMutations.RESET_GLASS_PARTS_STATE);
|
expect(dispatch).toBeCalledWith(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("resetRegistrationAndDependencies action", async () => {
|
it("resetRegistrationAndDependencies action", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const context = state;
|
const context = state;
|
||||||
const commit = jest.fn();
|
const commit = jest.fn();
|
||||||
|
const dispatch = jest.fn();
|
||||||
|
|
||||||
context.commit = commit;
|
context.commit = commit;
|
||||||
|
context.dispatch = dispatch;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await actions.resetRegistrationAndDependencies(context);
|
await actions.resetRegistrationAndDependencies(context);
|
||||||
|
|
||||||
expect(commit).toBeCalledWith(storeMutations.RESET_REGISTRATION_STATE);
|
expect(commit).toBeCalledWith(storeMutations.RESET_REGISTRATION_STATE);
|
||||||
expect(commit).toBeCalledWith(storeMutations.RESET_GLASS_PARTS_STATE);
|
expect(dispatch).toBeCalledWith(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("resetPartsAndDependencies action", async () => {
|
it("resetPartsAndDependencies action", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const context = state;
|
const context = state;
|
||||||
const commit = jest.fn();
|
const commit = jest.fn();
|
||||||
|
const dispatch = jest.fn();
|
||||||
|
|
||||||
context.commit = commit;
|
context.commit = commit;
|
||||||
|
context.dispatch = dispatch;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await actions.resetPartsAndDependencies(context);
|
await actions.resetPartsAndDependencies(context);
|
||||||
|
|
||||||
expect(commit).toBeCalledWith(storeMutations.RESET_GLASS_PARTS_STATE);
|
expect(commit).toBeCalledWith(storeMutations.RESET_GLASS_PARTS_STATE);
|
||||||
|
expect(dispatch).toBeCalledWith(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("resetState action", async () => {
|
it("resetState action", async () => {
|
||||||
|
|
@ -933,6 +942,90 @@ describe("Actions", () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("saveServiceZipCodeInfo, should call mutation and save zip code to state", () => {
|
||||||
|
// Arrange
|
||||||
|
const context = state;
|
||||||
|
const commit = jest.fn();
|
||||||
|
context.commit = commit;
|
||||||
|
|
||||||
|
const serviceZipCodeInfo = {
|
||||||
|
zipCode: "43212",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
actions.saveServiceLocation(context, serviceZipCodeInfo);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(commit).toBeCalledWith(storeMutations.UPDATE_SERVICE_LOCATION, serviceZipCodeInfo);
|
||||||
|
expect(state.order.serviceLocation.zipCode).toEqual("43212");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("saveServiceZipCodeInfo, should reset if zip code is different", () => {
|
||||||
|
// Arrange
|
||||||
|
const context = {
|
||||||
|
state: {
|
||||||
|
order: {
|
||||||
|
serviceLocation: {
|
||||||
|
zipCode: "43212",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const commit = jest.fn();
|
||||||
|
const dispatch = jest.fn();
|
||||||
|
|
||||||
|
context.commit = commit;
|
||||||
|
context.dispatch = dispatch;
|
||||||
|
|
||||||
|
const serviceZipCodeInfo = {
|
||||||
|
zipCode: "43202",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
actions.saveServiceZipCodeInfo(context, serviceZipCodeInfo);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(dispatch).toHaveBeenCalledWith(
|
||||||
|
storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES
|
||||||
|
);
|
||||||
|
expect(commit).toHaveBeenCalledWith(storeMutations.RESET_SERVICE_LOCATION_MOBILE_ADDRESS);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("saveServiceZipCodeInfo, should not reset if zip code is the same", () => {
|
||||||
|
// Arrange
|
||||||
|
const context = {
|
||||||
|
state: {
|
||||||
|
order: {
|
||||||
|
serviceLocation: {
|
||||||
|
zipCode: "43212",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const commit = jest.fn();
|
||||||
|
const dispatch = jest.fn();
|
||||||
|
|
||||||
|
context.commit = commit;
|
||||||
|
context.dispatch = dispatch;
|
||||||
|
|
||||||
|
const serviceZipCodeInfo = {
|
||||||
|
zipCode: "43212",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
actions.saveServiceZipCodeInfo(context, serviceZipCodeInfo);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(dispatch).not.toHaveBeenCalledWith(
|
||||||
|
storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES
|
||||||
|
);
|
||||||
|
expect(commit).not.toHaveBeenCalledWith(
|
||||||
|
storeMutations.RESET_SERVICE_LOCATION_MOBILE_ADDRESS
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("saveServiceLocation, should call mutation and save service address to state", () => {
|
it("saveServiceLocation, should call mutation and save service address to state", () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const context = state;
|
const context = state;
|
||||||
|
|
@ -959,6 +1052,250 @@ describe("Actions", () => {
|
||||||
expect(state.order.serviceLocation.zipCodeCtu).toEqual("01820");
|
expect(state.order.serviceLocation.zipCodeCtu).toEqual("01820");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("saveServiceLocation, should reset if zipcode is different", () => {
|
||||||
|
// Arrange
|
||||||
|
const context = {
|
||||||
|
state: {
|
||||||
|
order: {
|
||||||
|
serviceLocation: {
|
||||||
|
address: "123 Test Lane",
|
||||||
|
city: "Columbus",
|
||||||
|
zipCode: "43212",
|
||||||
|
state: "OH",
|
||||||
|
zipCodeCtu: "01820",
|
||||||
|
appointmentType: "Mobile",
|
||||||
|
isVehicleProtected: true,
|
||||||
|
provider: {
|
||||||
|
providerNumber: "11111",
|
||||||
|
address: {
|
||||||
|
streetAddress: "123 Test Lane",
|
||||||
|
city: "Columbus",
|
||||||
|
state: "OH",
|
||||||
|
zipCode: "43212",
|
||||||
|
zipCodeCtu: "01820",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const commit = jest.fn();
|
||||||
|
const dispatch = jest.fn();
|
||||||
|
|
||||||
|
context.commit = commit;
|
||||||
|
context.dispatch = dispatch;
|
||||||
|
|
||||||
|
const serviceLocation = {
|
||||||
|
address: "123 Test Lane",
|
||||||
|
city: "Columbus",
|
||||||
|
zipCode: "43202",
|
||||||
|
state: "OH",
|
||||||
|
zipCodeCtu: "01820",
|
||||||
|
appointmentType: "Mobile",
|
||||||
|
isVehicleProtected: true,
|
||||||
|
provider: {
|
||||||
|
providerNumber: "11111",
|
||||||
|
address: {
|
||||||
|
streetAddress: "123 Test Lane",
|
||||||
|
city: "Columbus",
|
||||||
|
state: "OH",
|
||||||
|
zipCode: "43212",
|
||||||
|
zipCodeCtu: "01820",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
actions.saveServiceLocation(context, serviceLocation);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(commit).toHaveBeenCalledWith(storeMutations.RESET_SCHEDULE);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("saveServiceLocation, should reset if provider is different", () => {
|
||||||
|
// Arrange
|
||||||
|
const context = {
|
||||||
|
state: {
|
||||||
|
order: {
|
||||||
|
serviceLocation: {
|
||||||
|
address: "123 Test Lane",
|
||||||
|
city: "Columbus",
|
||||||
|
zipCode: "43212",
|
||||||
|
state: "OH",
|
||||||
|
zipCodeCtu: "01820",
|
||||||
|
appointmentType: "Mobile",
|
||||||
|
isVehicleProtected: true,
|
||||||
|
provider: {
|
||||||
|
providerNumber: "11111",
|
||||||
|
address: {
|
||||||
|
streetAddress: "123 Test Lane",
|
||||||
|
city: "Columbus",
|
||||||
|
state: "OH",
|
||||||
|
zipCode: "43212",
|
||||||
|
zipCodeCtu: "01820",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const commit = jest.fn();
|
||||||
|
const dispatch = jest.fn();
|
||||||
|
|
||||||
|
context.commit = commit;
|
||||||
|
context.dispatch = dispatch;
|
||||||
|
|
||||||
|
const serviceLocation = {
|
||||||
|
address: "123 Test Lane",
|
||||||
|
city: "Columbus",
|
||||||
|
zipCode: "43212",
|
||||||
|
state: "OH",
|
||||||
|
zipCodeCtu: "01820",
|
||||||
|
appointmentType: "Mobile",
|
||||||
|
isVehicleProtected: true,
|
||||||
|
provider: {
|
||||||
|
providerNumber: "22222",
|
||||||
|
address: {
|
||||||
|
streetAddress: "321 Test Lane",
|
||||||
|
city: "Columbus",
|
||||||
|
state: "OH",
|
||||||
|
zipCode: "43212",
|
||||||
|
zipCodeCtu: "01820",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
actions.saveServiceLocation(context, serviceLocation);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(commit).toHaveBeenCalledWith(storeMutations.RESET_SCHEDULE);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("saveServiceLocation, should reset if appointment type is different", () => {
|
||||||
|
// Arrange
|
||||||
|
const context = {
|
||||||
|
state: {
|
||||||
|
order: {
|
||||||
|
serviceLocation: {
|
||||||
|
address: "123 Test Lane",
|
||||||
|
city: "Columbus",
|
||||||
|
zipCode: "43212",
|
||||||
|
state: "OH",
|
||||||
|
zipCodeCtu: "01820",
|
||||||
|
appointmentType: "Mobile",
|
||||||
|
isVehicleProtected: true,
|
||||||
|
provider: {
|
||||||
|
providerNumber: "11111",
|
||||||
|
address: {
|
||||||
|
streetAddress: "123 Test Lane",
|
||||||
|
city: "Columbus",
|
||||||
|
state: "OH",
|
||||||
|
zipCode: "43212",
|
||||||
|
zipCodeCtu: "01820",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const commit = jest.fn();
|
||||||
|
const dispatch = jest.fn();
|
||||||
|
|
||||||
|
context.commit = commit;
|
||||||
|
context.dispatch = dispatch;
|
||||||
|
|
||||||
|
const serviceLocation = {
|
||||||
|
address: "123 Test Lane",
|
||||||
|
city: "Columbus",
|
||||||
|
zipCode: "43212",
|
||||||
|
state: "OH",
|
||||||
|
zipCodeCtu: "01820",
|
||||||
|
appointmentType: "Inshop",
|
||||||
|
isVehicleProtected: true,
|
||||||
|
provider: {
|
||||||
|
providerNumber: "11111",
|
||||||
|
address: {
|
||||||
|
streetAddress: "123 Test Lane",
|
||||||
|
city: "Columbus",
|
||||||
|
state: "OH",
|
||||||
|
zipCode: "43212",
|
||||||
|
zipCodeCtu: "01820",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
actions.saveServiceLocation(context, serviceLocation);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(commit).toHaveBeenCalledWith(storeMutations.RESET_SCHEDULE);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("saveServiceLocation, should not reset if parameters are the same", () => {
|
||||||
|
// Arrange
|
||||||
|
const context = {
|
||||||
|
state: {
|
||||||
|
order: {
|
||||||
|
serviceLocation: {
|
||||||
|
address: "123 Test Lane",
|
||||||
|
city: "Columbus",
|
||||||
|
zipCode: "43212",
|
||||||
|
state: "OH",
|
||||||
|
zipCodeCtu: "01820",
|
||||||
|
appointmentType: "Mobile",
|
||||||
|
isVehicleProtected: true,
|
||||||
|
provider: {
|
||||||
|
providerNumber: "11111",
|
||||||
|
address: {
|
||||||
|
streetAddress: "123 Test Lane",
|
||||||
|
city: "Columbus",
|
||||||
|
state: "OH",
|
||||||
|
zipCode: "43212",
|
||||||
|
zipCodeCtu: "01820",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const commit = jest.fn();
|
||||||
|
const dispatch = jest.fn();
|
||||||
|
|
||||||
|
context.commit = commit;
|
||||||
|
context.dispatch = dispatch;
|
||||||
|
|
||||||
|
const serviceLocation = {
|
||||||
|
address: "123 Test Lane",
|
||||||
|
city: "Columbus",
|
||||||
|
zipCode: "43212",
|
||||||
|
state: "OH",
|
||||||
|
zipCodeCtu: "01820",
|
||||||
|
appointmentType: "Mobile",
|
||||||
|
isVehicleProtected: true,
|
||||||
|
provider: {
|
||||||
|
providerNumber: "11111",
|
||||||
|
address: {
|
||||||
|
streetAddress: "123 Test Lane",
|
||||||
|
city: "Columbus",
|
||||||
|
state: "OH",
|
||||||
|
zipCode: "43212",
|
||||||
|
zipCodeCtu: "01820",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
actions.saveServiceLocation(context, serviceLocation);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(commit).not.toHaveBeenCalledWith(storeMutations.RESET_SCHEDULE);
|
||||||
|
});
|
||||||
|
|
||||||
it("saveGlassParts, should call mutation", () => {
|
it("saveGlassParts, should call mutation", () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const context = {
|
const context = {
|
||||||
|
|
@ -966,14 +1303,88 @@ describe("Actions", () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const commit = jest.fn();
|
const commit = jest.fn();
|
||||||
|
const dispatch = jest.fn();
|
||||||
|
|
||||||
context.commit = commit;
|
context.commit = commit;
|
||||||
|
context.dispatch = dispatch;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
actions.saveGlassParts(context, { glassParts: {} });
|
actions.saveGlassParts(context, { glassParts: {} });
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(commit).toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, { glassParts: {} });
|
expect(commit).toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, { glassParts: {} });
|
||||||
|
expect(dispatch).toBeCalledWith(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("saveSupportingItems, should call mutations", () => {
|
||||||
|
// Arrange
|
||||||
|
const context = {
|
||||||
|
state: state,
|
||||||
|
};
|
||||||
|
|
||||||
|
const commit = jest.fn();
|
||||||
|
const dispatch = jest.fn();
|
||||||
|
|
||||||
|
context.commit = commit;
|
||||||
|
context.dispatch = dispatch;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
actions.saveSupportingItems(context, []);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(commit).toBeCalledWith(storeMutations.UPDATE_SUPPORTING_ITEMS, []);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("saveSupportingItems, should call reset logic when value is new", () => {
|
||||||
|
// Arrange
|
||||||
|
const context = {
|
||||||
|
state: {
|
||||||
|
order: {
|
||||||
|
lineItems: {
|
||||||
|
supportingItems: ["TestValue1", "TestValue2"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const commit = jest.fn();
|
||||||
|
const dispatch = jest.fn();
|
||||||
|
|
||||||
|
context.commit = commit;
|
||||||
|
context.dispatch = dispatch;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
actions.saveSupportingItems(context, ["TestValue3", "TestValue4", "TestValue5"]);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(dispatch).toBeCalledWith(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("saveSupportingItems, should not call reset logic when value is the same", () => {
|
||||||
|
// Arrange
|
||||||
|
const context = {
|
||||||
|
state: {
|
||||||
|
order: {
|
||||||
|
lineItems: {
|
||||||
|
supportingItems: ["TestValue1", "TestValue2"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const commit = jest.fn();
|
||||||
|
const dispatch = jest.fn();
|
||||||
|
|
||||||
|
context.commit = commit;
|
||||||
|
context.dispatch = dispatch;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
actions.saveSupportingItems(context, ["TestValue1", "TestValue2"]);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(dispatch).not.toBeCalledWith(
|
||||||
|
storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("clearVin, should call mutation", () => {
|
it("clearVin, should call mutation", () => {
|
||||||
|
|
@ -2808,6 +3219,36 @@ describe("isVinOptionalVehicle", () => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var vinOptionalResult = actions.isVinOptionalVehicle(context);
|
||||||
|
expect(vinOptionalResult).toEqual(expectedVinSkip);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const testcarID = [
|
||||||
|
["CR00000100", "make", [{ glassLocation: "driver" }], false],
|
||||||
|
["CR00067899", "make2", [{ glassLocation: "windshield" }], true],
|
||||||
|
[
|
||||||
|
"CR00062396",
|
||||||
|
"make3",
|
||||||
|
[{ glassLocation: "windshield" }, { glassLocation: "driver" }],
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
["CR00066428", "make4", [{ glassLocation: "rear" }], false],
|
||||||
|
];
|
||||||
|
|
||||||
|
test.each(testcarID)(
|
||||||
|
"%s %s %o should skip vin lookup is %s",
|
||||||
|
async (carId, make, glassLocation, expectedVinSkip) => {
|
||||||
|
const context = state;
|
||||||
|
|
||||||
|
context.state = {
|
||||||
|
order: {
|
||||||
|
vehicle: { make: make, carId: carId },
|
||||||
|
damage: {
|
||||||
|
glassToReplace: glassLocation,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
var vinOptionalResult = actions.isVinOptionalVehicle(context);
|
var vinOptionalResult = actions.isVinOptionalVehicle(context);
|
||||||
expect(vinOptionalResult).toEqual(expectedVinSkip);
|
expect(vinOptionalResult).toEqual(expectedVinSkip);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,7 @@
|
||||||
@click-event="
|
@click-event="
|
||||||
$emit('textLinkClicked', getRouterLinkRouteFromCopy(copy))
|
$emit('textLinkClicked', getRouterLinkRouteFromCopy(copy))
|
||||||
"
|
"
|
||||||
:data-bs-target="'#' + getRouterLinkRouteFromCopy(copy)"
|
:data-bs-target="'#' + getRouterLinkRouteFromCopy(copy)" />
|
||||||
aria-label="Modal window" />
|
|
||||||
</span>
|
</span>
|
||||||
<span v-else v-html="copy"></span>
|
<span v-else v-html="copy"></span>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue