Merging from develop
This commit is contained in:
commit
6e1825aa0a
18 changed files with 1160 additions and 384 deletions
|
|
@ -15,7 +15,7 @@ module.exports = {
|
|||
"!src/layouts/vehicle-damage/windshield-options/windshield-options.vue",
|
||||
"!src/layouts/reveal/**/*.vue",
|
||||
"!src/ux-components/text-link/**/*.vue",
|
||||
"!src/common-components/date-picker/**/*.vue",
|
||||
"!src/common-components/date-picker/**/*.vue", // Temp until unit tests completed
|
||||
"!src/layouts/vin-lookup/**/*.vue", //Temporary for Quote page testing
|
||||
"!src/common-components/funnel-header/menu-modal/**/*.vue",
|
||||
// END
|
||||
|
|
|
|||
16
src/constants/scheduling.js
Normal file
16
src/constants/scheduling.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
const monthsOfYear = [
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December",
|
||||
];
|
||||
|
||||
export { monthsOfYear };
|
||||
437
src/digital-components/date-picker/date-picker.spec.js
Normal file
437
src/digital-components/date-picker/date-picker.spec.js
Normal file
|
|
@ -0,0 +1,437 @@
|
|||
import datePicker from "./date-picker";
|
||||
|
||||
// Supporting Files
|
||||
import { shallowMount } from "@vue/test-utils";
|
||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||
|
||||
describe("date-picker.vue", () => {
|
||||
describe("initial setup", () => {
|
||||
test("Creates a date object from today", () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
// Act
|
||||
const testResult = wrapper.vm.todayDate instanceof Date;
|
||||
|
||||
// Assert
|
||||
expect(testResult).toEqual(true);
|
||||
|
||||
wrapper.unmount();
|
||||
});
|
||||
});
|
||||
// describe("formatNumberToSetDigits", () => {
|
||||
// const testScenarios = [
|
||||
// [0, 3, "000"],
|
||||
// [8, 2, "08"],
|
||||
// [345, 2, "345"],
|
||||
// [4567, 0, "4567"],
|
||||
// ];
|
||||
// test.each(testScenarios)(
|
||||
// "when num is %s and digits is %s, formatNumberToSetDigits should return %s",
|
||||
// async (num, digits, expectedReturn) => {
|
||||
// // Arrange
|
||||
// const { wrapper } = setupMocks({});
|
||||
|
||||
// // Act
|
||||
// const fnReturn = wrapper.vm.formatNumberToSetDigits(num, digits);
|
||||
|
||||
// // Assert
|
||||
// expect(fnReturn).toEqual(expectedReturn);
|
||||
// }
|
||||
// );
|
||||
// });
|
||||
// describe("formatDate", () => {
|
||||
// test("Can format a date as MM/dd/yyyy", () => {
|
||||
// // Arrange
|
||||
// const { wrapper } = setupMocks({});
|
||||
// const testDate = new Date("01-26-2023");
|
||||
|
||||
// // Act
|
||||
// const testResult = wrapper.vm.formatDate(testDate);
|
||||
|
||||
// // Assert
|
||||
// expect(testResult).toEqual("01/26/2023");
|
||||
|
||||
// wrapper.unmount();
|
||||
// });
|
||||
|
||||
// // test("Can get correct initialFirstDate from today", () => {
|
||||
// // // Arrange
|
||||
// // const { wrapper } = setupMocks({
|
||||
// // propsData: {
|
||||
// // todayDate: "Thu Jan 26 2023",
|
||||
// // },
|
||||
// // });
|
||||
|
||||
// // // Act
|
||||
// // const testResult = wrapper.vm.initialFirstDate;
|
||||
// // const testResultAsDateString = testResult.toDateString();
|
||||
|
||||
// // // Assert
|
||||
// // expect(testResultAsDateString).toEqual("Sun Jan 22 2023");
|
||||
|
||||
// // wrapper.unmount();
|
||||
// // });
|
||||
// });
|
||||
// describe("calendarData", () => {
|
||||
// test("should return an array", async () => {
|
||||
// // Arrange
|
||||
// const { wrapper } = setupMocks({});
|
||||
|
||||
// // Act
|
||||
// const testResult = await wrapper.vm.calendarData;
|
||||
|
||||
// // Assert
|
||||
// expect(Array.isArray(testResult)).toBe(true);
|
||||
|
||||
// wrapper.unmount();
|
||||
// });
|
||||
// describe("each month should have keys monthLabel, yearNum, dates, firstDateDayIndex", () => {
|
||||
// const testScenarios = [
|
||||
// ["monthLabel", true, "string"],
|
||||
// ["yearNum", true, "number"],
|
||||
// ["dates", true, "object"],
|
||||
// ["firstDateDayIndex", true, "number"],
|
||||
// ["monthClass", true, "string"],
|
||||
// ];
|
||||
|
||||
// test.each(testScenarios)(
|
||||
// "test for %s key should return %s and be type %s",
|
||||
// async (label, test, type) => {
|
||||
// // Arrange
|
||||
// const { wrapper } = setupMocks({});
|
||||
// const containsMonthLabel = (obj) => Object.hasOwn(obj, label);
|
||||
// const isCorrectType = (obj) => typeof obj[label] === type;
|
||||
// const isTruthyOrZero = (obj) => (obj[label] || obj[label] === 0 ? true : false);
|
||||
|
||||
// // Act
|
||||
// const calendarData = await wrapper.vm.calendarData;
|
||||
// // console.log("calendarData: ", calendarData);
|
||||
|
||||
// // Assert
|
||||
// expect(calendarData.every(containsMonthLabel)).toBe(true);
|
||||
// expect(calendarData.every(isCorrectType)).toBe(true);
|
||||
// expect(calendarData.every(isTruthyOrZero)).toBe(true);
|
||||
|
||||
// wrapper.unmount();
|
||||
// }
|
||||
// );
|
||||
// });
|
||||
|
||||
// describe("the current month's week which includes today", () => {
|
||||
// const testScenarios = [
|
||||
// ["2022-02-05T03:00:00", 1, 2, true],
|
||||
// ["2024-02-05T03:00:00", 4, 0, true],
|
||||
// ["2020-01-15T03:00:00", 12, 0, true],
|
||||
// ];
|
||||
|
||||
// test.each(testScenarios)(
|
||||
// "the first date of the week including the date %s should be the date number %s and be day index %s",
|
||||
// async (todayDateString, startDate, dayOfWeekIndex) => {
|
||||
// // Arrange
|
||||
// const { wrapper } = setupMocks({
|
||||
// propsData: {
|
||||
// todayOverrideDateString: todayDateString,
|
||||
// },
|
||||
// });
|
||||
|
||||
// // Act
|
||||
// const calendarData = await wrapper.vm.calendarData;
|
||||
// const currentMonthIndex = calendarData.findIndex((month) => {
|
||||
// console.log("month: ", month);
|
||||
// return month.monthClass === "currentMonth";
|
||||
// });
|
||||
// console.log("calendarData ", calendarData);
|
||||
// console.log("currentMonthIndex ", currentMonthIndex);
|
||||
|
||||
// // Assert
|
||||
// expect(calendarData[currentMonthIndex].dates[0].dateNum === startDate).toBe(
|
||||
// true
|
||||
// );
|
||||
// expect(
|
||||
// calendarData[currentMonthIndex].dates[0].firstDateDayIndex ===
|
||||
// dayOfWeekIndex
|
||||
// ).toBe(true);
|
||||
|
||||
// wrapper.unmount();
|
||||
// }
|
||||
// );
|
||||
// });
|
||||
|
||||
// test("the current month's dates array should start with the first day of the week which includes today", async () => {
|
||||
// // Arrange
|
||||
// const { wrapper } = setupMocks({});
|
||||
// const hasAFirstInDates = (obj) => obj?.dates[0].dateNum === 1;
|
||||
|
||||
// // Act
|
||||
// const calendarData = await wrapper.vm.calendarData;
|
||||
// const currentMonthIndex = calendarData.findIndex((month) => {
|
||||
// return month.monthClass === "currentMonth";
|
||||
// });
|
||||
|
||||
// // Assert
|
||||
// expect(calendarData[currentMonthIndex].dates[0].dateNum === 1).toBe(true);
|
||||
|
||||
// wrapper.unmount();
|
||||
// });
|
||||
|
||||
// test("for each month (other than current month), the dates array should have the 1st of the month", async () => {
|
||||
// // Arrange
|
||||
// const { wrapper } = setupMocks({});
|
||||
// const hasAFirstInDates = (obj) => obj?.dates[0].dateNum === 1;
|
||||
|
||||
// // Act
|
||||
// const calendarData = await wrapper.vm.calendarData;
|
||||
|
||||
// // Assert
|
||||
// expect(calendarData.every(hasAFirstInDates)).toBe(true);
|
||||
|
||||
// wrapper.unmount();
|
||||
// });
|
||||
|
||||
// describe("for the current month in calendarData, the dates array should have correct days of month", () => {
|
||||
// const testScenarios = [
|
||||
// ["2022-02-05T03:00:00", 28, true],
|
||||
// ["2024-02-05T03:00:00", 29, true],
|
||||
// ["2020-01-15T03:00:00", 31, true],
|
||||
// ];
|
||||
|
||||
// test.each(testScenarios)(
|
||||
// "the current month including the date %s should have %s days in its dates array",
|
||||
// async (dateString, noOfDays) => {
|
||||
// // Arrange
|
||||
// const { wrapper } = setupMocks({
|
||||
// propsData: {
|
||||
// todayOverrideDateString: dateString,
|
||||
// },
|
||||
// });
|
||||
|
||||
// // Act
|
||||
// const calendarData = await wrapper.vm.calendarData;
|
||||
// const currentMonthIndex = calendarData.findIndex((month) => {
|
||||
// return month.monthClass === "currentMonth";
|
||||
// });
|
||||
|
||||
// // Assert
|
||||
// expect(calendarData[currentMonthIndex].dates.length === noOfDays).toBe(true);
|
||||
|
||||
// wrapper.unmount();
|
||||
// }
|
||||
// );
|
||||
|
||||
// // test.each(testScenarios)(
|
||||
// // "the date %s should have %s days in dates array",
|
||||
// // async (dateString, noOfDays) => {
|
||||
// // // Arrange
|
||||
// // const { wrapper } = setupMocks({
|
||||
// // propsData: {
|
||||
// // todayOverrideDateString: dateString,
|
||||
// // },
|
||||
// // });
|
||||
// // const hasCorrectNumberOfDates = (obj) => obj?.dates.length === noOfDays;
|
||||
|
||||
// // // Act
|
||||
// // const calendarData = await wrapper.vm.calendarData;
|
||||
|
||||
// // // Assert
|
||||
// // expect(calendarData.every(hasCorrectNumberOfDates)).toBe(true);
|
||||
|
||||
// // wrapper.unmount();
|
||||
// // }
|
||||
// // );
|
||||
// });
|
||||
|
||||
// // describe("for each object in array, the dates array should have correct days of month", () => {
|
||||
// // const testScenarios = [
|
||||
// // ["2022-02-05T03:00:00", 28, true],
|
||||
// // ["2024-02-05T03:00:00", 29, true],
|
||||
// // ["2020-01-15T03:00:00", 31, true],
|
||||
// // ];
|
||||
|
||||
// // test.each(testScenarios)(
|
||||
// // "the date %s should have %s days in dates array",
|
||||
// // async (dateString, noOfDays) => {
|
||||
// // // Arrange
|
||||
// // const { wrapper } = setupMocks({
|
||||
// // propsData: {
|
||||
// // todayOverrideDateString: dateString,
|
||||
// // },
|
||||
// // });
|
||||
// // const hasCorrectNumberOfDates = (obj) => obj?.dates.length === noOfDays;
|
||||
|
||||
// // // Act
|
||||
// // const calendarData = await wrapper.vm.calendarData;
|
||||
|
||||
// // // Assert
|
||||
// // expect(calendarData.every(hasCorrectNumberOfDates)).toBe(true);
|
||||
|
||||
// // wrapper.unmount();
|
||||
// // }
|
||||
// // );
|
||||
// // });
|
||||
|
||||
// // test.only("each object in array should have a monthLabel", () => {
|
||||
// // // Arrange
|
||||
// // const { wrapper } = setupMocks({});
|
||||
// // // const testTodayDate = new Date("01-26-2023");
|
||||
|
||||
// // // Act
|
||||
// // const testResult = wrapper.vm.calendarData;
|
||||
// // const containsMonthLabel = (month) => Object.hasOwn(month, 'monthLabel');
|
||||
|
||||
// // // Assert
|
||||
// // expect(testResult.every(containsMonthLabel)).toBe(true);
|
||||
|
||||
// // });
|
||||
|
||||
// // test.only("array should have a month object with same month as today", () => {
|
||||
// // // Arrange
|
||||
// // const { wrapper } = setupMocks({});
|
||||
// // // const testTodayDate = new Date("01-26-2023");
|
||||
|
||||
// // // Act
|
||||
// // // const testResult = wrapper.vm.calendarData;
|
||||
// // console.log("wrapper.vm.calendarData: ", wrapper.vm.calendarData)
|
||||
|
||||
// // // Assert
|
||||
// // // expect(wrapper.vm.calendarData).toEqual("something");
|
||||
// // // expect(wrapper.vm.calendarDataOld).toEqual("something");
|
||||
|
||||
// // expect(wrapper.vm.calendarData[1]).toMatchObject({
|
||||
// // "dates": [
|
||||
// // {
|
||||
// // "dateNum": 1
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 2
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 3
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 4
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 5
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 6
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 7
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 8
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 9
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 10
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 11
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 12
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 13
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 14
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 15
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 16
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 17
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 18
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 19
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 20
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 21
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 22
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 23
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 24
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 25
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 26
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 27
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 28
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 29
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 30
|
||||
// // },
|
||||
// // {
|
||||
// // "dateNum": 31
|
||||
// // }
|
||||
// // ],
|
||||
// // "monthLabel": "January",
|
||||
// // "yearNum": 2023
|
||||
// // });
|
||||
|
||||
// // wrapper.unmount();
|
||||
// // });
|
||||
// });
|
||||
});
|
||||
|
||||
function setupMocks(mountOptionsMockData = {}) {
|
||||
const initialMountOptionsMockData = {
|
||||
// router: {
|
||||
// navigate: jest.fn(),
|
||||
// navigateWithSaving: jest.fn(),
|
||||
// navigateWithoutSaving: jest.fn(),
|
||||
// },
|
||||
// actionList: [
|
||||
// // {
|
||||
// // actionName: storeActions.SAVE_PART_QUESTION_ANSWERS,
|
||||
// // data: {},
|
||||
// // },
|
||||
// // {
|
||||
// // actionName: storeActions.GET_PARTS,
|
||||
// // data: {},
|
||||
// // },
|
||||
// ],
|
||||
// store: {
|
||||
// // getters: store.getters,
|
||||
// // commit: store.commit,
|
||||
// },
|
||||
};
|
||||
|
||||
const mountOptions = getMountOptions(
|
||||
Object.assign(initialMountOptionsMockData, mountOptionsMockData)
|
||||
);
|
||||
|
||||
// console.log("mountOptions: ", mountOptions)
|
||||
|
||||
const wrapper = shallowMount(datePicker, mountOptions);
|
||||
|
||||
return { wrapper };
|
||||
}
|
||||
|
|
@ -1,224 +1,376 @@
|
|||
<template>
|
||||
<div id="app">
|
||||
<fieldset>
|
||||
<div class="date-picker" :class="calendarViewDirection">
|
||||
<fieldset v-if="months">
|
||||
<legend class="sr-only">Date Picker</legend>
|
||||
<div class="calendar-grid-container">
|
||||
<div class="month-year body-small d-flex align-items-center ps-3 small">
|
||||
{{ currentMonth }} {{ currentYear }}
|
||||
</div>
|
||||
<div class="legend caption d-flex align-items-center justify-content-end">
|
||||
<span class="legend-circle me-1"></span> = Available
|
||||
</div>
|
||||
<div class="nav-back ps-3"><button></button></div>
|
||||
<div class="nav-forward pe-3"><button></button></div>
|
||||
<!-- Do the days of the weeek need to be read? -->
|
||||
<div class="grid-item caption"><span class="sr-only">Sunday</span>S</div>
|
||||
<div class="grid-item caption"><span class="sr-only">Monday</span>M</div>
|
||||
<div class="grid-item caption"><span class="sr-only">Tuesday</span>T</div>
|
||||
<div class="grid-item caption"><span class="sr-only">Wednesday</span>W</div>
|
||||
<div class="grid-item caption"><span class="sr-only">Thursday</span>T</div>
|
||||
<div class="grid-item caption"><span class="sr-only">Friday</span>F</div>
|
||||
<div class="grid-item caption"><span class="sr-only">Saturday</span>S</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input disabled type="radio" name="day-of-month" id="sep-25" />
|
||||
<label class="past-day" for="sep-25"><span>25</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input disabled type="radio" name="day-of-month" id="sep-26" />
|
||||
<label class="past-day" for="sep-26"><span>26</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input disabled type="radio" name="day-of-month" id="sep-27" />
|
||||
<label class="past-day" for="sep-27"><span>27</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input disabled type="radio" name="day-of-month" id="sep-28" />
|
||||
<label class="past-day" for="sep-28"><span>28</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input disabled type="radio" name="day-of-month" id="sep-29" />
|
||||
<label class="past-day" for="sep-29"><span>29</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input disabled type="radio" name="day-of-month" id="sep-30" />
|
||||
<label class="past-day" for="sep-30"><span>30</span></label>
|
||||
</div>
|
||||
<!-- Use this as the base calendar item -->
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-1" />
|
||||
<label
|
||||
class="highlighted-day selectable-day"
|
||||
:class="[isCurrentDay ? 'current-day' : '']"
|
||||
for="oct-1"
|
||||
><span>1</span>
|
||||
<span class="sr-only">October 1st</span>
|
||||
<div v-if="isFirstDayOfMonth" class="current-month first-day">OCT</div>
|
||||
</label>
|
||||
</div>
|
||||
<!-- END -->
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-2" />
|
||||
<label class="selectable-day" for="oct-2"><span>2</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-3" />
|
||||
<label class="selectable-day" for="oct-3"><span>3</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-4" />
|
||||
<label class="selectable-day" for="oct-4"><span>4</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-5" />
|
||||
<label class="selectable-day" for="oct-5"><span>5</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-6" />
|
||||
<label class="selectable-day" for="oct-6"><span>6</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-7" />
|
||||
<label class="selectable-day" for="oct-7"><span>7</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-8" />
|
||||
<label class="selectable-day" for="oct-8"><span>8</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-9" />
|
||||
<label class="selectable-day" for="oct-9"><span>9</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-10" />
|
||||
<label class="selectable-day" for="oct-10"><span>10</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-11" />
|
||||
<label class="selectable-day" for="oct-11"><span>11</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-12" />
|
||||
<label class="selectable-day" for="oct-12"><span>12</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-13" />
|
||||
<label class="selectable-day" for="oct-13"><span>13</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-14" />
|
||||
<label class="selectable-day" for="oct-14"><span>14</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-15" />
|
||||
<label class="selectable-day" for="oct-15"><span>15</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-16" />
|
||||
<label class="selectable-day" for="oct-16"><span>16</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-17" />
|
||||
<label class="selectable-day" for="oct-17"><span>17</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-18" />
|
||||
<label class="selectable-day" for="oct-18"><span>18</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-19" />
|
||||
<label class="selectable-day" for="oct-19"><span>19</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-20" />
|
||||
<label class="selectable-day" for="oct-20"><span>20</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-21" />
|
||||
<label class="selectable-day" for="oct-21"><span>21</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-22" />
|
||||
<label class="selectable-day" for="oct-22"><span>22</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-23" />
|
||||
<label class="selectable-day" for="oct-23"><span>23</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-24" />
|
||||
<label class="selectable-day" for="oct-24"><span>24</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-25" />
|
||||
<label class="selectable-day" for="oct-25"><span>25</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-26" />
|
||||
<label class="selectable-day" for="oct-26"><span>26</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-27" />
|
||||
<label class="selectable-day" for="oct-27"><span>27</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-28" />
|
||||
<label class="selectable-day" for="oct-28"><span>28</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-29" />
|
||||
<label class="selectable-day" for="oct-29"><span>29</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="nov-30" />
|
||||
<label class="selectable-day" for="nov-30"><span>30</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-31" />
|
||||
<label class="selectable-day" for="oct-31"><span>31</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="nov-1" />
|
||||
<label class="future-day selectable-day" for="nov-1"
|
||||
><span>1</span>
|
||||
<div v-if="isFirstDayOfMonth" class="current-month first-day">OCT</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="nov-2" />
|
||||
<label class="future-day selectable-day" for="nov-2"><span>2</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="nov-3" />
|
||||
<label class="future-day selectable-day" for="nov-3"><span>3</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="nov-4" />
|
||||
<label class="future-day selectable-day" for="nov-4"><span>4</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="nov-5" />
|
||||
<label class="future-day selectable-day" for="nov-5"><span>5</span></label>
|
||||
<div
|
||||
v-for="month in months"
|
||||
:key="`${month.monthLabel}-${month.yearNum.toString()}-${months.length}`">
|
||||
<div
|
||||
class="calendar-grid-container"
|
||||
:class="[isInitialView === true ? 'initial-view' : '', month.monthClass]">
|
||||
<div class="month-year body-small d-flex align-items-center ps-3 small">
|
||||
{{ month.monthLabel }} {{ month.yearNum.toString() }}
|
||||
</div>
|
||||
<div
|
||||
v-if="calendarViewDirection === 'future'"
|
||||
class="legend caption d-flex align-items-center justify-content-end">
|
||||
<span class="legend-circle me-1"></span> = Available
|
||||
</div>
|
||||
<div class="nav-back ps-3"><button></button></div>
|
||||
<div class="nav-forward pe-3"><button></button></div>
|
||||
<!-- Do the days of the weeek need to be read? -->
|
||||
<div class="grid-item caption"><span class="sr-only">Sunday</span>S</div>
|
||||
<div class="grid-item caption"><span class="sr-only">Monday</span>M</div>
|
||||
<div class="grid-item caption"><span class="sr-only">Tuesday</span>T</div>
|
||||
<div class="grid-item caption"><span class="sr-only">Wednesday</span>W</div>
|
||||
<div class="grid-item caption"><span class="sr-only">Thursday</span>T</div>
|
||||
<div class="grid-item caption"><span class="sr-only">Friday</span>F</div>
|
||||
<div class="grid-item caption"><span class="sr-only">Saturday</span>S</div>
|
||||
<div
|
||||
v-for="date in month.dates"
|
||||
:key="`${month.monthLabel}-${date.dateNum.toString()}-${months.length}`"
|
||||
class="grid-item radio-wrapper"
|
||||
:class="[
|
||||
date.dateNum === 1 ? 'first-day-' + month.startDateDayIndex : '',
|
||||
date.dayClasses,
|
||||
]">
|
||||
<input
|
||||
:disabled="!isSelectableDate(date.inputValue)"
|
||||
type="radio"
|
||||
name="day-of-month"
|
||||
v-model="selectedDate"
|
||||
:value="date.inputValue"
|
||||
:id="`${month.monthLabel}-${date.dateNum.toString()}`" />
|
||||
<label :for="`${month.monthLabel}-${date.dateNum.toString()}`">
|
||||
<span>{{ date.dateNum.toString() }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<button
|
||||
v-if="calendarViewDirection === 'future'"
|
||||
type="button"
|
||||
class="btn-link"
|
||||
@click="goForward">
|
||||
View more dates
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// Supporting files
|
||||
import { monthsOfYear } from "@/constants/scheduling.js";
|
||||
|
||||
const SelectableDaysOptions = Object.freeze({
|
||||
CUSTOM: "custom",
|
||||
PAST: "past",
|
||||
});
|
||||
|
||||
export default {
|
||||
name: "datePicker",
|
||||
data() {
|
||||
return {
|
||||
currentMonth: "October",
|
||||
currentYear: "2022",
|
||||
isFirstDayOfMonth: true,
|
||||
isCurrentDay: true,
|
||||
calendarData: [],
|
||||
monthsBeforeToLoadOffset: 0,
|
||||
monthsAfterToLoadOffset: 1,
|
||||
selectableDatesData: [], // NOTE: uses monthNum (1-based), NOT monthIndex (0-based)
|
||||
isInitialView: true,
|
||||
initialViewRowsToShow: 5,
|
||||
initialViewRowsTally: 0,
|
||||
};
|
||||
},
|
||||
props: {
|
||||
selectableDates: {
|
||||
type: String,
|
||||
validator(value) {
|
||||
return Object.values(SelectableDaysOptions).includes(value);
|
||||
},
|
||||
default: SelectableDaysOptions.PAST,
|
||||
},
|
||||
modelValue: {
|
||||
type: Object,
|
||||
},
|
||||
todayOverrideDateString: {
|
||||
// only used for unit tests to override today's date
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
customSelectableDatesCallback: {
|
||||
type: Function,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
todayDate() {
|
||||
return this.todayOverrideDateString
|
||||
? new Date(this.todayOverrideDateString)
|
||||
: new Date();
|
||||
},
|
||||
months() {
|
||||
if (this.calendarData?.length < 1) {
|
||||
this.setCalendarData();
|
||||
}
|
||||
return this.calendarData;
|
||||
},
|
||||
calendarViewDirection() {
|
||||
if (this.selectableDates === "past") return "past";
|
||||
if (this.selectableDates === "custom") return "future";
|
||||
return "none";
|
||||
},
|
||||
selectedDate: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(newSelectedDate) {
|
||||
this.$emit("update:modelValue", newSelectedDate);
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
goForward() {
|
||||
if (this.isInitialView) {
|
||||
this.isInitialView = false; // 1st click removes hidden styling
|
||||
} else {
|
||||
this.addMonthData(); // 2nd click adds 1 month data
|
||||
}
|
||||
},
|
||||
setCalendarData() {
|
||||
// GENERATE MONTHS AND PUSH THEM INTO ARRAY
|
||||
const months = [];
|
||||
if (this.calendarViewDirection === "future") {
|
||||
// first 0, then 1
|
||||
for (let i = 0; i <= this.monthsAfterToLoadOffset; i++) {
|
||||
months.push(this.getMonthData(i));
|
||||
}
|
||||
} else if (this.calendarViewDirection === "past") {
|
||||
// first 0, then -1
|
||||
for (let i = 0; i >= 0 - this.monthsAfterToLoadOffset; i--) {
|
||||
months.unshift(this.getMonthData(i));
|
||||
}
|
||||
} else {
|
||||
// TK - IF A CALENDAR WITH BOTH PAST AND FUTURE WAS EVER NEEDED
|
||||
// for (let i = this.monthsAfterToLoadOffset; i >= this.monthsBeforeToLoadOffset; i--) {
|
||||
// months.push(this.getMonthDataPAST(i));
|
||||
// }
|
||||
}
|
||||
this.calendarData = months;
|
||||
},
|
||||
getMonthData(offset) {
|
||||
const direction = this.calendarViewDirection; // "past" or "future"
|
||||
let yearNum;
|
||||
let monthIndex;
|
||||
let monthClass = "";
|
||||
let initialViewEndDate; // only used for setCalendarData FUTURE
|
||||
let initialViewStartDate; // only used for setCalendarData PAST
|
||||
let currentWeekEndDateNum; // only used for setCalendarData PAST
|
||||
let currentWeekStartDateNum; // only used for setCalendarData FUTURE
|
||||
let firstMonthDayTally; // only used for setCalendarData on 1st month generated
|
||||
const datesArray = [];
|
||||
const todayDateNum = this.todayDate.getDate();
|
||||
|
||||
if (typeof offset !== "undefined") {
|
||||
// offset only passed during initial setup with setCalendarData
|
||||
yearNum = this.todayDate.getFullYear();
|
||||
monthIndex = this.todayDate.getMonth() + offset;
|
||||
|
||||
if (direction === "future" && offset > 0) {
|
||||
while (monthIndex > 11) {
|
||||
monthIndex = monthIndex - 12;
|
||||
yearNum++;
|
||||
}
|
||||
} else if (direction === "past" && offset < 0) {
|
||||
while (monthIndex < 0) {
|
||||
monthIndex = 12 + monthIndex;
|
||||
yearNum--;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// used only when adding an additional month
|
||||
|
||||
if (direction === "future") {
|
||||
// get last day of current calendar data
|
||||
const lastMonthInCalendarData = this.calendarData[this.calendarData.length - 1];
|
||||
|
||||
if (lastMonthInCalendarData.monthIndex === 11) {
|
||||
monthIndex = 0;
|
||||
yearNum = lastMonthInCalendarData.yearNum + 1;
|
||||
} else {
|
||||
monthIndex = lastMonthInCalendarData.monthIndex + 1;
|
||||
yearNum = lastMonthInCalendarData.yearNum;
|
||||
}
|
||||
}
|
||||
if (direction === "past") {
|
||||
// get last day of current calendar data
|
||||
const firstMonthInCalendarData = this.calendarData[0];
|
||||
|
||||
if (firstMonthInCalendarData.monthIndex === 0) {
|
||||
monthIndex = 11;
|
||||
yearNum = firstMonthInCalendarData.yearNum - 1;
|
||||
} else {
|
||||
monthIndex = firstMonthInCalendarData.monthIndex - 1;
|
||||
yearNum = firstMonthInCalendarData.yearNum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const todayDayIndex = this.todayDate.getDay(); // get day of week index of today (0-6)
|
||||
currentWeekStartDateNum =
|
||||
todayDayIndex > todayDateNum ? 1 : todayDateNum - todayDayIndex; // FUTURE
|
||||
const monthEndDate = new Date(yearNum, monthIndex + 1, 0); // BOTH
|
||||
let monthEndDateNum = monthEndDate.getDate(); // BOTH
|
||||
currentWeekEndDateNum = todayDateNum + 6 - todayDayIndex; // PAST, aka Sat. (ok if it's larger than the month end?)
|
||||
if (offset === 0 && direction === "past" && monthEndDateNum > currentWeekEndDateNum)
|
||||
monthEndDateNum = currentWeekEndDateNum; // PAST
|
||||
const monthStartDateNum =
|
||||
offset === 0 && direction === "future" ? currentWeekStartDateNum : 1; // FUTURE
|
||||
const monthStartDate = new Date(yearNum, monthIndex, monthStartDateNum); // BOTH
|
||||
const startDateDayIndex = monthStartDate.getDay(); // FUTURE
|
||||
const endDateDayIndex = monthEndDate.getDay(); // PAST
|
||||
|
||||
if (typeof offset !== "undefined") {
|
||||
if (offset === 0 && direction === "future")
|
||||
firstMonthDayTally = monthStartDateNum - startDateDayIndex; // FUTURE (starts low, counts up)
|
||||
if (offset === 0 && direction === "past")
|
||||
firstMonthDayTally = currentWeekEndDateNum; // PAST (starts high, counts down)
|
||||
|
||||
while (
|
||||
direction === "future" &&
|
||||
offset === 0 &&
|
||||
firstMonthDayTally < monthEndDateNum
|
||||
) {
|
||||
// FUTURE
|
||||
firstMonthDayTally = firstMonthDayTally + 7;
|
||||
this.initialViewRowsTally++;
|
||||
}
|
||||
|
||||
while (
|
||||
direction === "past" &&
|
||||
offset === 0 &&
|
||||
firstMonthDayTally > monthStartDateNum
|
||||
) {
|
||||
// PAST
|
||||
firstMonthDayTally = firstMonthDayTally - 7;
|
||||
this.initialViewRowsTally++;
|
||||
}
|
||||
|
||||
if (Math.abs(offset) === 1) {
|
||||
if (this.initialViewRowsTally < this.initialViewRowsToShow) {
|
||||
initialViewEndDate = 6 - startDateDayIndex + monthStartDateNum; // FUTURE
|
||||
initialViewStartDate = monthEndDateNum - endDateDayIndex; // PAST
|
||||
this.initialViewRowsTally++;
|
||||
} else {
|
||||
monthClass = monthClass + " month-hidden";
|
||||
}
|
||||
|
||||
while (this.initialViewRowsTally < this.initialViewRowsToShow) {
|
||||
initialViewEndDate = initialViewEndDate + 7;
|
||||
initialViewStartDate = initialViewStartDate - 7;
|
||||
this.initialViewRowsTally++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.selectableDates === "custom") {
|
||||
const monthStart = {
|
||||
year: yearNum,
|
||||
month: monthIndex + 1,
|
||||
date: offset === 0 ? todayDateNum : monthStartDateNum,
|
||||
};
|
||||
const monthEnd = {
|
||||
year: monthEndDate.getFullYear(),
|
||||
month: monthEndDate.getMonth() + 1,
|
||||
date: monthEndDateNum,
|
||||
};
|
||||
// get available dates
|
||||
this.updateSelectableDates(monthStart, monthEnd);
|
||||
}
|
||||
|
||||
// populate datesArray
|
||||
for (let i = monthStartDateNum; i <= monthEndDateNum; i++) {
|
||||
let dayClasses = "";
|
||||
const thisDate = {
|
||||
// NOTE: uses monthNum (1-based), NOT monthIndex (0-based)
|
||||
year: yearNum,
|
||||
month: monthIndex + 1,
|
||||
date: i,
|
||||
};
|
||||
if (offset === 0 && i === todayDateNum) {
|
||||
dayClasses += "current-day";
|
||||
}
|
||||
if (offset === 0 && i < todayDateNum && direction === "future") {
|
||||
dayClasses += "unavailable-day";
|
||||
}
|
||||
if (offset === 0 && i > todayDateNum && direction === "past") {
|
||||
dayClasses += "unavailable-day";
|
||||
}
|
||||
if (Math.abs(offset) === 1 && direction === "future" && i > initialViewEndDate) {
|
||||
dayClasses += "day-hidden";
|
||||
}
|
||||
if (Math.abs(offset) === 1 && direction === "past" && i < initialViewStartDate) {
|
||||
dayClasses += "day-hidden";
|
||||
}
|
||||
if (this.selectableDates === "custom" && this.isSelectableDate(thisDate)) {
|
||||
dayClasses += " selectable-day";
|
||||
}
|
||||
const dateObject = {
|
||||
dateNum: i,
|
||||
dayClasses: dayClasses,
|
||||
inputValue: thisDate,
|
||||
};
|
||||
datesArray.push(dateObject);
|
||||
}
|
||||
|
||||
const monthToAdd = {
|
||||
monthLabel: monthsOfYear[monthIndex],
|
||||
monthIndex: monthIndex,
|
||||
yearNum: yearNum,
|
||||
dates: datesArray,
|
||||
startDateDayIndex: startDateDayIndex,
|
||||
monthClass: monthClass,
|
||||
};
|
||||
|
||||
return monthToAdd;
|
||||
},
|
||||
addMonthData() {
|
||||
const monthToAdd = this.getMonthData();
|
||||
if (this.calendarViewDirection === "future") {
|
||||
this.calendarData.push(monthToAdd);
|
||||
}
|
||||
if (this.calendarViewDirection === "past") {
|
||||
this.calendarData.unshift(monthToAdd);
|
||||
}
|
||||
},
|
||||
isSelectableDate(thisDate) {
|
||||
const testForDate = (dateInArray) => {
|
||||
return (
|
||||
dateInArray.date === thisDate.date &&
|
||||
dateInArray.month === thisDate.month &&
|
||||
dateInArray.year === thisDate.year
|
||||
);
|
||||
};
|
||||
const isSelectable =
|
||||
this.selectableDatesData.findIndex(testForDate) > -1 ? true : false;
|
||||
return isSelectable;
|
||||
},
|
||||
updateSelectableDates(monthStart, monthEnd) {
|
||||
this.customSelectableDatesCallback(monthStart, monthEnd)?.forEach((newObj) => {
|
||||
const index = this.selectableDatesData.findIndex(
|
||||
(obj) =>
|
||||
obj.year === newObj.year &&
|
||||
obj.month === newObj.month &&
|
||||
obj.date === newObj.date
|
||||
);
|
||||
if (index === -1) this.selectableDatesData.push(newObj);
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
.calendar-grid-container {
|
||||
margin: 0 auto;
|
||||
max-width: 414px;
|
||||
|
|
@ -231,16 +383,39 @@ export default {
|
|||
.grid-item {
|
||||
text-align: center;
|
||||
margin: 10%;
|
||||
|
||||
&.first-day-,
|
||||
&.first-day-0 {
|
||||
grid-column-start: 1;
|
||||
}
|
||||
&.first-day-1 {
|
||||
grid-column-start: 2;
|
||||
}
|
||||
&.first-day-2 {
|
||||
grid-column-start: 3;
|
||||
}
|
||||
&.first-day-3 {
|
||||
grid-column-start: 4;
|
||||
}
|
||||
&.first-day-4 {
|
||||
grid-column-start: 5;
|
||||
}
|
||||
&.first-day-5 {
|
||||
grid-column-start: 6;
|
||||
}
|
||||
&.first-day-6 {
|
||||
grid-column-start: 7;
|
||||
}
|
||||
}
|
||||
|
||||
.month-year {
|
||||
grid-area: 1 / 1 / 2 / 4;
|
||||
grid-area: 1 / 1 / 2 / 5;
|
||||
text-transform: uppercase;
|
||||
font-weight: 300;
|
||||
letter-spacing: 0.75px;
|
||||
}
|
||||
.legend {
|
||||
grid-area: 1 / 4 / 2 / 6;
|
||||
grid-area: 1 / 5 / 2 / 8;
|
||||
.legend-circle {
|
||||
border-radius: 50%;
|
||||
width: 16px;
|
||||
|
|
@ -249,54 +424,9 @@ export default {
|
|||
border: 1px solid $blue;
|
||||
}
|
||||
}
|
||||
.nav-forward {
|
||||
grid-area: 1 / 7 / 2 / 8;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
button {
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 7px;
|
||||
height: 12px;
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='7' height='12' viewBox='0 0 7 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.66831 5.99879C6.66955 6.17554 6.60074 6.34558 6.47695 6.47168L1.15501 11.8017C1.02812 11.9287 0.85603 12 0.676587 12C0.497145 12 0.325053 11.9287 0.198168 11.8017C0.0712831 11.6748 7.268e-09 11.5026 8.07183e-09 11.3231C8.87567e-09 11.1436 0.0712831 10.9714 0.198168 10.8445L5.05126 5.99879L0.198168 1.14736C0.0725521 1.02042 0.00248585 0.848755 0.00338306 0.670131C0.00428028 0.491506 0.0760674 0.320554 0.202952 0.194881C0.329837 0.0692091 0.501426 -0.000888818 0.679971 9.54485e-06C0.858515 0.000906955 1.02939 0.0727263 1.15501 0.199668L6.47312 5.52399C6.59843 5.65017 6.66862 5.82092 6.66831 5.99879Z' fill='%231574A1'/%3E%3C/svg%3E%0A");
|
||||
}
|
||||
}
|
||||
}
|
||||
.nav-back {
|
||||
grid-area: 1 / 6 / 2 / 7;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
button {
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 7px;
|
||||
height: 12px;
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='7' height='12' viewBox='0 0 7 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0.331685 6.00121C0.330445 5.82446 0.399256 5.65442 0.523053 5.52832L5.84499 0.198256C5.97188 0.0713149 6.14397 -8.63821e-08 6.32341 -6.72174e-08C6.50285 -4.80527e-08 6.67495 0.071315 6.80183 0.198256C6.92872 0.325198 7 0.497368 7 0.67689C7 0.856412 6.92872 1.02858 6.80183 1.15552L1.94874 6.00121L6.80183 10.8526C6.92745 10.9796 6.99751 11.1512 6.99662 11.3299C6.99572 11.5085 6.92393 11.6794 6.79705 11.8051C6.67016 11.9308 6.49857 12.0009 6.32003 12C6.14148 11.9991 5.97061 11.9273 5.84499 11.8003L0.526881 6.47601C0.401568 6.34983 0.331375 6.17908 0.331685 6.00121Z' fill='%231574A1'/%3E%3C/svg%3E%0A");
|
||||
}
|
||||
}
|
||||
}
|
||||
.nav-back,
|
||||
.nav-forward {
|
||||
button {
|
||||
position: relative;
|
||||
border-radius: 50%;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background-color: $blue-100;
|
||||
border: 1px solid $blue;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
&:focus,
|
||||
&:focus-visible {
|
||||
border: 2.5px solid $blue;
|
||||
box-shadow: none;
|
||||
background-color: $blue-100;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
display: none;
|
||||
}
|
||||
|
||||
.radio-wrapper {
|
||||
|
|
@ -369,40 +499,6 @@ export default {
|
|||
min-width: 40px;
|
||||
border-radius: 50%;
|
||||
|
||||
&.past-day {
|
||||
color: $gray-500;
|
||||
background-color: transparent;
|
||||
border: 1px solid transparent;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&.selectable-day {
|
||||
color: $blue;
|
||||
background-color: $blue-100;
|
||||
border: 1px solid $blue;
|
||||
}
|
||||
|
||||
&.current-day {
|
||||
&:after {
|
||||
content: "";
|
||||
width: 0.25rem;
|
||||
height: 0.25rem;
|
||||
border-radius: 50%;
|
||||
background-color: $blue;
|
||||
position: absolute;
|
||||
top: 28px;
|
||||
}
|
||||
.first-day {
|
||||
color: $blue;
|
||||
}
|
||||
}
|
||||
|
||||
&.future-day {
|
||||
color: $gray-600;
|
||||
background-color: transparent;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
span {
|
||||
&.small {
|
||||
font-size: 0.75rem;
|
||||
|
|
@ -434,6 +530,105 @@ export default {
|
|||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.selectable-day {
|
||||
label {
|
||||
color: $blue;
|
||||
background-color: $blue-100;
|
||||
border: 1px solid $blue;
|
||||
}
|
||||
}
|
||||
&.past-day,
|
||||
&.future-day,
|
||||
&.unavailable-day {
|
||||
label {
|
||||
color: $gray-500;
|
||||
background-color: transparent;
|
||||
border: 1px solid transparent;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
&.current-day {
|
||||
label {
|
||||
&:after {
|
||||
content: "";
|
||||
width: 0.25rem;
|
||||
height: 0.25rem;
|
||||
border-radius: 50%;
|
||||
background-color: $blue;
|
||||
position: absolute;
|
||||
top: 28px;
|
||||
}
|
||||
.first-day {
|
||||
color: $blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.initial-view {
|
||||
&.month-hidden {
|
||||
display: none;
|
||||
}
|
||||
.day-hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.past {
|
||||
.calendar-grid-container {
|
||||
.month-year {
|
||||
grid-area: 1 / 1 / 2 / 6;
|
||||
}
|
||||
.nav-forward {
|
||||
grid-area: 1 / 7 / 2 / 8;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
button {
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 7px;
|
||||
height: 12px;
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='7' height='12' viewBox='0 0 7 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.66831 5.99879C6.66955 6.17554 6.60074 6.34558 6.47695 6.47168L1.15501 11.8017C1.02812 11.9287 0.85603 12 0.676587 12C0.497145 12 0.325053 11.9287 0.198168 11.8017C0.0712831 11.6748 7.268e-09 11.5026 8.07183e-09 11.3231C8.87567e-09 11.1436 0.0712831 10.9714 0.198168 10.8445L5.05126 5.99879L0.198168 1.14736C0.0725521 1.02042 0.00248585 0.848755 0.00338306 0.670131C0.00428028 0.491506 0.0760674 0.320554 0.202952 0.194881C0.329837 0.0692091 0.501426 -0.000888818 0.679971 9.54485e-06C0.858515 0.000906955 1.02939 0.0727263 1.15501 0.199668L6.47312 5.52399C6.59843 5.65017 6.66862 5.82092 6.66831 5.99879Z' fill='%231574A1'/%3E%3C/svg%3E%0A");
|
||||
}
|
||||
}
|
||||
}
|
||||
.nav-back {
|
||||
grid-area: 1 / 6 / 2 / 7;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
button {
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 7px;
|
||||
height: 12px;
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='7' height='12' viewBox='0 0 7 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0.331685 6.00121C0.330445 5.82446 0.399256 5.65442 0.523053 5.52832L5.84499 0.198256C5.97188 0.0713149 6.14397 -8.63821e-08 6.32341 -6.72174e-08C6.50285 -4.80527e-08 6.67495 0.071315 6.80183 0.198256C6.92872 0.325198 7 0.497368 7 0.67689C7 0.856412 6.92872 1.02858 6.80183 1.15552L1.94874 6.00121L6.80183 10.8526C6.92745 10.9796 6.99751 11.1512 6.99662 11.3299C6.99572 11.5085 6.92393 11.6794 6.79705 11.8051C6.67016 11.9308 6.49857 12.0009 6.32003 12C6.14148 11.9991 5.97061 11.9273 5.84499 11.8003L0.526881 6.47601C0.401568 6.34983 0.331375 6.17908 0.331685 6.00121Z' fill='%231574A1'/%3E%3C/svg%3E%0A");
|
||||
}
|
||||
}
|
||||
}
|
||||
.nav-back,
|
||||
.nav-forward {
|
||||
button {
|
||||
position: relative;
|
||||
border-radius: 50%;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background-color: $blue-100;
|
||||
border: 1px solid $blue;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
&:focus,
|
||||
&:focus-visible {
|
||||
border: 2.5px solid $blue;
|
||||
box-shadow: none;
|
||||
background-color: $blue-100;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
ref="closeButton"
|
||||
type="button"
|
||||
class="btn-close"
|
||||
data-bs-dismiss="modal"
|
||||
@mousedown="closeModal"
|
||||
aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body ps-5 pe-5 pb-4 pt-0">
|
||||
|
|
@ -77,7 +77,7 @@ export default {
|
|||
async validateAndEmit() {
|
||||
const validationResult = await this.validate();
|
||||
if (validationResult.valid) {
|
||||
this.$emit("footer-button-event");
|
||||
this.$emit("footer-button-event");
|
||||
} else {
|
||||
this.resetButtonStyle();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,8 +71,9 @@ export function getMountOptions(mockData) {
|
|||
mixins: mockData.mixins,
|
||||
stubs: { Form },
|
||||
};
|
||||
const props = mockData.propsData || {};
|
||||
|
||||
return { global };
|
||||
return { global, props };
|
||||
}
|
||||
|
||||
export function setupMocksForJsFiles(mockData = {}) {
|
||||
|
|
|
|||
19
src/layouts/demo-date-picker/demo-date-picker.spec.js
Normal file
19
src/layouts/demo-date-picker/demo-date-picker.spec.js
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import demoDatePicker from "./demo-date-picker";
|
||||
|
||||
// Supporting Files
|
||||
import { shallowMount } from "@vue/test-utils";
|
||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||
|
||||
describe("demo-date-picker.vue", () => {
|
||||
test.only("test TK...", () => {});
|
||||
});
|
||||
|
||||
function setupMocks({ mountOptionsMockData = {} }) {
|
||||
const mountOptions = getMountOptions({
|
||||
...mountOptionsMockData,
|
||||
});
|
||||
|
||||
const wrapper = shallowMount(demoDatePicker, mountOptions);
|
||||
|
||||
return { wrapper };
|
||||
}
|
||||
83
src/layouts/demo-date-picker/demo-date-picker.vue
Normal file
83
src/layouts/demo-date-picker/demo-date-picker.vue
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
<template>
|
||||
<div class="page-container-grouped-styles">
|
||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||
<div class="select-car">
|
||||
<div class="select-car-form rounded text-center">
|
||||
<div class="fade-on-route-transition">
|
||||
<date-picker
|
||||
selectableDates="custom"
|
||||
v-model="selectedDate"
|
||||
:customSelectableDatesCallback="getAvailableDates" />
|
||||
<!-- EXAMPLE THAT OVERRIDES TODAY BY PASSING STRING --
|
||||
<date-picker
|
||||
selectableDates="custom"
|
||||
v-model="selectedDate"
|
||||
:customSelectableDatesCallback="getAvailableDates"
|
||||
todayOverrideDateString="2022-12-30T03:00:00" /> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// Components
|
||||
import datePicker from "@/digital-components/date-picker/date-picker";
|
||||
import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
|
||||
import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner";
|
||||
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
|
||||
|
||||
// Supporting files
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||
import { storeActions } from "@/constants/store-actions";
|
||||
import store from "@/store";
|
||||
|
||||
import baseMixin from "@/mixins/base-mixin";
|
||||
|
||||
export default {
|
||||
name: "demo-date-picker",
|
||||
data() {
|
||||
return {
|
||||
selectedDate: null,
|
||||
// selectedDate: { // USE THIS FORMAT FOR A PRE-SELECTED DATE ON LOAD
|
||||
// year: 2023,
|
||||
// month: 3, // use 1-based index for months
|
||||
// date: 21,
|
||||
// },
|
||||
mockSelectableDatesData: [
|
||||
{ year: 2023, month: 3, date: 4 },
|
||||
{ year: 2023, month: 3, date: 5 },
|
||||
{ year: 2023, month: 3, date: 22 },
|
||||
{ year: 2023, month: 4, date: 13 },
|
||||
{ year: 2023, month: 4, date: 14 },
|
||||
{ year: 2023, month: 4, date: 26 },
|
||||
{ year: 2023, month: 5, date: 21 },
|
||||
{ year: 2023, month: 5, date: 23 },
|
||||
{ year: 2023, month: 5, date: 25 },
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
todayDate() {
|
||||
const today = new Date();
|
||||
return today.toDateString();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
arePagePrerequisitesValid() {
|
||||
return true;
|
||||
},
|
||||
getAvailableDates(startDate, endDate) {
|
||||
this.mockSelectableDatesData.push(endDate);
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
// temporary test method that adds endDate to list of selectable dates
|
||||
return this.mockSelectableDatesData;
|
||||
},
|
||||
},
|
||||
components: {
|
||||
datePicker,
|
||||
funnelHeader,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -271,7 +271,7 @@ export default {
|
|||
|
||||
// call saveSession here - navigateWithSaving saves too late in the flow
|
||||
await saveSession({});
|
||||
return this.$router.navigateWithoutSaving(
|
||||
return this.$router.navigateWithSaving(
|
||||
this.navigationScenarios.CLICKED_FORWARD_WITH_NO_QUESTIONS,
|
||||
this.$route
|
||||
);
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ import { getPricedMobileFeePart } from "@/layouts/service-location/helpers/servi
|
|||
|
||||
export default {
|
||||
name: "mobile-location-modal-questions",
|
||||
emits: ["update:modelValue", "set-mobile-fee-part"],
|
||||
emits: ["update:modelValue", "updated-mobile-fee-part", "updated-contains-military-base"],
|
||||
data() {
|
||||
return {
|
||||
internalModel: deepClone(this.modelValue),
|
||||
|
|
@ -89,8 +89,6 @@ export default {
|
|||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
isZipServiceableMobile: Boolean,
|
||||
isZipServiceableInshop: Boolean,
|
||||
linkWidgetName: String,
|
||||
modalWidgetName: String,
|
||||
alertNonServiceableZipWidgetName: String,
|
||||
|
|
@ -109,7 +107,8 @@ export default {
|
|||
this.addressModel.state &&
|
||||
this.addressModel.state !== "" &&
|
||||
this.addressModel.zipCode &&
|
||||
this.addressModel.zipCode !== ""
|
||||
this.addressModel.zipCode !== "" &&
|
||||
this.internalModel.isVehicleProtected !== null
|
||||
) {
|
||||
return `${this.addressModel.streetAddress}\n${this.addressModel.city}, ${this.addressModel.state} ${this.addressModel.zipCode}`;
|
||||
}
|
||||
|
|
@ -183,10 +182,11 @@ export default {
|
|||
const mobileFeePart = await getPricedMobileFeePart(serviceZipCode);
|
||||
|
||||
// emit it to parent
|
||||
this.$emit("set-mobile-fee-part", mobileFeePart);
|
||||
this.$emit("updated-mobile-fee-part", mobileFeePart);
|
||||
|
||||
// Update the page level model
|
||||
this.$emit("update:modelValue", this.internalModel);
|
||||
this.$emit("updated-contains-military-base", zipCodeData.containsMilitaryBase);
|
||||
this.closeModal();
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -319,11 +319,15 @@ describe("service-location.vue", () => {
|
|||
state: "OH",
|
||||
};
|
||||
|
||||
// Assert
|
||||
wrapper.setData({ zipCode: newServiceZipCodeQuestion.zipCode });
|
||||
wrapper.vm.$nextTick(() => {
|
||||
expect(wrapper.vm.zipContainsMilitaryBase).toBe(true);
|
||||
const serviceZipCodeComponent = wrapper.findComponent({
|
||||
ref: "serviceZipCodeQuestion",
|
||||
});
|
||||
|
||||
// Act
|
||||
serviceZipCodeComponent.vm.$emit("updated-contains-military-base", true);
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.zipContainsMilitaryBase).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -4,44 +4,42 @@
|
|||
<loadingModal ref="loadingModal" />
|
||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
|
||||
<div class="fade-on-route-transition sub-container make-tall">
|
||||
<serviceZipModalQuestion
|
||||
v-model="serviceZipCodeQuestion"
|
||||
ref="serviceZipCodeQuestion"
|
||||
:mobileFeePart="mobileFeePart"
|
||||
@set-mobile-fee-part="setMobileFeePart"
|
||||
linkWidgetName="ServiceZipLinkWidget"
|
||||
modalWidgetName="ServiceZipModalWidget" />
|
||||
<alert
|
||||
class="my-4"
|
||||
cmsWidgetName="AlertMilitaryBaseZipWidget"
|
||||
v-if="zipContainsMilitaryBase"
|
||||
alertClass="alert-warning" />
|
||||
<serviceTypeQuestion
|
||||
v-model="selectedServiceType"
|
||||
:isGlassServiceableInshop="isGlassServiceableInshop"
|
||||
:isRecalibrationServiceableInshop="isRecalibrationServiceableInshop"
|
||||
:isGlassServiceableMobile="isGlassServiceableMobile"
|
||||
:isRecalibrationServiceableMobile="isRecalibrationServiceableMobile"
|
||||
ref="serviceTypeQuestion"
|
||||
groupName="serviceTypeQuestion"
|
||||
cmsWidgetName="ServiceTypeQuestionWidget"
|
||||
validationRules="option-required" />
|
||||
<mobileLocationModalQuestions
|
||||
v-if="selectedServiceType === 'Mobile'"
|
||||
v-model="mobileLocationQuestions"
|
||||
:mobileFeePart="mobileFeePart"
|
||||
@set-mobile-fee-part="setMobileFeePart"
|
||||
ref="mobileLocationModalQuestions"
|
||||
linkWidgetName="MobileLocationLinkWidget"
|
||||
modalWidgetName="MobileLocationModalWidget" />
|
||||
<funnel-footer
|
||||
cmsWidgetName="FunnelFooterWidget"
|
||||
ref="funnelFooter"
|
||||
:isForwardActionDisabled="!meta.valid || shouldDisableForwardAction"
|
||||
@back-clicked="backButtonAction"
|
||||
@ForwardClicked="forwardButtonAction" />
|
||||
</div>
|
||||
<serviceZipModalQuestion
|
||||
v-model="serviceZipCodeQuestion"
|
||||
ref="serviceZipCodeQuestion"
|
||||
:mobileFeePart="mobileFeePart"
|
||||
@updated-mobile-fee-part="setMobileFeePart"
|
||||
linkWidgetName="ServiceZipLinkWidget"
|
||||
modalWidgetName="ServiceZipModalWidget" />
|
||||
<alert
|
||||
class="my-4"
|
||||
cmsWidgetName="AlertMilitaryBaseZipWidget"
|
||||
v-if="showMilitaryZipAlert"
|
||||
alertClass="alert-warning" />
|
||||
<serviceTypeQuestion
|
||||
v-model="selectedServiceType"
|
||||
:isGlassServiceableInshop="isGlassServiceableInshop"
|
||||
:isRecalibrationServiceableInshop="isRecalibrationServiceableInshop"
|
||||
:isGlassServiceableMobile="isGlassServiceableMobile"
|
||||
:isRecalibrationServiceableMobile="isRecalibrationServiceableMobile"
|
||||
ref="serviceTypeQuestion"
|
||||
groupName="serviceTypeQuestion"
|
||||
cmsWidgetName="ServiceTypeQuestionWidget"
|
||||
validationRules="option-required" />
|
||||
<mobileLocationModalQuestions
|
||||
v-if="selectedServiceType === 'Mobile'"
|
||||
v-model="mobileLocationQuestions"
|
||||
:mobileFeePart="mobileFeePart"
|
||||
@updated-mobile-fee-part="setMobileFeePart"
|
||||
ref="mobileLocationModalQuestions"
|
||||
linkWidgetName="MobileLocationLinkWidget"
|
||||
modalWidgetName="MobileLocationModalWidget" />
|
||||
<funnel-footer
|
||||
cmsWidgetName="FunnelFooterWidget"
|
||||
ref="funnelFooter"
|
||||
:isForwardActionDisabled="!meta.valid"
|
||||
@back-clicked="backButtonAction"
|
||||
@ForwardClicked="forwardButtonAction" />
|
||||
</div>
|
||||
</Form>
|
||||
</template>
|
||||
|
|
@ -102,12 +100,7 @@ export default {
|
|||
const parentAccountNumber = store.getters.payment.parentAccountNumber;
|
||||
const billToAccountNumber = 87291;
|
||||
|
||||
const mobileFeePartPromise = getPricedMobileFeePart(
|
||||
serviceZipCode,
|
||||
serviceType,
|
||||
parentAccountNumber,
|
||||
billToAccountNumber
|
||||
);
|
||||
const mobileFeePartPromise = getPricedMobileFeePart(serviceZipCode);
|
||||
|
||||
// Settle promises and get results
|
||||
const promiseResultMap = [
|
||||
|
|
@ -181,8 +174,11 @@ export default {
|
|||
this.isVehicleProtected = newValue.isVehicleProtected;
|
||||
},
|
||||
},
|
||||
shouldDisableForwardAction() {
|
||||
return this.zipContainsMilitaryBase;
|
||||
showMilitaryZipAlert() {
|
||||
return (
|
||||
this.zipContainsMilitaryBase &&
|
||||
(this.isZipServiceableMobile == null || this.isZipServiceableMobile === true)
|
||||
);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -206,6 +202,11 @@ export default {
|
|||
this.mobileFeePart = mobileFeePart;
|
||||
}
|
||||
},
|
||||
setContainsMilitaryBase(val) {
|
||||
if (this.zipContainsMilitaryBase !== val) {
|
||||
this.zipContainsMilitaryBase = val;
|
||||
}
|
||||
},
|
||||
getServiceAddressFromStore() {
|
||||
return store.getters.order.serviceLocation.address;
|
||||
},
|
||||
|
|
@ -247,15 +248,11 @@ export default {
|
|||
zipCode: {
|
||||
async handler(newValue, oldValue) {
|
||||
if (newValue !== oldValue) {
|
||||
baseMixin.methods.getZipCodeData(newValue).then((r) => {
|
||||
this.zipContainsMilitaryBase = r.containsMilitaryBase;
|
||||
});
|
||||
const serviceZipCode = store.getters.order.serviceLocation.zipCode;
|
||||
const serviceabilityDetails = await getServiceabilityDetails(serviceZipCode);
|
||||
|
||||
this.setServiceabilityDetails(serviceabilityDetails.data);
|
||||
}
|
||||
|
||||
const serviceZipCode = store.getters.order.serviceLocation.zipCode;
|
||||
const serviceabilityDetails = await getServiceabilityDetails(serviceZipCode);
|
||||
|
||||
this.setServiceabilityDetails(serviceabilityDetails.data);
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -55,8 +55,10 @@ export default {
|
|||
return filteredAnswers;
|
||||
},
|
||||
serviceability() {
|
||||
const mobileAvailable = this.isGlassServiceableMobile && this.isRecalibrationServiceableMobile;
|
||||
const inshopAvailable = this.isGlassServiceableInshop && this.isRecalibrationServiceableInshop;
|
||||
const mobileAvailable =
|
||||
this.isGlassServiceableMobile && this.isRecalibrationServiceableMobile;
|
||||
const inshopAvailable =
|
||||
this.isGlassServiceableInshop && this.isRecalibrationServiceableInshop;
|
||||
|
||||
let result;
|
||||
if (inshopAvailable && mobileAvailable) {
|
||||
|
|
@ -68,7 +70,7 @@ export default {
|
|||
} else if (!inshopAvailable && !mobileAvailable) {
|
||||
result = "None";
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
},
|
||||
selectedValues: {
|
||||
|
|
|
|||
|
|
@ -44,13 +44,12 @@ import { getPricedMobileFeePart } from "@/layouts/service-location/helpers/servi
|
|||
|
||||
export default {
|
||||
name: "service-zip-modal-question",
|
||||
emits: ["update:modelValue", "set-mobile-fee-part"],
|
||||
emits: ["update:modelValue", "updated-mobile-fee-part", "updated-contains-military-base"],
|
||||
data() {
|
||||
return {
|
||||
internalModel: this.copyModel(this.modelValue),
|
||||
serviceZipCodeTextInputId: "",
|
||||
displayInvalidZipAlert: false,
|
||||
|
||||
};
|
||||
},
|
||||
props: {
|
||||
|
|
@ -142,26 +141,32 @@ export default {
|
|||
},
|
||||
|
||||
async setZipCode() {
|
||||
this.resetAlerts();
|
||||
if (this.internalModel.zipCode !== this.modelValue.zipCode) {
|
||||
this.resetAlerts();
|
||||
|
||||
const zipCodeData = await this.getZipCodeData(this.internalModel.zipCode);
|
||||
const zipCodeData = await this.getZipCodeData(this.internalModel.zipCode);
|
||||
|
||||
if (!zipCodeData.isValid) {
|
||||
this.displayInvalidZipAlert = true;
|
||||
this.focusOnZipInput();
|
||||
this.resetModalButtonStyle();
|
||||
if (!zipCodeData.isValid) {
|
||||
this.displayInvalidZipAlert = true;
|
||||
this.focusOnZipInput();
|
||||
this.resetModalButtonStyle();
|
||||
} else {
|
||||
this.internalModel.state = zipCodeData.state;
|
||||
|
||||
// retrieve mobile fee part
|
||||
const serviceZipCode = this.internalModel.zipCode;
|
||||
const mobileFeePart = await getPricedMobileFeePart(serviceZipCode);
|
||||
|
||||
// emit it to parent
|
||||
this.$emit("updated-mobile-fee-part", mobileFeePart);
|
||||
|
||||
// Update the page level model
|
||||
this.$emit("update:modelValue", this.internalModel);
|
||||
this.$emit("updated-contains-military-base", zipCodeData.containsMilitaryBase);
|
||||
|
||||
this.closeModal();
|
||||
}
|
||||
} else {
|
||||
this.internalModel.state = zipCodeData.state;
|
||||
|
||||
// retrieve mobile fee part
|
||||
const serviceZipCode = this.internalModel.zipCode;
|
||||
const mobileFeePart = await getPricedMobileFeePart(serviceZipCode);
|
||||
|
||||
// emit it to parent
|
||||
this.$emit("set-mobile-fee-part", mobileFeePart);
|
||||
|
||||
// Update the page level model
|
||||
this.$emit("update:modelValue", this.internalModel);
|
||||
this.closeModal();
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -325,6 +325,17 @@ export default {
|
|||
false
|
||||
);
|
||||
|
||||
if (this.isWindshieldRepair) {
|
||||
const supportingItems = await this.dispatchStoreAction(
|
||||
storeActions.GET_SUPPORTING_ITEMS
|
||||
);
|
||||
this.dispatchStoreAction(
|
||||
this.storeActions.SAVE_SUPPORTING_ITEMS,
|
||||
supportingItems.data,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
return this.navigateForward();
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -31,8 +31,14 @@ import { applicationConfig } from "../constants/application-config";
|
|||
// Components
|
||||
import quote from "@/layouts/quote/quote.vue";
|
||||
import datePicker from "@/digital-components/date-picker/date-picker.vue";
|
||||
import demoDatePicker from "@/layouts/demo-date-picker/demo-date-picker.vue";
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: "/demo-date-picker", // This is a temporary route for testing.
|
||||
name: "demo-date-picker",
|
||||
component: demoDatePicker,
|
||||
},
|
||||
{
|
||||
path: "/date-picker", // This is a temporary route for testing.
|
||||
name: "date-picker",
|
||||
|
|
|
|||
|
|
@ -929,7 +929,7 @@ export const actions = {
|
|||
getServiceabilityDetails(context, { serviceZipCode }) {
|
||||
const lineItems = context.getters.order.lineItems;
|
||||
const lineItemsToSend = [...lineItems.supportingItems];
|
||||
const encodedLineItems = encodeURIComponent(JSON.stringify(lineItemsToSend))
|
||||
const encodedLineItems = encodeURIComponent(JSON.stringify(lineItemsToSend));
|
||||
return globalMethods.callHttpClient({
|
||||
method: endpoints.GetServiceabilityDetails.method,
|
||||
endpoint: `${endpoints.GetServiceabilityDetails.url}?zip=${serviceZipCode}&lineItems=${encodedLineItems}`,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ describe("buttonMain.vue", () => {
|
|||
const wrapper = shallowMount(
|
||||
buttonMain,
|
||||
setupMocks({
|
||||
props: {
|
||||
propsData: {
|
||||
isPrimary: true,
|
||||
},
|
||||
})
|
||||
|
|
@ -25,7 +25,7 @@ describe("buttonMain.vue", () => {
|
|||
const wrapper = shallowMount(
|
||||
buttonMain,
|
||||
setupMocks({
|
||||
props: {
|
||||
propsData: {
|
||||
isDisabled: true,
|
||||
},
|
||||
})
|
||||
|
|
@ -41,7 +41,7 @@ describe("buttonMain.vue", () => {
|
|||
const wrapper = shallowMount(
|
||||
buttonMain,
|
||||
setupMocks({
|
||||
props: {
|
||||
propsData: {
|
||||
loaderColor: "blue",
|
||||
loaderEnabled: true,
|
||||
},
|
||||
|
|
@ -62,7 +62,7 @@ describe("buttonMain.vue", () => {
|
|||
const wrapper = shallowMount(
|
||||
buttonMain,
|
||||
setupMocks({
|
||||
props: {
|
||||
propsData: {
|
||||
loaderPosition: "right",
|
||||
loaderEnabled: true,
|
||||
},
|
||||
|
|
@ -83,7 +83,7 @@ describe("buttonMain.vue", () => {
|
|||
const wrapper = shallowMount(
|
||||
buttonMain,
|
||||
setupMocks({
|
||||
props: {
|
||||
propsData: {
|
||||
loaderPosition: "right",
|
||||
loaderEnabled: true,
|
||||
},
|
||||
|
|
@ -108,7 +108,7 @@ describe("buttonMain.vue", () => {
|
|||
const wrapper = shallowMount(
|
||||
buttonMain,
|
||||
setupMocks({
|
||||
props: {
|
||||
propsData: {
|
||||
loaderPosition: "right",
|
||||
loaderEnabled: true,
|
||||
},
|
||||
|
|
@ -132,7 +132,7 @@ describe("buttonMain.vue", () => {
|
|||
const wrapper = shallowMount(
|
||||
buttonMain,
|
||||
setupMocks({
|
||||
props: {
|
||||
propsData: {
|
||||
loaderPosition: "right",
|
||||
loaderEnabled: true,
|
||||
isDisabled: false,
|
||||
|
|
@ -156,7 +156,7 @@ describe("buttonMain.vue", () => {
|
|||
const wrapper = shallowMount(
|
||||
buttonMain,
|
||||
setupMocks({
|
||||
props: {
|
||||
propsData: {
|
||||
loaderPosition: "right",
|
||||
loaderEnabled: true,
|
||||
isDisabled: true,
|
||||
|
|
|
|||
Loading…
Reference in a new issue