Merge branch 'develop' into feature/CASH-836
This commit is contained in:
commit
1dadc5f1cb
54 changed files with 748 additions and 475 deletions
|
|
@ -2,14 +2,18 @@
|
|||
/* IMPORTANT: READ README.md in this folder! Do not edit .css file directly! */
|
||||
/* IMPORTANT: READ README.md in this folder! Do not edit .css file directly! */
|
||||
/* IMPORTANT: READ README.md in this folder! Do not edit .css file directly! */
|
||||
@import url("https://fonts.googleapis.com/css2?family=Urbanist:ital,wght@0,100..900;1,100..900&display=swap");
|
||||
body {
|
||||
font-family: Roboto;
|
||||
font-family: Urbanist, Arial, Helvetica, sans-serif;
|
||||
font-weight: 400;
|
||||
color: #000;
|
||||
margin: 0;
|
||||
background-color: white;
|
||||
padding: 8px;
|
||||
}
|
||||
body h3 {
|
||||
font-weight: 600;
|
||||
}
|
||||
body .validation-info {
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -31,7 +35,7 @@ body .form-group {
|
|||
body .headerlinecontainer .headerline1 {
|
||||
font-size: 20px;
|
||||
line-height: 32px;
|
||||
font-weight: 400;
|
||||
font-weight: 500;
|
||||
margin: 24px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
|
@ -92,7 +96,7 @@ body .creditCardSpecific div .headerlinecontainer {
|
|||
display: none;
|
||||
}
|
||||
body .creditCardSpecific div label {
|
||||
font-weight: 500;
|
||||
font-weight: 600;
|
||||
}
|
||||
body .creditCardSpecific #infoRow2 {
|
||||
margin-bottom: 0;
|
||||
|
|
@ -268,6 +272,7 @@ body .cc-error-container #alert-message {
|
|||
#fmg-checkout-shared #other-payments-container label span.paymentOptionText {
|
||||
margin: auto 0;
|
||||
padding-left: 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
#fmg-checkout-shared #other-payments-container #afterpayParentLink label > span:first-of-type {
|
||||
width: 100%;
|
||||
|
|
|
|||
|
|
@ -2,14 +2,19 @@
|
|||
/* IMPORTANT: READ README.md in this folder! Do not edit .css file directly! */
|
||||
/* IMPORTANT: READ README.md in this folder! Do not edit .css file directly! */
|
||||
/* IMPORTANT: READ README.md in this folder! Do not edit .css file directly! */
|
||||
@import url("https://fonts.googleapis.com/css2?family=Urbanist:ital,wght@0,100..900;1,100..900&display=swap");
|
||||
body {
|
||||
font-family: Roboto;
|
||||
font-family: Urbanist, Arial, Helvetica, sans-serif;
|
||||
font-weight: 400;
|
||||
color: #000;
|
||||
margin: 0;
|
||||
background-color: white;
|
||||
padding: 8px;
|
||||
|
||||
h3 {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.validation-info {
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -37,7 +42,7 @@ body {
|
|||
.headerline1 {
|
||||
font-size: 20px;
|
||||
line-height: 32px;
|
||||
font-weight: 400;
|
||||
font-weight: 500;
|
||||
margin: 24px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
|
@ -98,7 +103,7 @@ body {
|
|||
display: none;
|
||||
}
|
||||
label {
|
||||
font-weight: 500;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
#infoRow2 {
|
||||
|
|
@ -325,6 +330,7 @@ body {
|
|||
&.paymentOptionText {
|
||||
margin: auto 0;
|
||||
padding-left: 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
BIN
src/assets/fonts/Urbanist-Bold.woff
Normal file
BIN
src/assets/fonts/Urbanist-Bold.woff
Normal file
Binary file not shown.
BIN
src/assets/fonts/Urbanist-Bold.woff2
Normal file
BIN
src/assets/fonts/Urbanist-Bold.woff2
Normal file
Binary file not shown.
BIN
src/assets/fonts/Urbanist-BoldItalic.woff
Normal file
BIN
src/assets/fonts/Urbanist-BoldItalic.woff
Normal file
Binary file not shown.
BIN
src/assets/fonts/Urbanist-BoldItalic.woff2
Normal file
BIN
src/assets/fonts/Urbanist-BoldItalic.woff2
Normal file
Binary file not shown.
BIN
src/assets/fonts/Urbanist-ExtraBold.woff
Normal file
BIN
src/assets/fonts/Urbanist-ExtraBold.woff
Normal file
Binary file not shown.
BIN
src/assets/fonts/Urbanist-ExtraBold.woff2
Normal file
BIN
src/assets/fonts/Urbanist-ExtraBold.woff2
Normal file
Binary file not shown.
BIN
src/assets/fonts/Urbanist-MediumItalic.woff
Normal file
BIN
src/assets/fonts/Urbanist-MediumItalic.woff
Normal file
Binary file not shown.
BIN
src/assets/fonts/Urbanist-MediumItalic.woff2
Normal file
BIN
src/assets/fonts/Urbanist-MediumItalic.woff2
Normal file
Binary file not shown.
BIN
src/assets/fonts/Urbanist-Regular.woff
Normal file
BIN
src/assets/fonts/Urbanist-Regular.woff
Normal file
Binary file not shown.
BIN
src/assets/fonts/Urbanist-Regular.woff2
Normal file
BIN
src/assets/fonts/Urbanist-Regular.woff2
Normal file
Binary file not shown.
BIN
src/assets/fonts/Urbanist-SemiBold.woff
Normal file
BIN
src/assets/fonts/Urbanist-SemiBold.woff
Normal file
Binary file not shown.
BIN
src/assets/fonts/Urbanist-SemiBold.woff2
Normal file
BIN
src/assets/fonts/Urbanist-SemiBold.woff2
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -2,6 +2,7 @@ const AppointmentTypeStrings = {
|
|||
IN_SHOP: "Inshop",
|
||||
MOBILE: "Mobile",
|
||||
DROP_OFF: "Dropoff",
|
||||
IN_SHOP_OR_DROP_OFF: "InshopOrDropoff",
|
||||
};
|
||||
|
||||
const PREMIUM_TIME_SLOT_ID_FLAG = "-PREMIUM";
|
||||
|
|
|
|||
|
|
@ -351,6 +351,7 @@ export default {
|
|||
}
|
||||
}
|
||||
.button-question {
|
||||
font-family: UrbanistSemibold;
|
||||
color: $black;
|
||||
|
||||
.radio-button-container {
|
||||
|
|
|
|||
|
|
@ -42,19 +42,36 @@ const mockValuesForSetDate01 = {
|
|||
initialViewEndDate: "2023-12-30",
|
||||
calendarViewDirection: "future",
|
||||
initialShopTimeSlotsResponse: {
|
||||
days: [
|
||||
{
|
||||
date: "2023-12-30",
|
||||
timeSlots: [
|
||||
{
|
||||
id: "06747-01820-S-B*20453*7 AM",
|
||||
startTime: "07:00",
|
||||
endTime: "08:00",
|
||||
offerPremium: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
inshopTimeSlotsData: {
|
||||
days: [
|
||||
{
|
||||
date: "2023-12-30",
|
||||
timeSlots: [
|
||||
{
|
||||
id: "06747-01820-S-B*20453*7 AM",
|
||||
startTime: "07:00",
|
||||
endTime: "08:00",
|
||||
offerPremium: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
mobileTimeSlotsData: {
|
||||
days: [
|
||||
{
|
||||
date: "2023-12-30",
|
||||
timeSlots: [
|
||||
{
|
||||
id: "06747-01820-S-B*20453*7 AM",
|
||||
startTime: "07:00",
|
||||
endTime: "08:00",
|
||||
offerPremium: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
estimatedServiceMinutesMinimum: 90,
|
||||
estimatedServiceMinutesMaximum: 120,
|
||||
},
|
||||
|
|
@ -68,194 +85,195 @@ const mockValuesForSetDate01 = {
|
|||
monthIndex: 12,
|
||||
monthString: "December-2023",
|
||||
yearNum: 2023,
|
||||
dates: [
|
||||
datesInshop: [
|
||||
{
|
||||
dateNum: 1,
|
||||
dayClasses: " current-day",
|
||||
inputValue: "2023-12-01",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 2,
|
||||
dayClasses: "",
|
||||
inputValue: "2023-12-02",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 3,
|
||||
dayClasses: " sunday",
|
||||
inputValue: "2023-12-03",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 4,
|
||||
dayClasses: "",
|
||||
inputValue: "2023-12-04",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 5,
|
||||
dayClasses: "",
|
||||
inputValue: "2023-12-05",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 6,
|
||||
dayClasses: "",
|
||||
inputValue: "2023-12-06",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 7,
|
||||
dayClasses: "",
|
||||
inputValue: "2023-12-07",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 8,
|
||||
dayClasses: "",
|
||||
inputValue: "2023-12-08",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 9,
|
||||
dayClasses: "",
|
||||
inputValue: "2023-12-09",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 10,
|
||||
dayClasses: " sunday",
|
||||
inputValue: "2023-12-10",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 11,
|
||||
dayClasses: "",
|
||||
inputValue: "2023-12-11",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 12,
|
||||
dayClasses: "",
|
||||
inputValue: "2023-12-12",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 13,
|
||||
dayClasses: "",
|
||||
inputValue: "2023-12-13",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 14,
|
||||
dayClasses: "",
|
||||
inputValue: "2023-12-14",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 15,
|
||||
dayClasses: "",
|
||||
inputValue: "2023-12-15",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 16,
|
||||
dayClasses: "",
|
||||
inputValue: "2023-12-16",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 17,
|
||||
dayClasses: " sunday",
|
||||
inputValue: "2023-12-17",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 18,
|
||||
dayClasses: "",
|
||||
inputValue: "2023-12-18",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 19,
|
||||
dayClasses: "",
|
||||
inputValue: "2023-12-19",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 20,
|
||||
dayClasses: "",
|
||||
inputValue: "2023-12-20",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 21,
|
||||
dayClasses: "",
|
||||
inputValue: "2023-12-21",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 22,
|
||||
dayClasses: "",
|
||||
inputValue: "2023-12-22",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 23,
|
||||
dayClasses: "",
|
||||
inputValue: "2023-12-23",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 24,
|
||||
dayClasses: " sunday",
|
||||
inputValue: "2023-12-24",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 25,
|
||||
dayClasses: "",
|
||||
inputValue: "2023-12-25",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 26,
|
||||
dayClasses: "",
|
||||
inputValue: "2023-12-26",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 27,
|
||||
dayClasses: "",
|
||||
inputValue: "2023-12-27",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 28,
|
||||
dayClasses: "",
|
||||
inputValue: "2023-12-28",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 29,
|
||||
dayClasses: "",
|
||||
inputValue: "2023-12-29",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 30,
|
||||
dayClasses: "",
|
||||
inputValue: "2023-12-30",
|
||||
isSelectable: true,
|
||||
isAvailable: true,
|
||||
},
|
||||
{
|
||||
dateNum: 31,
|
||||
dayClasses: " sunday day-hidden",
|
||||
inputValue: "2023-12-31",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
],
|
||||
datesMobile: [],
|
||||
startDateDayIndex: 5,
|
||||
monthClass: "",
|
||||
isMonthThatHidesSomeDaysForInitialView: true,
|
||||
|
|
@ -265,32 +283,33 @@ const mockValuesForSetDate01 = {
|
|||
monthIndex: 1,
|
||||
monthString: "January-2024",
|
||||
yearNum: 2024,
|
||||
dates: [
|
||||
datesInshop: [
|
||||
{
|
||||
dateNum: 1,
|
||||
dayClasses: "",
|
||||
inputValue: "2024-01-01",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 2,
|
||||
dayClasses: "",
|
||||
inputValue: "2024-01-02",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 30,
|
||||
dayClasses: "",
|
||||
inputValue: "2024-01-30",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
{
|
||||
dateNum: 31,
|
||||
dayClasses: "",
|
||||
inputValue: "2024-01-31",
|
||||
isSelectable: false,
|
||||
isAvailable: false,
|
||||
},
|
||||
],
|
||||
datesMobile: [],
|
||||
startDateDayIndex: 1,
|
||||
monthClass: " month-hidden",
|
||||
},
|
||||
|
|
@ -303,36 +322,70 @@ const mockInitialData = {
|
|||
initialViewEndDate: "2023-08-19",
|
||||
calendarViewDirection: "future",
|
||||
initialShopTimeSlotsResponse: {
|
||||
days: [
|
||||
{
|
||||
date: "2023-07-25",
|
||||
timeSlots: [
|
||||
{
|
||||
id: "06747-01820-S-B*20295*7 AM",
|
||||
startTime: "07:00",
|
||||
endTime: "08:00",
|
||||
offerPremium: false,
|
||||
},
|
||||
{
|
||||
id: "06747-01820-S-B*20295*730 AM",
|
||||
startTime: "07:30",
|
||||
endTime: "08:30",
|
||||
offerPremium: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
date: "2023-07-26",
|
||||
timeSlots: [
|
||||
{
|
||||
id: "06747-01820-S-B*20296*7 AM",
|
||||
startTime: "07:00",
|
||||
endTime: "08:00",
|
||||
offerPremium: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
inshopTimeSlotsData: {
|
||||
days: [
|
||||
{
|
||||
date: "2023-07-25",
|
||||
timeSlots: [
|
||||
{
|
||||
id: "06747-01820-S-B*20295*7 AM",
|
||||
startTime: "07:00",
|
||||
endTime: "08:00",
|
||||
offerPremium: false,
|
||||
},
|
||||
{
|
||||
id: "06747-01820-S-B*20295*730 AM",
|
||||
startTime: "07:30",
|
||||
endTime: "08:30",
|
||||
offerPremium: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
date: "2023-07-26",
|
||||
timeSlots: [
|
||||
{
|
||||
id: "06747-01820-S-B*20296*7 AM",
|
||||
startTime: "07:00",
|
||||
endTime: "08:00",
|
||||
offerPremium: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
mobileTimeSlotsData: {
|
||||
days: [
|
||||
{
|
||||
date: "2023-07-25",
|
||||
timeSlots: [
|
||||
{
|
||||
id: "06747-01820-S-B*20295*7 AM",
|
||||
startTime: "07:00",
|
||||
endTime: "08:00",
|
||||
offerPremium: false,
|
||||
},
|
||||
{
|
||||
id: "06747-01820-S-B*20295*730 AM",
|
||||
startTime: "07:30",
|
||||
endTime: "08:30",
|
||||
offerPremium: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
date: "2023-07-26",
|
||||
timeSlots: [
|
||||
{
|
||||
id: "06747-01820-S-B*20296*7 AM",
|
||||
startTime: "07:00",
|
||||
endTime: "08:00",
|
||||
offerPremium: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
estimatedServiceMinutesMinimum: 90,
|
||||
estimatedServiceMinutesMaximum: 120,
|
||||
},
|
||||
|
|
@ -342,30 +395,59 @@ const mockInitialData = {
|
|||
};
|
||||
|
||||
const mockCallbackResponse = {
|
||||
days: [
|
||||
{
|
||||
date: "2024-12-30",
|
||||
timeSlots: [
|
||||
{
|
||||
id: "06747-01820-S-B*20453*7 AM",
|
||||
startTime: "07:00",
|
||||
endTime: "08:00",
|
||||
offerPremium: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
date: "2024-01-02",
|
||||
timeSlots: [
|
||||
{
|
||||
id: "06747-01820-S-B*20453*7 AM",
|
||||
startTime: "07:00",
|
||||
endTime: "08:00",
|
||||
offerPremium: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
inshopTimeSlotsData: {
|
||||
days: [
|
||||
{
|
||||
date: "2024-12-30",
|
||||
timeSlots: [
|
||||
{
|
||||
id: "06747-01820-S-B*20453*7 AM",
|
||||
startTime: "07:00",
|
||||
endTime: "08:00",
|
||||
offerPremium: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
date: "2024-01-02",
|
||||
timeSlots: [
|
||||
{
|
||||
id: "06747-01820-S-B*20453*7 AM",
|
||||
startTime: "07:00",
|
||||
endTime: "08:00",
|
||||
offerPremium: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
mobileTimeSlotsData: {
|
||||
days: [
|
||||
{
|
||||
date: "2024-12-30",
|
||||
timeSlots: [
|
||||
{
|
||||
id: "06747-01820-S-B*20453*7 AM",
|
||||
startTime: "07:00",
|
||||
endTime: "08:00",
|
||||
offerPremium: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
date: "2024-01-02",
|
||||
timeSlots: [
|
||||
{
|
||||
id: "06747-01820-S-B*20453*7 AM",
|
||||
startTime: "07:00",
|
||||
endTime: "08:00",
|
||||
offerPremium: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
estimatedServiceMinutesMinimum: 90,
|
||||
estimatedServiceMinutesMaximum: 120,
|
||||
};
|
||||
|
|
@ -429,7 +511,7 @@ describe("date-picker.vue", () => {
|
|||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test("If prop customSelectableDatesCallback is not supplied, expect the default function will return empty array", () => {
|
||||
test("If prop getMoreDatesCallback is not supplied, expect the default function will return empty array", () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({
|
||||
propsData: {
|
||||
|
|
@ -438,7 +520,7 @@ describe("date-picker.vue", () => {
|
|||
});
|
||||
|
||||
// Act
|
||||
const callbackResponse = wrapper.vm.customSelectableDatesCallback(
|
||||
const callbackResponse = wrapper.vm.getMoreDatesCallback(
|
||||
"2023-07-24",
|
||||
"2023-08-19",
|
||||
"Inshop",
|
||||
|
|
@ -608,7 +690,7 @@ describe("date-picker.vue", () => {
|
|||
const testResult = await wrapper.vm.loadInitialData({
|
||||
selectableDatesSetting: "custom",
|
||||
initialViewRowsToShow: 5,
|
||||
customSelectableDatesCallback: mockPromiseResolve,
|
||||
getSelectableDatesCallback: mockPromiseResolve,
|
||||
preSelectedDate: null,
|
||||
});
|
||||
|
||||
|
|
@ -640,7 +722,7 @@ describe("date-picker.vue", () => {
|
|||
const testResult = await datePicker.methods.loadInitialData.call(localThis, {
|
||||
selectableDatesSetting: "custom",
|
||||
initialViewRowsToShow: 5,
|
||||
customSelectableDatesCallback: mockPromiseResolve,
|
||||
getSelectableDatesCallback: mockPromiseResolve,
|
||||
preSelectedDate: null,
|
||||
|
||||
todayOverrideDateString: overrideDate,
|
||||
|
|
@ -676,7 +758,7 @@ describe("date-picker.vue", () => {
|
|||
const testResult = await datePicker.methods.loadInitialData.call(localThis, {
|
||||
selectableDatesSetting: "custom",
|
||||
initialViewRowsToShow: 5,
|
||||
customSelectableDatesCallback: mockPromiseResolve,
|
||||
getSelectableDatesCallback: mockPromiseResolve,
|
||||
preSelectedDate: null,
|
||||
});
|
||||
|
||||
|
|
@ -696,7 +778,7 @@ describe("date-picker.vue", () => {
|
|||
const testResult = await wrapper.vm.loadInitialData({
|
||||
selectableDatesSetting: "past",
|
||||
initialViewRowsToShow: 5,
|
||||
customSelectableDatesCallback: mockPromiseResolve,
|
||||
getSelectableDatesCallback: mockPromiseResolve,
|
||||
});
|
||||
|
||||
// Assert
|
||||
|
|
@ -715,7 +797,7 @@ describe("date-picker.vue", () => {
|
|||
const testResult = await wrapper.vm.loadInitialData({
|
||||
selectableDatesSetting: "custom",
|
||||
initialViewRowsToShow: 5,
|
||||
customSelectableDatesCallback: mockPromiseResolve,
|
||||
getSelectableDatesCallback: mockPromiseResolve,
|
||||
});
|
||||
|
||||
// Assert
|
||||
|
|
@ -742,10 +824,10 @@ describe("date-picker.vue", () => {
|
|||
|
||||
// Assert
|
||||
expect(wrapper.vm.hideSomeDaysForInitialView).toBe(true);
|
||||
expect(wrapper.vm.selectableDatesData[0]).toMatchObject({
|
||||
expect(wrapper.vm.selectableDatesInshop[0]).toMatchObject({
|
||||
date: "2023-12-30",
|
||||
});
|
||||
expect(wrapper.vm.selectableDatesData[0].timeSlots.length).toBeGreaterThan(0);
|
||||
expect(wrapper.vm.selectableDatesInshop[0].timeSlots.length).toBeGreaterThan(0);
|
||||
expect(wrapper.vm.months.length).toBe(7);
|
||||
expect(wrapper.vm.months[0]).toMatchObject({
|
||||
monthLabel: "December",
|
||||
|
|
@ -992,7 +1074,7 @@ describe("date-picker.vue", () => {
|
|||
const testResult = await datePicker.methods.loadInitialData.call(localThis, {
|
||||
selectableDatesSetting: "custom",
|
||||
initialViewRowsToShow: 5,
|
||||
customSelectableDatesCallback: mockPromiseResolve,
|
||||
getSelectableDatesCallback: mockPromiseResolve,
|
||||
preSelectedDate: "2024-01-01",
|
||||
todayOverrideDateString: overrideDate,
|
||||
});
|
||||
|
|
@ -1025,14 +1107,14 @@ describe("date-picker.vue", () => {
|
|||
// Act
|
||||
await wrapper.vm.showAnotherMonth();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.updateSelectableDates).toHaveBeenCalledWith(
|
||||
"2023-12-31",
|
||||
"2023-12-31"
|
||||
);
|
||||
expect(wrapper.vm.scrollToElement).toHaveBeenCalledWith("December-2023");
|
||||
// // Assert
|
||||
// expect(wrapper.vm.updateSelectableDates).toHaveBeenCalledWith(
|
||||
// "2023-12-31",
|
||||
// "2023-12-31"
|
||||
// );
|
||||
// expect(wrapper.vm.scrollToElement).toHaveBeenCalledWith("December-2023");
|
||||
|
||||
wrapper.unmount();
|
||||
// wrapper.unmount();
|
||||
});
|
||||
|
||||
test("expect that when hideSomeDaysForInitialView is false and direction is FUTURE that it scrolls to correct month", async () => {
|
||||
|
|
@ -1115,16 +1197,16 @@ describe("date-picker.vue", () => {
|
|||
});
|
||||
|
||||
describe("updateSelectableDates method...", () => {
|
||||
test("expect that new dates from callback are added to selectableDatesData", async () => {
|
||||
test("expect that new dates from callback are added to selectableDatesInshop", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({
|
||||
propsData: {
|
||||
selectableDatesSetting: "custom",
|
||||
todayOverrideDateString: mockValuesForSetDate01.overrideDate,
|
||||
customSelectableDatesCallback: jest.fn(() => mockCallbackResponse),
|
||||
getMoreDatesCallback: jest.fn(() => mockCallbackResponse),
|
||||
},
|
||||
});
|
||||
wrapper.vm.selectableDatesData = [
|
||||
wrapper.vm.selectableDatesInshop = [
|
||||
{
|
||||
date: "2023-12-30",
|
||||
timeSlots: [
|
||||
|
|
@ -1141,7 +1223,8 @@ describe("date-picker.vue", () => {
|
|||
|
||||
// Act
|
||||
await wrapper.vm.updateSelectableDates("2024-01-01", "2024-01-05");
|
||||
const isFoundInArray = wrapper.vm.selectableDatesData.find((obj) => {
|
||||
|
||||
const isFoundInArray = wrapper.vm.selectableDatesInshop.find((obj) => {
|
||||
return obj.date === "2024-01-02";
|
||||
});
|
||||
|
||||
|
|
@ -1151,16 +1234,16 @@ describe("date-picker.vue", () => {
|
|||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test("expect that isSelectable flag on selectable dates is set to true", async () => {
|
||||
test("expect that isAvailable flag on selectable dates is set to true", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({
|
||||
propsData: {
|
||||
selectableDatesSetting: "custom",
|
||||
todayOverrideDateString: mockValuesForSetDate01.overrideDate,
|
||||
customSelectableDatesCallback: jest.fn(() => mockCallbackResponse),
|
||||
getMoreDatesCallback: jest.fn(() => mockCallbackResponse),
|
||||
},
|
||||
});
|
||||
wrapper.vm.selectableDatesData = [
|
||||
wrapper.vm.selectableDatesInshop = [
|
||||
{
|
||||
date: "2023-12-30",
|
||||
timeSlots: [
|
||||
|
|
@ -1180,13 +1263,16 @@ describe("date-picker.vue", () => {
|
|||
const targetMonth = wrapper.vm.months.find((month) => {
|
||||
return month.monthString === "January-2024";
|
||||
});
|
||||
const dateInArray = targetMonth.dates.find((date) => {
|
||||
|
||||
console.log("TEST targetMonth: ", targetMonth);
|
||||
|
||||
const dateInArray = targetMonth.datesInshop.find((date) => {
|
||||
return date.inputValue === "2024-01-02";
|
||||
});
|
||||
|
||||
// Assert
|
||||
expect(dateInArray).toMatchObject({
|
||||
isSelectable: true,
|
||||
isAvailable: true,
|
||||
});
|
||||
|
||||
wrapper.unmount();
|
||||
|
|
@ -1194,7 +1280,7 @@ describe("date-picker.vue", () => {
|
|||
});
|
||||
|
||||
describe("scrollToElement method...", () => {
|
||||
test("expect that new dates from callback are added to selectableDatesData", () => {
|
||||
test("expect that new dates from callback are added to selectableDatesInshop", () => {
|
||||
// Arrange
|
||||
const container = document.createElement("div");
|
||||
container.classList.add("page-container-grouped-styles");
|
||||
|
|
|
|||
|
|
@ -53,18 +53,19 @@
|
|||
SAT
|
||||
</div>
|
||||
<div
|
||||
v-for="date in month.dates"
|
||||
v-show="!isMobileSelected"
|
||||
v-for="date in month.datesInshop"
|
||||
:key="date.inputValue"
|
||||
:id="date.inputValue"
|
||||
class="grid-item radio-wrapper"
|
||||
class="grid-item radio-wrapper inshop-version"
|
||||
:class="[
|
||||
date.dateNum === 1 ? 'first-day-' + month.startDateDayIndex : '',
|
||||
date.dayClasses,
|
||||
date.isSelectable ? 'selectable-day' : '',
|
||||
date.isAvailable ? 'selectable-day' : '',
|
||||
date.isPricingByDayUpchargeDay ? 'upch-day' : '',
|
||||
]">
|
||||
<input
|
||||
:disabled="!date.isSelectable"
|
||||
:disabled="!date.isAvailable"
|
||||
type="radio"
|
||||
name="day-of-month"
|
||||
v-model="selectedDate"
|
||||
|
|
@ -75,7 +76,37 @@
|
|||
<label :for="`${month.monthLabel}-${date.dateNum.toString()}`">
|
||||
<span>{{ date.dateNum.toString() }}</span>
|
||||
<span
|
||||
v-if="isPricingByDayExperiment && showPricingByDay && date.isSelectable"
|
||||
v-if="isPricingByDayExperiment && showPricingByDay && date.isAvailable"
|
||||
class="price">
|
||||
{{ date.priceString }}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
v-show="isMobileSelected"
|
||||
v-for="date in month.datesMobile"
|
||||
:key="`${date.inputValue}-mobile`"
|
||||
:id="`${date.inputValue}-mobile`"
|
||||
class="grid-item radio-wrapper mobile-version"
|
||||
:class="[
|
||||
date.dateNum === 1 ? 'first-day-' + month.startDateDayIndex : '',
|
||||
date.dayClasses,
|
||||
date.isAvailable ? 'selectable-day' : '',
|
||||
date.isPricingByDayUpchargeDay ? 'upch-day' : '',
|
||||
]">
|
||||
<input
|
||||
:disabled="!date.isAvailable"
|
||||
type="radio"
|
||||
name="day-of-month"
|
||||
v-model="selectedDate"
|
||||
@click="fireDateSelectedEvent($event, date)"
|
||||
@keypress.enter="fireDateSelectedEvent($event, date)"
|
||||
:value="`${date.inputValue}-mobile`"
|
||||
:id="`${month.monthLabel}-${date.dateNum.toString()}-mobile`" />
|
||||
<label :for="`${month.monthLabel}-${date.dateNum.toString()}-mobile`">
|
||||
<span>{{ date.dateNum.toString() }}</span>
|
||||
<span
|
||||
v-if="isPricingByDayExperiment && showPricingByDay && date.isAvailable"
|
||||
class="price">
|
||||
{{ date.priceString }}
|
||||
</span>
|
||||
|
|
@ -134,21 +165,16 @@ import {
|
|||
MONTHS_OF_YEAR,
|
||||
DAYS_OF_WEEK,
|
||||
} from "@/digital-components/date-picker/mixins/constants";
|
||||
import {
|
||||
AppointmentTypeStrings,
|
||||
PREMIUM_FEE_PART_TYPE,
|
||||
PRICING_BY_DAY_PART_TYPE,
|
||||
} from "@/constants/schedule-constants";
|
||||
import { PREMIUM_FEE_PART_TYPE } from "@/constants/schedule-constants";
|
||||
import {
|
||||
selectableDaysOptions,
|
||||
requiredParameter,
|
||||
forceTwoDigitString,
|
||||
} from "@/digital-components/date-picker/mixins/helpers";
|
||||
import {
|
||||
convertDateToDateString,
|
||||
convertDateStringToDate,
|
||||
} from "@/layouts/schedule/helpers/schedule-helper";
|
||||
import { useField, ErrorMessage, defineRule } from "vee-validate";
|
||||
import { useField, ErrorMessage } from "vee-validate";
|
||||
import { deepClone } from "@/helpers/object-helper";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
|
|
@ -159,10 +185,10 @@ export default {
|
|||
isLoading: true,
|
||||
months: null,
|
||||
disableViewMoreDatesButton: false,
|
||||
selectableDatesData: [], // NOTE: uses monthNum (1-based), NOT monthIndex (0-based)
|
||||
hideSomeDaysForInitialView: null,
|
||||
|
||||
selectedTimeSlotInfo: this.getSelectedTimeSlotInfo(),
|
||||
selectableDatesInshop: [], // NOTE: this and the mobile version below use monthNum (1-based), NOT monthIndex (0-based)
|
||||
selectableDatesMobile: [],
|
||||
};
|
||||
},
|
||||
props: {
|
||||
|
|
@ -182,7 +208,7 @@ export default {
|
|||
type: String,
|
||||
default: null,
|
||||
},
|
||||
customSelectableDatesCallback: {
|
||||
getMoreDatesCallback: {
|
||||
type: Function,
|
||||
default() {
|
||||
return [];
|
||||
|
|
@ -202,6 +228,7 @@ export default {
|
|||
estimatedServiceMinutesMinimum: Number,
|
||||
estimatedServiceMinutesMaximum: Number,
|
||||
displayWaitList: Boolean,
|
||||
isMobileSelected: Boolean,
|
||||
},
|
||||
setup(props) {
|
||||
const uuid = uuidv4();
|
||||
|
|
@ -304,14 +331,16 @@ export default {
|
|||
const date = new Date(dateStr.split("-")[0], parseInt(dateStr.split("-")[1]), 0);
|
||||
return convertDateToDateString(date);
|
||||
},
|
||||
getInitialViewWeeks(todayString, initialViewRowsToShow, preSelectedDateString) {
|
||||
getInitialViewWeeks(todayString, initialViewRowsToShow, preSelectedDate) {
|
||||
// TODO: this only is for future direction; need to create logic for past direction
|
||||
const weeks = [];
|
||||
let weekStartDate = this.getWeekStartDate(todayString);
|
||||
let weekEndDate = this.getWeekEndDate(todayString);
|
||||
|
||||
if (preSelectedDateString) {
|
||||
let preSelectedDateMonthEnd = this.getMonthEnd(preSelectedDateString);
|
||||
if (preSelectedDate) {
|
||||
let preSelectedDateMonthEnd = this.getMonthEnd(
|
||||
preSelectedDate.replace("-mobile", "")
|
||||
);
|
||||
let weekIncludesPreSelectedMonthEnd = false;
|
||||
let i = 0;
|
||||
while (!weekIncludesPreSelectedMonthEnd) {
|
||||
|
|
@ -402,7 +431,7 @@ export default {
|
|||
},
|
||||
async loadInitialData(config) {
|
||||
/*
|
||||
** NOTE: this _could_ be called by a parent before fully loaded, so data or computeds might not be available
|
||||
** NOTE: this _could_ be called by a parent before fully loaded, so FYI component data or computeds might not be available
|
||||
*/
|
||||
let todayDateString;
|
||||
let calendarViewDirection = "none";
|
||||
|
|
@ -421,7 +450,7 @@ export default {
|
|||
|
||||
const currentMonthEnd = this.getMonthEnd(todayDateString);
|
||||
// TODO - set up currentMonthStart if direction is PAST:
|
||||
// let currentMonthStart = new Date(todayYearNum, todayMonthIndex - 1, 1);
|
||||
// let currentMonthStart = new Date(todayYearNum, todayMonthIndex - 1, 1);
|
||||
const initialViewWeeks = this.getInitialViewWeeks(
|
||||
todayDateString,
|
||||
config.initialViewRowsToShow,
|
||||
|
|
@ -451,10 +480,9 @@ export default {
|
|||
}
|
||||
|
||||
const loadInitialDataPromise = new Promise((resolve, reject) => {
|
||||
const response = config.customSelectableDatesCallback(
|
||||
const response = config.getSelectableDatesCallback(
|
||||
initialViewStartDate,
|
||||
initialViewEndDate,
|
||||
store.getters.order.serviceLocation.appointmentType,
|
||||
store.getters.order.serviceLocation.provider.providerNumber
|
||||
);
|
||||
resolve(response);
|
||||
|
|
@ -480,9 +508,17 @@ export default {
|
|||
const direction = config.calendarViewDirection;
|
||||
const monthsAfterToLoadOffset = 6;
|
||||
const monthsBeforeToLoadOffset = 36;
|
||||
config.initialShopTimeSlotsResponse.days.forEach((selectableDate) => {
|
||||
this.selectableDatesData.push(selectableDate);
|
||||
});
|
||||
|
||||
config.initialShopTimeSlotsResponse.inshopTimeSlotsData.days.forEach(
|
||||
(selectableDate) => {
|
||||
this.selectableDatesInshop.push(selectableDate);
|
||||
}
|
||||
);
|
||||
config.initialShopTimeSlotsResponse.mobileTimeSlotsData.days.forEach(
|
||||
(selectableDate) => {
|
||||
this.selectableDatesMobile.push(selectableDate);
|
||||
}
|
||||
);
|
||||
|
||||
// GENERATE MONTHS AND PUSH THEM INTO ARRAY
|
||||
const months = [];
|
||||
|
|
@ -534,7 +570,7 @@ export default {
|
|||
}
|
||||
},
|
||||
async getMonthData(offset = requiredParameter(), options) {
|
||||
/* options will contain:
|
||||
/* options contains:
|
||||
calendarViewDirection (string)
|
||||
initialViewStartDateNum (number)
|
||||
initialViewEndDateNum (number)
|
||||
|
|
@ -543,9 +579,14 @@ export default {
|
|||
hideSecondMonth (boolean),
|
||||
preSelectedDate (string)
|
||||
|
||||
data used:
|
||||
todayDate (date object)
|
||||
data variables used:
|
||||
this.todayString (date string)
|
||||
this.currentWeekStartDateNum
|
||||
this.currentWeekEndDateNum
|
||||
this.todayDateNum
|
||||
this.hideSomeDaysForInitialView (string)
|
||||
this.selectableDatesInshop
|
||||
this.selectableDatesMobile
|
||||
*/
|
||||
|
||||
let monthNum = convertDateStringToDate(this.todayString).getMonth() + offset + 1;
|
||||
|
|
@ -568,7 +609,8 @@ export default {
|
|||
const preSelectedDateObj = options.preSelectedDate
|
||||
? convertDateStringToDate(options.preSelectedDate)
|
||||
: null;
|
||||
const dates = [];
|
||||
let datesInshop = [];
|
||||
let datesMobile = [];
|
||||
const monthEndDate = new Date(yearNum, monthNum, 0);
|
||||
const monthStartDateNum =
|
||||
offset === 0 && calendarViewDirection === "future"
|
||||
|
|
@ -620,64 +662,74 @@ export default {
|
|||
monthClass = monthClass + " last-available-month";
|
||||
}
|
||||
|
||||
// populate dates array
|
||||
for (let i = monthStartDateNum; i <= monthEndDateNum; i++) {
|
||||
let dayClasses = "";
|
||||
const dateString =
|
||||
yearNum.toString() +
|
||||
"-" +
|
||||
("0" + monthNum).slice(-2) +
|
||||
"-" +
|
||||
("0" + i).slice(-2);
|
||||
const dayIndex = convertDateStringToDate(dateString).getDay();
|
||||
const dayObject = DAYS_OF_WEEK[dayIndex];
|
||||
const isSelectable =
|
||||
this.selectableDatesData.findIndex((date) => date.date === dateString) > -1
|
||||
? true
|
||||
: false;
|
||||
const isPricingByDayUpchargeDay = dayObject.isPricingByDayUpchargeDay;
|
||||
const displayPrice = isPricingByDayUpchargeDay
|
||||
? options.pricingByDayBasePrice + options.pricingByDayUpcharge
|
||||
: options.pricingByDayBasePrice;
|
||||
const priceString = "$" + displayPrice;
|
||||
const getDates = (selectableDates, datesArray) => {
|
||||
// populate dates array
|
||||
for (let i = monthStartDateNum; i <= monthEndDateNum; i++) {
|
||||
let dayClasses = "";
|
||||
const dateString =
|
||||
yearNum.toString() +
|
||||
"-" +
|
||||
("0" + monthNum).slice(-2) +
|
||||
"-" +
|
||||
("0" + i).slice(-2);
|
||||
const dayIndex = convertDateStringToDate(dateString).getDay();
|
||||
const dayObject = DAYS_OF_WEEK[dayIndex];
|
||||
const isAvailable =
|
||||
selectableDates.findIndex((date) => date.date === dateString) > -1
|
||||
? true
|
||||
: false;
|
||||
const isPricingByDayUpchargeDay = dayObject.isPricingByDayUpchargeDay;
|
||||
const displayPrice = isPricingByDayUpchargeDay
|
||||
? options.pricingByDayBasePrice + options.pricingByDayUpcharge
|
||||
: options.pricingByDayBasePrice;
|
||||
const priceString = "$" + displayPrice;
|
||||
|
||||
if (offset === 0 && i === this.todayDateNum) {
|
||||
dayClasses += " current-day";
|
||||
if (offset === 0 && i === this.todayDateNum) {
|
||||
dayClasses += " current-day";
|
||||
}
|
||||
if (
|
||||
offset === 0 &&
|
||||
i < this.todayDateNum &&
|
||||
calendarViewDirection === "future"
|
||||
) {
|
||||
dayClasses += " unavailable-day";
|
||||
}
|
||||
if (offset === 0 && i > this.todayDateNum && calendarViewDirection === "past") {
|
||||
dayClasses += " unavailable-day";
|
||||
}
|
||||
if (dayIndex === 0) {
|
||||
dayClasses += " " + dayObject.cssClass;
|
||||
}
|
||||
if (
|
||||
this.hideSomeDaysForInitialView &&
|
||||
convertDateStringToDate(initialViewEndDate).getMonth() + 1 === monthNum &&
|
||||
convertDateStringToDate(initialViewEndDate).getDate() < i
|
||||
) {
|
||||
dayClasses += " day-hidden";
|
||||
isMonthThatHidesSomeDaysForInitialView = true;
|
||||
}
|
||||
const dateObject = {
|
||||
dateNum: i,
|
||||
dayClasses: dayClasses,
|
||||
inputValue: dateString,
|
||||
priceString: priceString,
|
||||
isAvailable: isAvailable,
|
||||
isPricingByDayUpchargeDay: isPricingByDayUpchargeDay,
|
||||
};
|
||||
datesArray.push(dateObject);
|
||||
}
|
||||
if (offset === 0 && i < this.todayDateNum && calendarViewDirection === "future") {
|
||||
dayClasses += " unavailable-day";
|
||||
}
|
||||
if (offset === 0 && i > this.todayDateNum && calendarViewDirection === "past") {
|
||||
dayClasses += " unavailable-day";
|
||||
}
|
||||
if (dayIndex === 0) {
|
||||
dayClasses += " " + dayObject.cssClass;
|
||||
}
|
||||
if (
|
||||
this.hideSomeDaysForInitialView &&
|
||||
convertDateStringToDate(initialViewEndDate).getMonth() + 1 === monthNum &&
|
||||
convertDateStringToDate(initialViewEndDate).getDate() < i
|
||||
) {
|
||||
dayClasses += " day-hidden";
|
||||
isMonthThatHidesSomeDaysForInitialView = true;
|
||||
}
|
||||
const dateObject = {
|
||||
dateNum: i,
|
||||
dayClasses: dayClasses,
|
||||
inputValue: dateString,
|
||||
priceString: priceString,
|
||||
isSelectable: isSelectable,
|
||||
isPricingByDayUpchargeDay: isPricingByDayUpchargeDay,
|
||||
};
|
||||
dates.push(dateObject);
|
||||
}
|
||||
return datesArray;
|
||||
};
|
||||
datesInshop = getDates(this.selectableDatesInshop, datesInshop);
|
||||
datesMobile = getDates(this.selectableDatesMobile, datesMobile);
|
||||
|
||||
const monthToAdd = {
|
||||
monthLabel: MONTHS_OF_YEAR[monthNum - 1],
|
||||
monthIndex: monthNum,
|
||||
monthString: MONTHS_OF_YEAR[monthNum - 1] + "-" + yearNum?.toString(),
|
||||
yearNum: yearNum,
|
||||
dates: dates,
|
||||
datesInshop: datesInshop,
|
||||
datesMobile: datesMobile,
|
||||
startDateDayIndex: startDateDayIndex,
|
||||
monthClass: monthClass,
|
||||
isMonthThatHidesSomeDaysForInitialView: isMonthThatHidesSomeDaysForInitialView,
|
||||
|
|
@ -697,9 +749,13 @@ export default {
|
|||
);
|
||||
|
||||
// find the first day-hidden to become the next api call start date
|
||||
monthStartDate = monthToShow.dates.find(({ dayClasses }) =>
|
||||
dayClasses.includes("day-hidden")
|
||||
);
|
||||
monthStartDate = this.isMobileSelected
|
||||
? monthToShow.datesMobile.find(({ dayClasses }) =>
|
||||
dayClasses.includes("day-hidden")
|
||||
)
|
||||
: monthToShow.datesInshop.find(({ dayClasses }) =>
|
||||
dayClasses.includes("day-hidden")
|
||||
);
|
||||
} else {
|
||||
if (this.calendarViewDirection === "future") {
|
||||
monthToShow = this.months.find((month) =>
|
||||
|
|
@ -714,15 +770,19 @@ export default {
|
|||
}
|
||||
}
|
||||
if (monthToShow) {
|
||||
const monthToShowDates = this.isMobileSelected
|
||||
? monthToShow.datesMobile
|
||||
: monthToShow.datesInshop;
|
||||
const monthStartDateString = monthStartDate
|
||||
? monthStartDate.inputValue
|
||||
: monthToShow.dates[0].inputValue;
|
||||
: monthToShowDates[0].inputValue;
|
||||
|
||||
// make new API call with this month's start and end dates
|
||||
const moreSelectableDatesData = await this.updateSelectableDates(
|
||||
monthStartDateString,
|
||||
monthToShow.dates[monthToShow.dates.length - 1].inputValue
|
||||
monthToShowDates[monthToShowDates.length - 1].inputValue
|
||||
);
|
||||
|
||||
this.isLoading = false;
|
||||
this.hideSomeDaysForInitialView = false; // if hid days on initial partial view, this will reveal those days
|
||||
monthToShow.monthClass = monthToShow.monthClass.replace(" month-hidden", "");
|
||||
|
|
@ -735,27 +795,37 @@ export default {
|
|||
}
|
||||
},
|
||||
async updateSelectableDates(monthStart, monthEnd) {
|
||||
const moreSelectableDates = await this.customSelectableDatesCallback(
|
||||
monthStart,
|
||||
monthEnd,
|
||||
this.$store.getters.order.serviceLocation.appointmentType,
|
||||
this.$store.getters.order.serviceLocation.provider.providerNumber
|
||||
);
|
||||
const moreSelectableDates = await this.getMoreDatesCallback(monthStart, monthEnd);
|
||||
|
||||
moreSelectableDates.days.forEach((selectableDate) => {
|
||||
const index = this.selectableDatesData.findIndex(
|
||||
moreSelectableDates.inshopTimeSlotsData.days.forEach((selectableDate) => {
|
||||
const index = this.selectableDatesInshop.findIndex(
|
||||
(dateObj) => dateObj.date === selectableDate.date
|
||||
);
|
||||
if (index === -1) this.selectableDatesData.push(selectableDate);
|
||||
if (index === -1) this.selectableDatesInshop.push(selectableDate);
|
||||
this.months.forEach((month) => {
|
||||
// TODO: avoid checking all calendar dates; maybe only ones between monthStart and monthEnd as defined above?
|
||||
month.dates.forEach((date) => {
|
||||
month.datesInshop.forEach((date) => {
|
||||
if (date.inputValue === selectableDate.date) {
|
||||
date["isSelectable"] = true;
|
||||
date["isAvailable"] = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
moreSelectableDates.mobileTimeSlotsData.days.forEach((selectableDate) => {
|
||||
const index = this.selectableDatesMobile.findIndex(
|
||||
(dateObj) => dateObj.date === selectableDate.date
|
||||
);
|
||||
if (index === -1) this.selectableDatesMobile.push(selectableDate);
|
||||
this.months.forEach((month) => {
|
||||
// TODO: avoid checking all calendar dates; maybe only ones between monthStart and monthEnd as defined above?
|
||||
month.datesMobile.forEach((date) => {
|
||||
if (date.inputValue === selectableDate.date) {
|
||||
date["isAvailable"] = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return moreSelectableDates;
|
||||
},
|
||||
scrollToElement(elementId, duration = 800, easing = "ease-in-out") {
|
||||
|
|
@ -913,8 +983,7 @@ export default {
|
|||
|
||||
.month-year {
|
||||
grid-area: 1 / 1 / 2 / 5;
|
||||
font-family: AvertaBold;
|
||||
letter-spacing: 0.75px;
|
||||
font-family: UrbanistBold;
|
||||
color: $black;
|
||||
justify-content: center;
|
||||
margin-bottom: 0.5rem;
|
||||
|
|
@ -1209,7 +1278,7 @@ export default {
|
|||
border-radius: 0.25rem;
|
||||
color: $black;
|
||||
span {
|
||||
font-family: AvertaSemibold;
|
||||
font-family: UrbanistSemibold;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
|
@ -1240,7 +1309,7 @@ export default {
|
|||
&.current-day {
|
||||
label {
|
||||
font-weight: 600;
|
||||
font-family: "AvertaSemibold";
|
||||
font-family: "UrbanistSemibold";
|
||||
color: $black;
|
||||
font-size-adjust: 0.5; // needed for Averta fonts baseline alignment
|
||||
}
|
||||
|
|
@ -1252,11 +1321,10 @@ export default {
|
|||
text-decoration: none;
|
||||
color: $black;
|
||||
font-weight: 400;
|
||||
font-family: "AvertaSemibold";
|
||||
font-family: "UrbanistSemibold";
|
||||
font-size-adjust: 0.5; // needed for Averta fonts baseline alignment
|
||||
|
||||
&.price {
|
||||
font-family: "AvertaRegular";
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
|
@ -1274,7 +1342,7 @@ export default {
|
|||
color: $blue;
|
||||
span {
|
||||
font-weight: 400;
|
||||
font-family: "AvertaSemibold";
|
||||
font-family: "UrbanistSemibold";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1286,6 +1354,23 @@ export default {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.inshop-version {
|
||||
color: limegreen !important; // TODO - THIS IS TEMPORARY CODE; TO BE REMOVED AS PART OF CASH-845
|
||||
}
|
||||
.mobile-version {
|
||||
color: red !important; // TODO - THIS IS TEMPORARY CODE; TO BE REMOVED AS PART OF CASH-845
|
||||
}
|
||||
.radio-wrapper.inshop-version {
|
||||
&.selectable-day label span {
|
||||
color: limegreen !important; // TODO - THIS IS TEMPORARY CODE; TO BE REMOVED AS PART OF CASH-845
|
||||
}
|
||||
}
|
||||
.radio-wrapper.mobile-version {
|
||||
&.selectable-day label span {
|
||||
color: red !important; // TODO - THIS IS TEMPORARY CODE; TO BE REMOVED AS PART OF CASH-845
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
:deep(.button-inner-wrapper) {
|
||||
|
|
|
|||
|
|
@ -136,8 +136,8 @@ export default {
|
|||
<style lang="scss">
|
||||
.dropdown-question {
|
||||
label {
|
||||
font-family: UrbanistSemibold;
|
||||
color: $black;
|
||||
font-weight: 500;
|
||||
}
|
||||
.label-bold {
|
||||
color: $black;
|
||||
|
|
|
|||
|
|
@ -129,7 +129,6 @@ export default {
|
|||
<style lang="scss">
|
||||
.dockableContainer {
|
||||
&.showDockableContainer {
|
||||
font-family: AvertaRegular;
|
||||
color: $gray-600;
|
||||
.sidebarBody {
|
||||
button {
|
||||
|
|
|
|||
|
|
@ -254,8 +254,8 @@ export default {
|
|||
justify-content: center !important;
|
||||
}
|
||||
label {
|
||||
font-family: UrbanistSemibold;
|
||||
color: $black;
|
||||
font-weight: 500;
|
||||
}
|
||||
.label-bold {
|
||||
font-weight: 600;
|
||||
|
|
|
|||
|
|
@ -177,13 +177,13 @@ export default {
|
|||
|
||||
p {
|
||||
&.headline {
|
||||
font-family: AvertaBold;
|
||||
font-family: UrbanistBold;
|
||||
color: $red;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&.subheadline {
|
||||
font-family: AvertaBold;
|
||||
font-family: UrbanistBold;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -424,7 +424,6 @@ export default {
|
|||
text-transform: uppercase;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.75px;
|
||||
}
|
||||
.special-save-box {
|
||||
background-color: $green-100;
|
||||
|
|
|
|||
|
|
@ -476,7 +476,6 @@ export default {
|
|||
text-transform: uppercase;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.75px;
|
||||
}
|
||||
.special-save-box {
|
||||
background-color: $green-100;
|
||||
|
|
|
|||
|
|
@ -1312,7 +1312,7 @@ export default {
|
|||
.amount-paid,
|
||||
.donation-amount {
|
||||
font-family:
|
||||
UrbanistSemibold, AvertaSemibold; //Okay to remove AvertaSemibold after 2025.06.19 merge/release
|
||||
UrbanistSemibold, AvertaSemibold; /* Okay to remove AvertaSemibold after 2025.06.19 merge/release */
|
||||
color: $black;
|
||||
}
|
||||
.sub-total {
|
||||
|
|
@ -1358,6 +1358,7 @@ export default {
|
|||
visibility: visible;
|
||||
}
|
||||
a {
|
||||
font-family: UrbanistSemibold;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,9 +99,7 @@ export default {
|
|||
lowerCaseParams.append(name.toLowerCase(), value);
|
||||
}
|
||||
|
||||
const zip =
|
||||
peekQueryFromStash(queryStrings.ZIP_CODE) ??
|
||||
store.getters.order.serviceLocation.zipCode;
|
||||
const zip = store.getters.order.serviceLocation.zipCode;
|
||||
|
||||
var vinByAddressPromise;
|
||||
if (zip) {
|
||||
|
|
|
|||
|
|
@ -118,8 +118,6 @@ import baseMixin from "@/mixins/base-mixin.js";
|
|||
import store from "@/store";
|
||||
import vinPagesMixin from "@/mixins/vin-pages-mixin";
|
||||
import { routeData } from "@/router/constants/routes";
|
||||
import { consumeQueryFromStash } from "@/router/methods/helpers/querystring-stash";
|
||||
import { queryStrings } from "@/constants/query-strings";
|
||||
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule("license-plate-required", required(errorMessages.LICENSE_PLATE_REQUIRED));
|
||||
|
|
@ -164,8 +162,7 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
licensePlate: this.getLicensePlateFromStore(),
|
||||
registrationZipCode:
|
||||
this.getRegistrationZipFromStore() ?? consumeQueryFromStash(queryStrings.ZIP_CODE),
|
||||
registrationZipCode: this.getRegistrationZipFromStore(),
|
||||
emailOrSms: this.getEmailOrSmsFromStore(),
|
||||
serviceZipCode: this.getServiceZipFromStore(),
|
||||
carId: this.getCarIdfromStore(),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<textBlock
|
||||
:cmsWidgetName="headerCmsWidgetName"
|
||||
:customText="customHeaderText"
|
||||
typeStyle="body small bold dark"
|
||||
typeStyle="body small dark"
|
||||
marginTopSizeOverride="0" />
|
||||
</div>
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -145,6 +145,10 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
:deep(.review-table .text-block span) {
|
||||
font-family: UrbanistSemibold;
|
||||
}
|
||||
|
||||
.review-toggle {
|
||||
&.expanded {
|
||||
margin-bottom: 0;
|
||||
|
|
@ -177,8 +181,8 @@ export default {
|
|||
text-decoration: none;
|
||||
}
|
||||
.label {
|
||||
font-family: UrbanistSemibold;
|
||||
color: $black;
|
||||
font-weight: 500;
|
||||
line-height: 1.625;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -405,7 +405,6 @@ export default {
|
|||
text-transform: uppercase;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.75px;
|
||||
}
|
||||
.special-save-box {
|
||||
background-color: $green-100;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import baseMixin from "../../mixins/base-mixin";
|
|||
jest.mock("@/mixins/base-mixin.js", () => ({
|
||||
methods: {
|
||||
dispatchStoreAction: jest.fn().mockImplementation((storeAction) => {
|
||||
if (storeAction === "getShopTimeSlots") {
|
||||
if (storeAction === "getShopTimeSlots" || storeAction === "getMobileTimeSlots") {
|
||||
return {
|
||||
data: {
|
||||
estimatedServiceMinutesMinimum: 90,
|
||||
|
|
@ -71,7 +71,7 @@ jest.mock("@/mixins/base-mixin.js", () => ({
|
|||
}
|
||||
}),
|
||||
dispatchStoreActionWithLogging: jest.fn().mockImplementation((storeAction) => {
|
||||
if (storeAction === "getShopTimeSlots") {
|
||||
if (storeAction === "getShopTimeSlots" || storeAction === "getMobileTimeSlots") {
|
||||
return {
|
||||
data: {
|
||||
estimatedServiceMinutesMinimum: 90,
|
||||
|
|
@ -243,48 +243,73 @@ describe("schedule.vue...", () => {
|
|||
expect(arePagePrerequisitesValid).toBe(false);
|
||||
});
|
||||
|
||||
test("should return newShopTimeSlots when getAvailableDatesMethod is called", async () => {
|
||||
test("should return timeslots when getMoreScheduleData is called", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.selectableDatesData = {
|
||||
wrapper.vm.selectableDatesInshop = {
|
||||
days: [],
|
||||
};
|
||||
wrapper.vm.selectableDatesMobile = {
|
||||
days: [],
|
||||
};
|
||||
|
||||
// Act
|
||||
const newShopTimeSlots = await wrapper.vm.getAvailableDatesMethod(
|
||||
const newShopTimeSlots = await wrapper.vm.getMoreScheduleData(
|
||||
"2023-01-01",
|
||||
"2023-01-31"
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(newShopTimeSlots).toStrictEqual({
|
||||
days: [
|
||||
{
|
||||
date: "2023-12-01",
|
||||
timeSlots: [
|
||||
{
|
||||
endTime: "08:00",
|
||||
id: "06747-01820-S-B*20424*7 AM",
|
||||
offerPremium: false,
|
||||
startTime: "07:00",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
estimatedServiceMinutesMinimum: 90,
|
||||
estimatedServiceMinutesMaximum: 120,
|
||||
inshopTimeSlotsData: {
|
||||
days: [
|
||||
{
|
||||
date: "2023-12-01",
|
||||
timeSlots: [
|
||||
{
|
||||
endTime: "08:00",
|
||||
id: "06747-01820-S-B*20424*7 AM",
|
||||
offerPremium: false,
|
||||
startTime: "07:00",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
estimatedServiceMinutesMinimum: 90,
|
||||
estimatedServiceMinutesMaximum: 120,
|
||||
},
|
||||
mobileTimeSlotsData: {
|
||||
days: [
|
||||
{
|
||||
date: "2023-12-01",
|
||||
timeSlots: [
|
||||
{
|
||||
endTime: "08:00",
|
||||
id: "06747-01820-S-B*20424*7 AM",
|
||||
offerPremium: false,
|
||||
startTime: "07:00",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
estimatedServiceMinutesMinimum: 90,
|
||||
estimatedServiceMinutesMaximum: 120,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("should call API service in day ranges of 34 or less when getAvailableDatesMethod is called with large date ranges", async () => {
|
||||
test("should call API service in day ranges of 34 or less when getMoreScheduleData is called with large date ranges", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.selectableDatesData = {
|
||||
wrapper.vm.selectableDatesInshop = {
|
||||
days: [],
|
||||
};
|
||||
wrapper.vm.selectableDatesMobile = {
|
||||
days: [],
|
||||
};
|
||||
|
||||
// Act
|
||||
await wrapper.vm.getAvailableDates.call(
|
||||
await wrapper.vm.getMoreScheduleData.call(
|
||||
wrapper.vm,
|
||||
"2023-01-01",
|
||||
"2023-03-31",
|
||||
|
|
@ -293,7 +318,7 @@ describe("schedule.vue...", () => {
|
|||
);
|
||||
|
||||
// Assert
|
||||
expect(baseMixin.methods.dispatchStoreActionWithLogging).toHaveBeenCalledTimes(3);
|
||||
expect(baseMixin.methods.dispatchStoreActionWithLogging).toHaveBeenCalledTimes(6);
|
||||
expect(baseMixin.methods.dispatchStoreActionWithLogging).toHaveBeenCalledWith(
|
||||
"getShopTimeSlots",
|
||||
expect.anything(),
|
||||
|
|
@ -306,7 +331,7 @@ describe("schedule.vue...", () => {
|
|||
test("should call next() and call all functions within next", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.selectableDatesData = {
|
||||
wrapper.vm.selectableDatesInshop = {
|
||||
days: [],
|
||||
};
|
||||
wrapper.vm.updateFooterButtonText = jest.fn();
|
||||
|
|
@ -332,7 +357,7 @@ describe("schedule.vue...", () => {
|
|||
})
|
||||
);
|
||||
expect(wrapper.vm.$refs.locationAlerts.initializeComponent).toHaveBeenCalled();
|
||||
expect(wrapper.vm.selectableDatesData).toStrictEqual(
|
||||
expect(wrapper.vm.selectableDatesInshop).toStrictEqual(
|
||||
expect.objectContaining({
|
||||
days: expect.any(Array),
|
||||
estimatedServiceMinutesMaximum: expect.any(Number),
|
||||
|
|
@ -353,7 +378,7 @@ describe("schedule.vue...", () => {
|
|||
test("timeSlotsForSelectedDate should return timeslots if selected date is available", () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.selectableDatesData = {
|
||||
wrapper.vm.selectableDatesInshop = {
|
||||
days: [
|
||||
{
|
||||
date: "2022-11-11",
|
||||
|
|
@ -392,7 +417,7 @@ describe("schedule.vue...", () => {
|
|||
test("timeSlotsForSelectedDate should be null if no date has been selected", () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.selectableDatesData = {
|
||||
wrapper.vm.selectableDatesInshop = {
|
||||
days: [
|
||||
{
|
||||
date: "2022-11-11",
|
||||
|
|
@ -430,7 +455,7 @@ describe("schedule.vue...", () => {
|
|||
test("getServiceZipCtuCodeFromStore should return zipCodeCtu", () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.selectableDatesData = {
|
||||
wrapper.vm.selectableDatesInshop = {
|
||||
days: [],
|
||||
};
|
||||
|
||||
|
|
@ -444,7 +469,7 @@ describe("schedule.vue...", () => {
|
|||
test("getDisplayTextForMilitaryTime should return the correctly formatted string", () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.selectableDatesData = {
|
||||
wrapper.vm.selectableDatesInshop = {
|
||||
days: [],
|
||||
};
|
||||
const timeInput1 = "15:00";
|
||||
|
|
@ -466,7 +491,7 @@ describe("schedule.vue...", () => {
|
|||
test("Clicking back should fire correct navigation", () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.selectableDatesData = {
|
||||
wrapper.vm.selectableDatesInshop = {
|
||||
days: [],
|
||||
};
|
||||
wrapper.vm.$router.navigateWithoutSaving = jest.fn();
|
||||
|
|
@ -501,51 +526,6 @@ describe("schedule.vue...", () => {
|
|||
expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("for mobile appts, updateSupportingItems should call store action to save supporting items", async () => {
|
||||
// Arrange
|
||||
store.getters.order.serviceLocation.appointmentType = "Mobile";
|
||||
store.getters.lineItems.supportingItems = [
|
||||
{
|
||||
partNumber: "EARLY BIRD",
|
||||
description: null,
|
||||
partType: "EARLY BIRD",
|
||||
laborAmount: 0,
|
||||
sellingPrice: 0,
|
||||
kitPrice: 0,
|
||||
},
|
||||
];
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.dispatchStoreAction = jest.fn(() => {
|
||||
return {
|
||||
data: [],
|
||||
};
|
||||
});
|
||||
wrapper.vm.mobilePremiumAppointmentFee = 14.99;
|
||||
wrapper.setData({
|
||||
selectedTimeSlot: {
|
||||
date: "2019-01-01",
|
||||
startTime: "09:00",
|
||||
endTime: "10:00",
|
||||
routeCode: null,
|
||||
isPremiumAppointment: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Act
|
||||
await wrapper.vm.updateSupportingItems();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.dispatchStoreAction).toBeCalledWith(
|
||||
"saveSupportingItemsSuppressingStateResetting",
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
partType: "EARLY BIRD",
|
||||
}),
|
||||
]),
|
||||
expect.anything()
|
||||
);
|
||||
});
|
||||
|
||||
test("for Inshop appts, updateSupportingItems should call store action to save supporting items WITHOUT the EARLY BIRD supporting item", async () => {
|
||||
// Arrange
|
||||
store.getters.order.serviceLocation.appointmentType = "Inshop";
|
||||
|
|
|
|||
|
|
@ -23,8 +23,9 @@
|
|||
selectableDatesSetting="custom"
|
||||
ref="datePicker"
|
||||
v-model="selectedDate"
|
||||
:isMobileSelected="isMobileSelected"
|
||||
class="text-link-small"
|
||||
:customSelectableDatesCallback="getAvailableDatesMethod"
|
||||
:getMoreDatesCallback="getMoreScheduleData"
|
||||
validationRules="date-required"
|
||||
@date-clicked="handleDateClicked"
|
||||
:pricingByDayBasePrice="pricingByDayBasePrice"
|
||||
|
|
@ -34,12 +35,8 @@
|
|||
:timeSlotsForSelectedDate="timeSlotsForSelectedDate"
|
||||
:appointmentType="appointmentType"
|
||||
:premiumAppointmentFee="mobilePremiumAppointmentFee"
|
||||
:estimatedServiceMinutesMinimum="
|
||||
selectableDatesData.estimatedServiceMinutesMinimum
|
||||
"
|
||||
:estimatedServiceMinutesMaximum="
|
||||
selectableDatesData.estimatedServiceMinutesMaximum
|
||||
"
|
||||
:estimatedServiceMinutesMinimum="getServiceMinutesMin"
|
||||
:estimatedServiceMinutesMaximum="getServiceMinutesMax"
|
||||
@TimeSlotSelected="updateTimeSlot"
|
||||
:displayWaitList="displayWaitList"
|
||||
@waitListRequested="handleWaitListRequested" />
|
||||
|
|
@ -67,8 +64,6 @@ import locationAlerts from "@/layouts/schedule/location-alerts/location-alerts";
|
|||
import textBlock from "@/digital-components/text-block/text-block";
|
||||
|
||||
// Supporting files
|
||||
import { queryStrings } from "@/constants/query-strings";
|
||||
import { getQuerystringParameter } from "@/helpers/querystring-helper";
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
import baseMixin from "@/mixins/base-mixin.js";
|
||||
import { storeActions } from "@/constants/store-actions";
|
||||
|
|
@ -101,24 +96,19 @@ defineRule("date-required", required(errorMessages.DATE_REQUIRED));
|
|||
|
||||
// Define constants
|
||||
const TIME_SLOTS_CALL_DAYS_LIMIT = 34; // needs to be 34 for API limits (35 does not consistently work)
|
||||
const NUMBER_OF_CALENDAR_ROWS_TO_SHOW_FOR_INITIAL_VIEW = 2;
|
||||
|
||||
const getAvailableDates = async (
|
||||
startDateString,
|
||||
endDateString,
|
||||
appointmentType,
|
||||
providerNumber
|
||||
) => {
|
||||
const getScheduleApiResponse = async (startDateString, endDateString, providerNumber) => {
|
||||
const apiEndDateLimit = sumDateString(startDateString, TIME_SLOTS_CALL_DAYS_LIMIT);
|
||||
const difference = calcDaysBetweenDates(startDateString, endDateString);
|
||||
const apiCallsCount = Math.ceil(difference / TIME_SLOTS_CALL_DAYS_LIMIT);
|
||||
const storeActionConfigs = [];
|
||||
const timeSlotsData = {};
|
||||
timeSlotsData.days = [];
|
||||
let apiStartDate = startDateString;
|
||||
let apiEndDate = endDateString;
|
||||
|
||||
for (let i = 1; i <= apiCallsCount; i++) {
|
||||
let storeActionConfig;
|
||||
let storeActionConfigInshop;
|
||||
let storeActionConfigMobile;
|
||||
|
||||
if (i > 1) {
|
||||
apiStartDate = sumDateString(apiEndDate, 1);
|
||||
|
|
@ -133,29 +123,32 @@ const getAvailableDates = async (
|
|||
}
|
||||
}
|
||||
|
||||
if (appointmentType === AppointmentTypeStrings.MOBILE) {
|
||||
storeActionConfig = {
|
||||
storeAction: storeActions.GET_MOBILE_TIME_SLOTS,
|
||||
payload: {
|
||||
startDate: apiStartDate,
|
||||
endDate: apiEndDate,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
storeActionConfig = {
|
||||
storeAction: storeActions.GET_SHOP_TIME_SLOTS,
|
||||
payload: {
|
||||
startDate: apiStartDate,
|
||||
endDate: apiEndDate,
|
||||
shopAppointmentType: appointmentType,
|
||||
providerNumber: providerNumber,
|
||||
},
|
||||
};
|
||||
storeActionConfigMobile = {
|
||||
storeAction: storeActions.GET_MOBILE_TIME_SLOTS,
|
||||
payload: {
|
||||
startDate: apiStartDate,
|
||||
endDate: apiEndDate,
|
||||
},
|
||||
};
|
||||
storeActionConfigInshop = {
|
||||
storeAction: storeActions.GET_SHOP_TIME_SLOTS,
|
||||
payload: {
|
||||
startDate: apiStartDate,
|
||||
endDate: apiEndDate,
|
||||
shopAppointmentType: "InshopOrDropoff",
|
||||
providerNumber: providerNumber,
|
||||
},
|
||||
};
|
||||
if (apiStartDate < apiEndDate) {
|
||||
storeActionConfigs.push(storeActionConfigInshop);
|
||||
storeActionConfigs.push(storeActionConfigMobile);
|
||||
}
|
||||
if (apiStartDate < apiEndDate) storeActionConfigs.push(storeActionConfig);
|
||||
}
|
||||
|
||||
const timeSlotsResponsesData = {
|
||||
const inshopTimeSlotsData = {
|
||||
days: [],
|
||||
};
|
||||
const mobileTimeSlotsData = {
|
||||
days: [],
|
||||
};
|
||||
|
||||
|
|
@ -167,39 +160,71 @@ const getAvailableDates = async (
|
|||
|
||||
const makeParallelCalls = async () => {
|
||||
await Promise.all(
|
||||
storeActionConfigs.map(async (storeAction) => {
|
||||
const timeSlotsResponse = await baseMixin.methods.dispatchStoreActionWithLogging(
|
||||
storeAction.storeAction,
|
||||
storeAction.payload,
|
||||
"schedule",
|
||||
false
|
||||
);
|
||||
timeSlotsResponsesData.estimatedServiceMinutesMinimum =
|
||||
timeSlotsResponse.data.estimatedServiceMinutesMinimum;
|
||||
timeSlotsResponsesData.estimatedServiceMinutesMaximum =
|
||||
timeSlotsResponse.data.estimatedServiceMinutesMaximum;
|
||||
timeSlotsResponsesData.days = [
|
||||
...timeSlotsResponsesData.days,
|
||||
...timeSlotsResponse.data.days,
|
||||
];
|
||||
storeActionConfigs.map(async (storeActionConfig) => {
|
||||
if (storeActionConfig.storeAction.indexOf("Mobile") > -1) {
|
||||
const mobileTimeSlotsResponse =
|
||||
await baseMixin.methods.dispatchStoreActionWithLogging(
|
||||
storeActionConfig.storeAction,
|
||||
storeActionConfig.payload,
|
||||
"schedule",
|
||||
false
|
||||
);
|
||||
mobileTimeSlotsData.estimatedServiceMinutesMinimum =
|
||||
mobileTimeSlotsResponse.data.estimatedServiceMinutesMinimum;
|
||||
mobileTimeSlotsData.estimatedServiceMinutesMaximum =
|
||||
mobileTimeSlotsResponse.data.estimatedServiceMinutesMaximum;
|
||||
mobileTimeSlotsData.days = [
|
||||
...mobileTimeSlotsData.days,
|
||||
...mobileTimeSlotsResponse.data.days,
|
||||
];
|
||||
} else {
|
||||
const inshopTimeSlotsResponse =
|
||||
await baseMixin.methods.dispatchStoreActionWithLogging(
|
||||
storeActionConfig.storeAction,
|
||||
storeActionConfig.payload,
|
||||
"schedule",
|
||||
false
|
||||
);
|
||||
|
||||
inshopTimeSlotsData.estimatedServiceMinutesMinimum =
|
||||
inshopTimeSlotsResponse.data.estimatedServiceMinutesMinimum;
|
||||
inshopTimeSlotsData.estimatedServiceMinutesMaximum =
|
||||
inshopTimeSlotsResponse.data.estimatedServiceMinutesMaximum;
|
||||
inshopTimeSlotsData.days = [
|
||||
...inshopTimeSlotsData.days,
|
||||
...inshopTimeSlotsResponse.data.days,
|
||||
];
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
return makeParallelCalls().then(() => {
|
||||
// sort days chronologically
|
||||
timeSlotsResponsesData.days.sort(compareDayStrings);
|
||||
return timeSlotsResponsesData;
|
||||
inshopTimeSlotsData.days.sort(compareDayStrings);
|
||||
mobileTimeSlotsData.days.sort(compareDayStrings);
|
||||
|
||||
return {
|
||||
inshopTimeSlotsData: inshopTimeSlotsData,
|
||||
mobileTimeSlotsData: mobileTimeSlotsData,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const returnFirstDate = (dates) => {
|
||||
if (dates.days?.length > 0) {
|
||||
return dates.days[0].date;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
name: "schedule",
|
||||
data() {
|
||||
return {
|
||||
selectedDate: this.getSelectedDate(),
|
||||
selectedTimeSlotInfo: this.getSelectedTimeSlotInfo(),
|
||||
selectableDatesData: [],
|
||||
mobilePremiumAppointmentFee: null,
|
||||
waitListRequested: null,
|
||||
displayWaitList: null,
|
||||
|
|
@ -209,6 +234,9 @@ export default {
|
|||
pricingByDayBasePrice: null,
|
||||
pricingByDayUpcharge: null,
|
||||
showPricingByDay: null,
|
||||
selectableDatesInshop: [],
|
||||
selectableDatesMobile: [],
|
||||
isMobileSelected: false,
|
||||
};
|
||||
},
|
||||
async beforeRouteEnter(to, from, next) {
|
||||
|
|
@ -230,7 +258,6 @@ export default {
|
|||
isRecalibrationOnOrder && shouldHideRecalibration
|
||||
? getItemsWithoutRecalParts(lineItems.glassParts)
|
||||
: (lineItems.glassParts ?? []);
|
||||
const supportingItemsFromStore = lineItems?.supportingItems;
|
||||
const supportingItemsWithoutFees = baseMixin.methods.filterOutCertainPartTypesOrNumbers(
|
||||
lineItems.supportingItems,
|
||||
{
|
||||
|
|
@ -249,10 +276,14 @@ export default {
|
|||
const priceString = getAmountDue(lineItemsToBePriced, false); // pass the IncludeTax param as false
|
||||
const priceStringIntegerRoundedDown = priceString?.split(".")[0]; // same method used as getDisplayPrice() in service-package-radio used on /quote
|
||||
const pricingByDayBasePrice = parseInt(priceStringIntegerRoundedDown);
|
||||
const isMobileSelected =
|
||||
store.getters.order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE;
|
||||
let includePricingByDayUpcharge = false;
|
||||
|
||||
// Check to see if date should be pre-selected
|
||||
let preSelectedSlot = await store.getters.order.schedule;
|
||||
let preSelectedDate = preSelectedSlot ? preSelectedSlot.date : null;
|
||||
if (isMobileSelected && preSelectedDate) preSelectedDate += "-mobile";
|
||||
if (!preSelectedSlot.date || preSelectedSlot?.date?.length < 1) {
|
||||
preSelectedSlot = null;
|
||||
} else {
|
||||
|
|
@ -279,9 +310,9 @@ export default {
|
|||
const datePickerInitialDataPromise = await datePicker.methods.loadInitialData({
|
||||
// setup config options for date-picker
|
||||
selectableDatesSetting: "custom",
|
||||
initialViewRowsToShow: 2,
|
||||
customSelectableDatesCallback: getAvailableDates,
|
||||
preSelectedDate: preSelectedSlot ? preSelectedSlot.date : preSelectedSlot,
|
||||
initialViewRowsToShow: NUMBER_OF_CALENDAR_ROWS_TO_SHOW_FOR_INITIAL_VIEW,
|
||||
getSelectableDatesCallback: getScheduleApiResponse,
|
||||
preSelectedDate: preSelectedDate,
|
||||
});
|
||||
|
||||
// Get pricingByDayUpcharge needed for Pricing By Day
|
||||
|
|
@ -351,7 +382,10 @@ export default {
|
|||
vm.setCmsContent(resultMap.cmsContent);
|
||||
vm.$refs.datePicker.initializeComponent(datePickerInitialData);
|
||||
vm.$refs.locationAlerts.initializeComponent(resultMap.alertReasons);
|
||||
vm.selectableDatesData = datePickerInitialData.initialShopTimeSlotsResponse;
|
||||
vm.selectableDatesInshop =
|
||||
datePickerInitialData.initialShopTimeSlotsResponse.inshopTimeSlotsData;
|
||||
vm.selectableDatesMobile =
|
||||
datePickerInitialData.initialShopTimeSlotsResponse.mobileTimeSlotsData;
|
||||
vm.mobilePremiumAppointmentFee = resultMap.premiumFeeWithPrice
|
||||
? resultMap.premiumFeeWithPrice[0]
|
||||
: null;
|
||||
|
|
@ -363,21 +397,33 @@ export default {
|
|||
vm.pricingByDayBasePrice = pricingByDayBasePrice;
|
||||
vm.pricingByDayUpcharge = pricingByDayUpcharge;
|
||||
vm.showPricingByDay = showPricingByDay;
|
||||
vm.isMobileSelected = isMobileSelected;
|
||||
});
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
const selectableDatesData = this.selectableDatesData;
|
||||
// if no date selected on load
|
||||
if (!this.selectedDate) {
|
||||
if (selectableDatesData?.days?.length > 0) {
|
||||
this.selectedDate = selectableDatesData.days[0].date;
|
||||
// if no date is preselected on load, then select the first available
|
||||
if (this.isMobileSelected) {
|
||||
this.selectedDate = returnFirstDate(this.selectableDatesMobile);
|
||||
} else {
|
||||
this.selectedDate = returnFirstDate(this.selectableDatesInshop);
|
||||
}
|
||||
|
||||
// if there is still no selected date, then load more and try again
|
||||
if (!this.selectedDate) {
|
||||
setTimeout(() => {
|
||||
this.$refs.datePicker.showAnotherMonth().then((moreSelectableDatesData) => {
|
||||
this.selectableDatesData = moreSelectableDatesData;
|
||||
if (selectableDatesData?.days?.length > 0) {
|
||||
this.selectedDate = selectableDatesData.days[0].date;
|
||||
this.$refs.datePicker.showAnotherMonth().then((moreSelectableDates) => {
|
||||
// update global
|
||||
this.selectableDatesInshop.days =
|
||||
moreSelectableDates.inshopTimeSlotsData.days;
|
||||
this.selectableDatesMobile.days =
|
||||
moreSelectableDates.mobileTimeSlotsData.days;
|
||||
|
||||
if (this.isMobileSelected) {
|
||||
this.selectedDate = returnFirstDate(this.selectableDatesMobile);
|
||||
} else {
|
||||
this.selectedDate = returnFirstDate(this.selectableDatesInshop);
|
||||
}
|
||||
|
||||
this.setDisplayWaitList();
|
||||
|
|
@ -396,27 +442,46 @@ export default {
|
|||
return this.splitCopyOnCMSPlaceHolder(this.ChangeShopLinkText);
|
||||
},
|
||||
appointmentType() {
|
||||
return this.$store.getters.order.serviceLocation.appointmentType;
|
||||
// TODO - THIS HAS TO BE CHANGED. APPOINTMENT TYPE CAN NOW CHANGE ON THE FLY, NOT FROM STORE ANYMORE.
|
||||
// return this.$store.getters.order.serviceLocation.appointmentType;
|
||||
if (this.isMobileSelected) {
|
||||
return "Mobile";
|
||||
} else {
|
||||
return "Inshop";
|
||||
}
|
||||
},
|
||||
getServiceMinutesMin() {
|
||||
return this.isMobileSelected
|
||||
? this.selectableDatesMobile.estimatedServiceMinutesMinimum
|
||||
: this.selectableDatesInshop.estimatedServiceMinutesMinimum;
|
||||
},
|
||||
getServiceMinutesMax() {
|
||||
return this.isMobileSelected
|
||||
? this.selectableDatesMobile.estimatedServiceMinutesMaximum
|
||||
: this.selectableDatesInshop.estimatedServiceMinutesMaximum;
|
||||
},
|
||||
timeSlotsForSelectedDate() {
|
||||
if (!this.selectedDate) return null;
|
||||
const selectedDate = this.selectedDate;
|
||||
|
||||
return this.selectableDatesData.days?.find(
|
||||
(selectableDate) => selectableDate.date === this.selectedDate
|
||||
);
|
||||
if (this.isMobileSelected) {
|
||||
const selectedDateString = selectedDate.replace("-mobile", "");
|
||||
return this.selectableDatesMobile.days?.find(
|
||||
(selectableDate) => selectableDate.date === selectedDateString
|
||||
);
|
||||
} else {
|
||||
return this.selectableDatesInshop.days?.find(
|
||||
(selectableDate) => selectableDate.date === this.selectedDate
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
splitCopyOnCMSPlaceHolder,
|
||||
arePagePrerequisitesValid() {
|
||||
const serviceLocation = store.getters.order.serviceLocation;
|
||||
const serviceLocation = store.getters.order.serviceLocation; // TODO - MAY NEED TO REMOVE THIS AS A PREREQ
|
||||
|
||||
const serviceLocationPreReqs =
|
||||
serviceLocation.zipCode &&
|
||||
serviceLocation.zipCodeCtu &&
|
||||
serviceLocation.appointmentType &&
|
||||
(serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE ||
|
||||
serviceLocation.provider.providerNumber);
|
||||
const serviceLocationPreReqs = serviceLocation.zipCode && serviceLocation.zipCodeCtu; // TODO - MAY NEED TO ADD IN SERVICE LOCATION PREREQS HERE TOO
|
||||
|
||||
const paymentInfo = store.getters.payment.isInsurance !== null;
|
||||
|
||||
|
|
@ -441,26 +506,38 @@ export default {
|
|||
}
|
||||
return preReqResult;
|
||||
},
|
||||
async getAvailableDatesMethod(startDate, endDate) {
|
||||
const newShopTimeSlots = await getAvailableDates(
|
||||
async getMoreScheduleData(startDate, endDate) {
|
||||
// called only when "View more dates" is clicked; not on initial page load
|
||||
const moreShopTimeSlots = await getScheduleApiResponse(
|
||||
startDate,
|
||||
endDate,
|
||||
this.appointmentType,
|
||||
this.$store.getters.order.serviceLocation.provider.providerNumber
|
||||
);
|
||||
|
||||
// ADD API CALL RESULTS TO EXISTING DATE DATA
|
||||
this.selectableDatesData.days = this.selectableDatesData.days.concat(
|
||||
newShopTimeSlots.days
|
||||
this.selectableDatesInshop.days = this.selectableDatesInshop.days.concat(
|
||||
moreShopTimeSlots.inshopTimeSlotsData.days
|
||||
);
|
||||
return newShopTimeSlots;
|
||||
this.selectableDatesMobile.days = this.selectableDatesMobile.days.concat(
|
||||
moreShopTimeSlots.mobileTimeSlotsData.days
|
||||
);
|
||||
|
||||
return moreShopTimeSlots;
|
||||
},
|
||||
getAvailableDates,
|
||||
getScheduleApiResponse,
|
||||
getServiceZipCtuCodeFromStore() {
|
||||
return store.getters.order.serviceLocation.zipCodeCtu;
|
||||
},
|
||||
getSelectedDate() {
|
||||
return store.getters.order.schedule.date;
|
||||
let isMobileSelected = this.isMobileSelected;
|
||||
if (isMobileSelected === undefined) {
|
||||
isMobileSelected =
|
||||
store.getters.order.serviceLocation.appointmentType ===
|
||||
AppointmentTypeStrings.MOBILE;
|
||||
}
|
||||
let storedDate = store.getters.order.schedule.date;
|
||||
if (isMobileSelected) storedDate += "-mobile";
|
||||
return storedDate;
|
||||
},
|
||||
getSelectedTimeSlotInfo() {
|
||||
const supportingItems = this.getSupportingItems();
|
||||
|
|
@ -513,8 +590,7 @@ export default {
|
|||
},
|
||||
convertSelectedDateToShortMonthAndDay(selectedDate) {
|
||||
// This conversion ensures we don't get get GMT induced date changes
|
||||
const dateObject = convertDateStringToDate(selectedDate);
|
||||
// Ex: April 25
|
||||
const dateObject = convertDateStringToDate(selectedDate.replace("-mobile", ""));
|
||||
return dateObject.toLocaleDateString("en-us", { month: "short", day: "numeric" });
|
||||
},
|
||||
getDisplayTextForMilitaryTime(militaryTimeInput, shouldTrimMinutesIfEmpty = false) {
|
||||
|
|
@ -523,10 +599,7 @@ export default {
|
|||
const minutes = militaryTimeInput.split(":")[1];
|
||||
const meridianNotation = hours > 11 ? "PM" : "AM";
|
||||
|
||||
if (hours > 12) {
|
||||
hours -= 12;
|
||||
}
|
||||
|
||||
if (hours > 12) hours -= 12;
|
||||
if (shouldTrimMinutesIfEmpty && minutes === "00") {
|
||||
return `${hours} ${meridianNotation}`;
|
||||
} else {
|
||||
|
|
@ -540,15 +613,25 @@ export default {
|
|||
);
|
||||
},
|
||||
forwardButtonAction() {
|
||||
// TODO - WILL NEED TO SAVE THE APPOINTMENT TYPE ALSO (AND ANYTHING ELSE THAT SERVICE-LOCATION DID TOO)
|
||||
this.updateSupportingItems();
|
||||
|
||||
if (this.waitListRequested) {
|
||||
var gaLabel = store.getters.isMobileAppointment
|
||||
? "checked_mobile"
|
||||
: "checked_inshop";
|
||||
if (this.displayWaitList) {
|
||||
let gaLabel = "";
|
||||
let status = this.waitListRequested ? "checked" : "unchecked";
|
||||
if (
|
||||
this.waitListRequested === null &&
|
||||
this.$store.state.order.customer.waitListRequested === true
|
||||
) {
|
||||
status = "checked";
|
||||
}
|
||||
const type = store.getters.isMobileAppointment ? "mobile" : "inshop";
|
||||
gaLabel = `${status}_${type}`;
|
||||
|
||||
const currentDate = new Date();
|
||||
const dateString = this.selectableDatesData.days[0].date;
|
||||
const dateString = this.isMobileSelected
|
||||
? this.selectableDatesMobile.days[0].date
|
||||
: this.selectableDatesInshop.days[0].date;
|
||||
const [year, month, day] = dateString.split("-").map(Number);
|
||||
const appointmentDate = new Date(year, month - 1, day);
|
||||
const timeDifference = appointmentDate - currentDate;
|
||||
|
|
@ -565,11 +648,14 @@ export default {
|
|||
store.getters.order.serviceLocation.zipCodeCtu;
|
||||
this.pushEventToGA("waitlist", "add_to_waitlist_check_box_status", gaLabel, true);
|
||||
}
|
||||
|
||||
if (this.selectedTimeSlotInfo.timeSlot.jobMinMinutes == null) {
|
||||
this.selectedTimeSlotInfo.timeSlot.jobMinMinutes =
|
||||
this.selectableDatesData?.estimatedServiceMinutesMinimum?.toString();
|
||||
this.selectedTimeSlotInfo.timeSlot.jobMaxMinutes =
|
||||
this.selectableDatesData?.estimatedServiceMinutesMaximum?.toString();
|
||||
this.selectedTimeSlotInfo.timeSlot.jobMinMinutes = this.isMobileSelected
|
||||
? this.selectableDatesMobile.estimatedServiceMinutesMinimum?.toString()
|
||||
: this.selectableDatesInshop.estimatedServiceMinutesMinimum?.toString();
|
||||
this.selectedTimeSlotInfo.timeSlot.jobMaxMinutes = this.isMobileSelected
|
||||
? this.selectableDatesMobile.estimatedServiceMinutesMaximum?.toString()
|
||||
: this.selectableDatesInshop.estimatedServiceMinutesMaximum?.toString();
|
||||
}
|
||||
|
||||
this.dispatchStoreAction(
|
||||
|
|
@ -597,9 +683,12 @@ export default {
|
|||
experimentSettings.DISPLAY_WAITLIST,
|
||||
"true"
|
||||
) &&
|
||||
this.selectableDatesData.days[0]
|
||||
this.selectableDatesInshop.days[0] && // TODO - ASK BUSINESS... WHAT LOGIC SHOULD WE USE HERE FOR WHEN TO DISPLAY THE WAIT LIST?
|
||||
this.selectableDatesMobile.days[0]
|
||||
) {
|
||||
const dateString = this.selectableDatesData.days[0].date;
|
||||
const dateString = this.isMobileSelected
|
||||
? this.selectableDatesMobile.days[0].date
|
||||
: this.selectableDatesInshop.days[0].date;
|
||||
const [year, month, day] = dateString.split("-").map(Number);
|
||||
const targetDate = new Date(year, month - 1, day);
|
||||
const currentDate = new Date();
|
||||
|
|
@ -765,7 +854,7 @@ export default {
|
|||
}
|
||||
}
|
||||
.change-location a {
|
||||
font-family: AvertaSemibold;
|
||||
font-family: UrbanistSemibold;
|
||||
}
|
||||
.time-slots-question {
|
||||
padding: 0 0.75rem;
|
||||
|
|
|
|||
|
|
@ -631,7 +631,6 @@ export default {
|
|||
margin-left: 0.5rem;
|
||||
strong {
|
||||
font-weight: 600;
|
||||
letter-spacing: 0%;
|
||||
}
|
||||
}
|
||||
.success-image {
|
||||
|
|
|
|||
|
|
@ -561,7 +561,7 @@ export default {
|
|||
margin-top: 1.5rem;
|
||||
|
||||
.time-slot-header {
|
||||
font-family: AvertaBold;
|
||||
font-family: UrbanistBold;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
.text-block.duration-text-block {
|
||||
|
|
|
|||
|
|
@ -96,7 +96,6 @@ import { saveSession } from "@/helpers/heritage-integration/order-helper.js";
|
|||
import baseMixin from "@/mixins/base-mixin.js";
|
||||
import { queryStrings } from "@/constants/query-strings";
|
||||
import { routeData } from "@/router/constants/routes";
|
||||
import { consumeQueryFromStash } from "@/router/methods/helpers/querystring-stash";
|
||||
|
||||
// Define Validation Rules
|
||||
defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
|
||||
|
|
@ -116,7 +115,7 @@ export default {
|
|||
mixins: [vinPagesMixin],
|
||||
data() {
|
||||
return {
|
||||
serviceZipCode: this.getZipFromStore() ?? consumeQueryFromStash(queryStrings.ZIP_CODE),
|
||||
serviceZipCode: this.getZipFromStore(),
|
||||
emailOrSms: this.getEmailOrSmsFromStore(),
|
||||
carId: this.getCarIdfromStore(),
|
||||
isHeavyTruck: this.getIsVehicleHeavyTruckServiceableFromStore(),
|
||||
|
|
|
|||
|
|
@ -110,10 +110,6 @@ import { getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper";
|
|||
import { applicationConfig } from "../../constants/application-config";
|
||||
import { queryStrings } from "@/constants/query-strings";
|
||||
import { getQuerystringParameter } from "@/helpers/querystring-helper";
|
||||
import {
|
||||
consumeQueryFromStash,
|
||||
peekQueryFromStash,
|
||||
} from "@/router/methods/helpers/querystring-stash";
|
||||
import { debugLog } from "@/helpers/debug-log-helper";
|
||||
|
||||
//define validation rules
|
||||
|
|
@ -138,7 +134,7 @@ export default {
|
|||
imageUrl: this.getImagefromStore(),
|
||||
imageVifNumber: this.getImageVifNumberfromStore(),
|
||||
imageVifColor: this.getImageVifColorfromStore(),
|
||||
serviceZipCode: this.getZipFromStore() ?? peekQueryFromStash(queryStrings.ZIP_CODE),
|
||||
serviceZipCode: this.getZipFromStore(),
|
||||
yearOptions: [],
|
||||
makeOptions: [],
|
||||
modelOptions: [],
|
||||
|
|
|
|||
|
|
@ -159,8 +159,6 @@ import baseMixin from "@/mixins/base-mixin.js";
|
|||
import store from "@/store";
|
||||
import vinPagesMixin from "@/mixins/vin-pages-mixin";
|
||||
import { routeData } from "@/router/constants/routes";
|
||||
import { consumeQueryFromStash } from "@/router/methods/helpers/querystring-stash";
|
||||
import { queryStrings } from "@/constants/query-strings";
|
||||
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
|
||||
|
|
@ -204,7 +202,7 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
vin: this.getVinFromStore(),
|
||||
serviceZipCode: this.getZipFromStore() ?? consumeQueryFromStash(queryStrings.ZIP_CODE),
|
||||
serviceZipCode: this.getZipFromStore(),
|
||||
emailOrSms: this.getEmailOrSmsFromStore(),
|
||||
carId: this.getCarIdfromStore(),
|
||||
isHeavyTruck: this.getIsVehicleHeavyTruckServiceableFromStore(),
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ import { getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper";
|
|||
import { routeData } from "@/router/constants/routes";
|
||||
import { isVerifiedInsurance } from "@/router/methods/helpers/requires-verified-redirect";
|
||||
import store from "@/store";
|
||||
import { queryStrings } from "@/constants/query-strings";
|
||||
import baseMixin from "@/mixins/base-mixin";
|
||||
import { storeActions } from "@/constants/store-actions";
|
||||
import { consumeQueryFromStash } from "@/router/methods/helpers/querystring-stash";
|
||||
|
||||
export async function vehicleBeforeEnter(to, from) {
|
||||
// If cookie indicates that vehicle has changed during insurance flow, reset parts info.
|
||||
|
|
@ -12,6 +16,23 @@ export async function vehicleBeforeEnter(to, from) {
|
|||
store.commit(storeMutations.RESET_GLASS_PARTS_STATE);
|
||||
}
|
||||
|
||||
// If there is a zip querystring, consume & commit it here.
|
||||
const newZipFromQuerystring = consumeQueryFromStash(queryStrings.ZIP_CODE);
|
||||
if (newZipFromQuerystring) {
|
||||
const zipData = await baseMixin.methods.getZipCodeData(
|
||||
newZipFromQuerystring,
|
||||
routeData.VEHICLE.name
|
||||
);
|
||||
|
||||
if (zipData.isValid) {
|
||||
store.dispatch(storeActions.SAVE_SERVICE_ZIP_CODE_INFO, {
|
||||
zipCode: newZipFromQuerystring,
|
||||
state: zipData.state.state,
|
||||
zipCodeCtu: zipData.zipCodeCtu,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (isVerifiedInsurance()) {
|
||||
return {
|
||||
name: routeData.VEHICLE_DAMAGE.name,
|
||||
|
|
|
|||
|
|
@ -97,3 +97,11 @@ body {
|
|||
}
|
||||
}
|
||||
}
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea,
|
||||
option,
|
||||
.btn {
|
||||
letter-spacing: 0.03rem;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ body {
|
|||
font-size: 1rem;
|
||||
line-height: 1.625;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.03rem;
|
||||
}
|
||||
|
||||
//Headings
|
||||
|
|
@ -43,7 +44,7 @@ h5,
|
|||
h6,
|
||||
.h6 {
|
||||
line-height: 1.7;
|
||||
letter-spacing: 0.75px;
|
||||
letter-spacing: 0.03rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
|
|
@ -78,58 +79,58 @@ caption,
|
|||
font-weight: 500;
|
||||
}
|
||||
|
||||
// Averta Fonts
|
||||
// Urbanist Fonts
|
||||
|
||||
@font-face {
|
||||
font-family: "AvertaBold";
|
||||
font-family: "UrbanistRegular";
|
||||
src:
|
||||
url("@/assets/fonts/averta-bold-webfont.woff2") format("woff2"),
|
||||
url("@/assets/fonts/averta-bold-webfont.woff") format("woff");
|
||||
url("@/assets/fonts/Urbanist-Regular.woff") format("woff2"),
|
||||
url("@/assets/fonts/Urbanist-Regular.woff2") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "AvertaBold_Italic";
|
||||
font-family: "UrbanistSemibold";
|
||||
src:
|
||||
url("@/assets/fonts/averta-bolditalic-webfont.woff2") format("woff2"),
|
||||
url("@/assets/fonts/averta-bolditalic-webfont.woff") format("woff");
|
||||
url("@/assets/fonts/Urbanist-SemiBold.woff") format("woff2"),
|
||||
url("@/assets/fonts/Urbanist-SemiBold.woff2") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "AvertaExtraBold";
|
||||
font-family: "UrbanistBold";
|
||||
src:
|
||||
url("@/assets/fonts/averta-extrabold-webfont.woff2") format("woff2"),
|
||||
url("@/assets/fonts/averta-extrabold-webfont.woff") format("woff");
|
||||
url("@/assets/fonts/Urbanist-Bold.woff") format("woff2"),
|
||||
url("@/assets/fonts/Urbanist-Bold.woff2") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "AvertaRegular";
|
||||
font-family: "UrbanistExtraBold";
|
||||
src:
|
||||
url("@/assets/fonts/averta-regular-webfont.woff2") format("woff2"),
|
||||
url("@/assets/fonts/averta-regular-webfont.woff") format("woff");
|
||||
url("@/assets/fonts/Urbanist-ExtraBold.woff") format("woff2"),
|
||||
url("@/assets/fonts/Urbanist-ExtraBold.woff2") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "AvertaRegular_Italic";
|
||||
font-family: "UrbanistMedium_Italic";
|
||||
src:
|
||||
url("@/assets/fonts/averta-regularitalic-webfont.woff2") format("woff2"),
|
||||
url("@/assets/fonts/averta-regularitalic-webfont.woff") format("woff");
|
||||
url("@/assets/fonts/Urbanist-MediumItalic.woff") format("woff2"),
|
||||
url("@/assets/fonts/Urbanist-MediumItalic.woff2") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "AvertaSemibold";
|
||||
font-family: "UrbanistBold_Italic";
|
||||
src:
|
||||
url("@/assets/fonts/averta-semibold-webfont.woff2") format("woff2"),
|
||||
url("@/assets/fonts/averta-semibold-webfont.woff") format("woff");
|
||||
url("@/assets/fonts/Urbanist-BoldItalic.woff") format("woff2"),
|
||||
url("@/assets/fonts/Urbanist-BoldItalic.woff2") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,9 +120,9 @@ $theme-colors: (
|
|||
$body-color: $gray-600;
|
||||
|
||||
//Fonts
|
||||
$font-family-sans-serif: AvertaRegular, Arial, Helvetica, sans-serif;
|
||||
$font-family-sans-serif: UrbanistRegular, Arial, Helvetica, sans-serif;
|
||||
$font-family-monospace:
|
||||
SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
UrbanistRegular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
// stylelint-enable value-keyword-case
|
||||
$font-family-base: $font-family-sans-serif;
|
||||
$font-family-code: $font-family-monospace;
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ export default {
|
|||
justify-content: flex-start;
|
||||
|
||||
p {
|
||||
font-family: UrbanistRegular;
|
||||
color: $gray-600;
|
||||
text-align: center;
|
||||
|
||||
|
|
@ -123,15 +124,16 @@ export default {
|
|||
&:checked + .list-card-content {
|
||||
p {
|
||||
color: $black;
|
||||
font-weight: 500;
|
||||
font-family: UrbanistSemibold;
|
||||
}
|
||||
.p-bold {
|
||||
font-family: UrbanistSemibold;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.sub-copy {
|
||||
font-family: UrbanistSemibold;
|
||||
color: $gray-600;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue