Merge branch 'develop' into feature/CASH-836

This commit is contained in:
maguire-arman 2025-06-23 14:20:40 -04:00
commit 1dadc5f1cb
54 changed files with 748 additions and 475 deletions

View file

@ -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! */ /* 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 { body {
font-family: Roboto; font-family: Urbanist, Arial, Helvetica, sans-serif;
font-weight: 400; font-weight: 400;
color: #000; color: #000;
margin: 0; margin: 0;
background-color: white; background-color: white;
padding: 8px; padding: 8px;
} }
body h3 {
font-weight: 600;
}
body .validation-info { body .validation-info {
display: none; display: none;
} }
@ -31,7 +35,7 @@ body .form-group {
body .headerlinecontainer .headerline1 { body .headerlinecontainer .headerline1 {
font-size: 20px; font-size: 20px;
line-height: 32px; line-height: 32px;
font-weight: 400; font-weight: 500;
margin: 24px 0; margin: 24px 0;
text-align: center; text-align: center;
} }
@ -92,7 +96,7 @@ body .creditCardSpecific div .headerlinecontainer {
display: none; display: none;
} }
body .creditCardSpecific div label { body .creditCardSpecific div label {
font-weight: 500; font-weight: 600;
} }
body .creditCardSpecific #infoRow2 { body .creditCardSpecific #infoRow2 {
margin-bottom: 0; margin-bottom: 0;
@ -268,6 +272,7 @@ body .cc-error-container #alert-message {
#fmg-checkout-shared #other-payments-container label span.paymentOptionText { #fmg-checkout-shared #other-payments-container label span.paymentOptionText {
margin: auto 0; margin: auto 0;
padding-left: 0; padding-left: 0;
font-weight: 600;
} }
#fmg-checkout-shared #other-payments-container #afterpayParentLink label > span:first-of-type { #fmg-checkout-shared #other-payments-container #afterpayParentLink label > span:first-of-type {
width: 100%; width: 100%;

View file

@ -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! */ /* 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 { body {
font-family: Roboto; font-family: Urbanist, Arial, Helvetica, sans-serif;
font-weight: 400; font-weight: 400;
color: #000; color: #000;
margin: 0; margin: 0;
background-color: white; background-color: white;
padding: 8px; padding: 8px;
h3 {
font-weight: 600;
}
.validation-info { .validation-info {
display: none; display: none;
} }
@ -37,7 +42,7 @@ body {
.headerline1 { .headerline1 {
font-size: 20px; font-size: 20px;
line-height: 32px; line-height: 32px;
font-weight: 400; font-weight: 500;
margin: 24px 0; margin: 24px 0;
text-align: center; text-align: center;
} }
@ -98,7 +103,7 @@ body {
display: none; display: none;
} }
label { label {
font-weight: 500; font-weight: 600;
} }
} }
#infoRow2 { #infoRow2 {
@ -325,6 +330,7 @@ body {
&.paymentOptionText { &.paymentOptionText {
margin: auto 0; margin: auto 0;
padding-left: 0; padding-left: 0;
font-weight: 600;
} }
} }
} }

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.

View file

@ -2,6 +2,7 @@ const AppointmentTypeStrings = {
IN_SHOP: "Inshop", IN_SHOP: "Inshop",
MOBILE: "Mobile", MOBILE: "Mobile",
DROP_OFF: "Dropoff", DROP_OFF: "Dropoff",
IN_SHOP_OR_DROP_OFF: "InshopOrDropoff",
}; };
const PREMIUM_TIME_SLOT_ID_FLAG = "-PREMIUM"; const PREMIUM_TIME_SLOT_ID_FLAG = "-PREMIUM";

View file

@ -351,6 +351,7 @@ export default {
} }
} }
.button-question { .button-question {
font-family: UrbanistSemibold;
color: $black; color: $black;
.radio-button-container { .radio-button-container {

View file

@ -42,19 +42,36 @@ const mockValuesForSetDate01 = {
initialViewEndDate: "2023-12-30", initialViewEndDate: "2023-12-30",
calendarViewDirection: "future", calendarViewDirection: "future",
initialShopTimeSlotsResponse: { initialShopTimeSlotsResponse: {
days: [ inshopTimeSlotsData: {
{ days: [
date: "2023-12-30", {
timeSlots: [ date: "2023-12-30",
{ timeSlots: [
id: "06747-01820-S-B*20453*7 AM", {
startTime: "07:00", id: "06747-01820-S-B*20453*7 AM",
endTime: "08:00", startTime: "07:00",
offerPremium: false, 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, estimatedServiceMinutesMinimum: 90,
estimatedServiceMinutesMaximum: 120, estimatedServiceMinutesMaximum: 120,
}, },
@ -68,194 +85,195 @@ const mockValuesForSetDate01 = {
monthIndex: 12, monthIndex: 12,
monthString: "December-2023", monthString: "December-2023",
yearNum: 2023, yearNum: 2023,
dates: [ datesInshop: [
{ {
dateNum: 1, dateNum: 1,
dayClasses: " current-day", dayClasses: " current-day",
inputValue: "2023-12-01", inputValue: "2023-12-01",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 2, dateNum: 2,
dayClasses: "", dayClasses: "",
inputValue: "2023-12-02", inputValue: "2023-12-02",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 3, dateNum: 3,
dayClasses: " sunday", dayClasses: " sunday",
inputValue: "2023-12-03", inputValue: "2023-12-03",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 4, dateNum: 4,
dayClasses: "", dayClasses: "",
inputValue: "2023-12-04", inputValue: "2023-12-04",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 5, dateNum: 5,
dayClasses: "", dayClasses: "",
inputValue: "2023-12-05", inputValue: "2023-12-05",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 6, dateNum: 6,
dayClasses: "", dayClasses: "",
inputValue: "2023-12-06", inputValue: "2023-12-06",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 7, dateNum: 7,
dayClasses: "", dayClasses: "",
inputValue: "2023-12-07", inputValue: "2023-12-07",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 8, dateNum: 8,
dayClasses: "", dayClasses: "",
inputValue: "2023-12-08", inputValue: "2023-12-08",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 9, dateNum: 9,
dayClasses: "", dayClasses: "",
inputValue: "2023-12-09", inputValue: "2023-12-09",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 10, dateNum: 10,
dayClasses: " sunday", dayClasses: " sunday",
inputValue: "2023-12-10", inputValue: "2023-12-10",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 11, dateNum: 11,
dayClasses: "", dayClasses: "",
inputValue: "2023-12-11", inputValue: "2023-12-11",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 12, dateNum: 12,
dayClasses: "", dayClasses: "",
inputValue: "2023-12-12", inputValue: "2023-12-12",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 13, dateNum: 13,
dayClasses: "", dayClasses: "",
inputValue: "2023-12-13", inputValue: "2023-12-13",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 14, dateNum: 14,
dayClasses: "", dayClasses: "",
inputValue: "2023-12-14", inputValue: "2023-12-14",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 15, dateNum: 15,
dayClasses: "", dayClasses: "",
inputValue: "2023-12-15", inputValue: "2023-12-15",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 16, dateNum: 16,
dayClasses: "", dayClasses: "",
inputValue: "2023-12-16", inputValue: "2023-12-16",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 17, dateNum: 17,
dayClasses: " sunday", dayClasses: " sunday",
inputValue: "2023-12-17", inputValue: "2023-12-17",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 18, dateNum: 18,
dayClasses: "", dayClasses: "",
inputValue: "2023-12-18", inputValue: "2023-12-18",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 19, dateNum: 19,
dayClasses: "", dayClasses: "",
inputValue: "2023-12-19", inputValue: "2023-12-19",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 20, dateNum: 20,
dayClasses: "", dayClasses: "",
inputValue: "2023-12-20", inputValue: "2023-12-20",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 21, dateNum: 21,
dayClasses: "", dayClasses: "",
inputValue: "2023-12-21", inputValue: "2023-12-21",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 22, dateNum: 22,
dayClasses: "", dayClasses: "",
inputValue: "2023-12-22", inputValue: "2023-12-22",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 23, dateNum: 23,
dayClasses: "", dayClasses: "",
inputValue: "2023-12-23", inputValue: "2023-12-23",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 24, dateNum: 24,
dayClasses: " sunday", dayClasses: " sunday",
inputValue: "2023-12-24", inputValue: "2023-12-24",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 25, dateNum: 25,
dayClasses: "", dayClasses: "",
inputValue: "2023-12-25", inputValue: "2023-12-25",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 26, dateNum: 26,
dayClasses: "", dayClasses: "",
inputValue: "2023-12-26", inputValue: "2023-12-26",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 27, dateNum: 27,
dayClasses: "", dayClasses: "",
inputValue: "2023-12-27", inputValue: "2023-12-27",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 28, dateNum: 28,
dayClasses: "", dayClasses: "",
inputValue: "2023-12-28", inputValue: "2023-12-28",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 29, dateNum: 29,
dayClasses: "", dayClasses: "",
inputValue: "2023-12-29", inputValue: "2023-12-29",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 30, dateNum: 30,
dayClasses: "", dayClasses: "",
inputValue: "2023-12-30", inputValue: "2023-12-30",
isSelectable: true, isAvailable: true,
}, },
{ {
dateNum: 31, dateNum: 31,
dayClasses: " sunday day-hidden", dayClasses: " sunday day-hidden",
inputValue: "2023-12-31", inputValue: "2023-12-31",
isSelectable: false, isAvailable: false,
}, },
], ],
datesMobile: [],
startDateDayIndex: 5, startDateDayIndex: 5,
monthClass: "", monthClass: "",
isMonthThatHidesSomeDaysForInitialView: true, isMonthThatHidesSomeDaysForInitialView: true,
@ -265,32 +283,33 @@ const mockValuesForSetDate01 = {
monthIndex: 1, monthIndex: 1,
monthString: "January-2024", monthString: "January-2024",
yearNum: 2024, yearNum: 2024,
dates: [ datesInshop: [
{ {
dateNum: 1, dateNum: 1,
dayClasses: "", dayClasses: "",
inputValue: "2024-01-01", inputValue: "2024-01-01",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 2, dateNum: 2,
dayClasses: "", dayClasses: "",
inputValue: "2024-01-02", inputValue: "2024-01-02",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 30, dateNum: 30,
dayClasses: "", dayClasses: "",
inputValue: "2024-01-30", inputValue: "2024-01-30",
isSelectable: false, isAvailable: false,
}, },
{ {
dateNum: 31, dateNum: 31,
dayClasses: "", dayClasses: "",
inputValue: "2024-01-31", inputValue: "2024-01-31",
isSelectable: false, isAvailable: false,
}, },
], ],
datesMobile: [],
startDateDayIndex: 1, startDateDayIndex: 1,
monthClass: " month-hidden", monthClass: " month-hidden",
}, },
@ -303,36 +322,70 @@ const mockInitialData = {
initialViewEndDate: "2023-08-19", initialViewEndDate: "2023-08-19",
calendarViewDirection: "future", calendarViewDirection: "future",
initialShopTimeSlotsResponse: { initialShopTimeSlotsResponse: {
days: [ inshopTimeSlotsData: {
{ days: [
date: "2023-07-25", {
timeSlots: [ date: "2023-07-25",
{ timeSlots: [
id: "06747-01820-S-B*20295*7 AM", {
startTime: "07:00", id: "06747-01820-S-B*20295*7 AM",
endTime: "08:00", startTime: "07:00",
offerPremium: false, endTime: "08:00",
}, offerPremium: false,
{ },
id: "06747-01820-S-B*20295*730 AM", {
startTime: "07:30", id: "06747-01820-S-B*20295*730 AM",
endTime: "08:30", startTime: "07:30",
offerPremium: false, endTime: "08:30",
}, offerPremium: false,
], },
}, ],
{ },
date: "2023-07-26", {
timeSlots: [ date: "2023-07-26",
{ timeSlots: [
id: "06747-01820-S-B*20296*7 AM", {
startTime: "07:00", id: "06747-01820-S-B*20296*7 AM",
endTime: "08:00", startTime: "07:00",
offerPremium: false, 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, estimatedServiceMinutesMinimum: 90,
estimatedServiceMinutesMaximum: 120, estimatedServiceMinutesMaximum: 120,
}, },
@ -342,30 +395,59 @@ const mockInitialData = {
}; };
const mockCallbackResponse = { const mockCallbackResponse = {
days: [ inshopTimeSlotsData: {
{ days: [
date: "2024-12-30", {
timeSlots: [ date: "2024-12-30",
{ timeSlots: [
id: "06747-01820-S-B*20453*7 AM", {
startTime: "07:00", id: "06747-01820-S-B*20453*7 AM",
endTime: "08:00", startTime: "07:00",
offerPremium: false, endTime: "08:00",
}, offerPremium: false,
], },
}, ],
{ },
date: "2024-01-02", {
timeSlots: [ date: "2024-01-02",
{ timeSlots: [
id: "06747-01820-S-B*20453*7 AM", {
startTime: "07:00", id: "06747-01820-S-B*20453*7 AM",
endTime: "08:00", startTime: "07:00",
offerPremium: false, 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, estimatedServiceMinutesMinimum: 90,
estimatedServiceMinutesMaximum: 120, estimatedServiceMinutesMaximum: 120,
}; };
@ -429,7 +511,7 @@ describe("date-picker.vue", () => {
wrapper.unmount(); 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 // Arrange
const { wrapper } = setupMocks({ const { wrapper } = setupMocks({
propsData: { propsData: {
@ -438,7 +520,7 @@ describe("date-picker.vue", () => {
}); });
// Act // Act
const callbackResponse = wrapper.vm.customSelectableDatesCallback( const callbackResponse = wrapper.vm.getMoreDatesCallback(
"2023-07-24", "2023-07-24",
"2023-08-19", "2023-08-19",
"Inshop", "Inshop",
@ -608,7 +690,7 @@ describe("date-picker.vue", () => {
const testResult = await wrapper.vm.loadInitialData({ const testResult = await wrapper.vm.loadInitialData({
selectableDatesSetting: "custom", selectableDatesSetting: "custom",
initialViewRowsToShow: 5, initialViewRowsToShow: 5,
customSelectableDatesCallback: mockPromiseResolve, getSelectableDatesCallback: mockPromiseResolve,
preSelectedDate: null, preSelectedDate: null,
}); });
@ -640,7 +722,7 @@ describe("date-picker.vue", () => {
const testResult = await datePicker.methods.loadInitialData.call(localThis, { const testResult = await datePicker.methods.loadInitialData.call(localThis, {
selectableDatesSetting: "custom", selectableDatesSetting: "custom",
initialViewRowsToShow: 5, initialViewRowsToShow: 5,
customSelectableDatesCallback: mockPromiseResolve, getSelectableDatesCallback: mockPromiseResolve,
preSelectedDate: null, preSelectedDate: null,
todayOverrideDateString: overrideDate, todayOverrideDateString: overrideDate,
@ -676,7 +758,7 @@ describe("date-picker.vue", () => {
const testResult = await datePicker.methods.loadInitialData.call(localThis, { const testResult = await datePicker.methods.loadInitialData.call(localThis, {
selectableDatesSetting: "custom", selectableDatesSetting: "custom",
initialViewRowsToShow: 5, initialViewRowsToShow: 5,
customSelectableDatesCallback: mockPromiseResolve, getSelectableDatesCallback: mockPromiseResolve,
preSelectedDate: null, preSelectedDate: null,
}); });
@ -696,7 +778,7 @@ describe("date-picker.vue", () => {
const testResult = await wrapper.vm.loadInitialData({ const testResult = await wrapper.vm.loadInitialData({
selectableDatesSetting: "past", selectableDatesSetting: "past",
initialViewRowsToShow: 5, initialViewRowsToShow: 5,
customSelectableDatesCallback: mockPromiseResolve, getSelectableDatesCallback: mockPromiseResolve,
}); });
// Assert // Assert
@ -715,7 +797,7 @@ describe("date-picker.vue", () => {
const testResult = await wrapper.vm.loadInitialData({ const testResult = await wrapper.vm.loadInitialData({
selectableDatesSetting: "custom", selectableDatesSetting: "custom",
initialViewRowsToShow: 5, initialViewRowsToShow: 5,
customSelectableDatesCallback: mockPromiseResolve, getSelectableDatesCallback: mockPromiseResolve,
}); });
// Assert // Assert
@ -742,10 +824,10 @@ describe("date-picker.vue", () => {
// Assert // Assert
expect(wrapper.vm.hideSomeDaysForInitialView).toBe(true); expect(wrapper.vm.hideSomeDaysForInitialView).toBe(true);
expect(wrapper.vm.selectableDatesData[0]).toMatchObject({ expect(wrapper.vm.selectableDatesInshop[0]).toMatchObject({
date: "2023-12-30", 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.length).toBe(7);
expect(wrapper.vm.months[0]).toMatchObject({ expect(wrapper.vm.months[0]).toMatchObject({
monthLabel: "December", monthLabel: "December",
@ -992,7 +1074,7 @@ describe("date-picker.vue", () => {
const testResult = await datePicker.methods.loadInitialData.call(localThis, { const testResult = await datePicker.methods.loadInitialData.call(localThis, {
selectableDatesSetting: "custom", selectableDatesSetting: "custom",
initialViewRowsToShow: 5, initialViewRowsToShow: 5,
customSelectableDatesCallback: mockPromiseResolve, getSelectableDatesCallback: mockPromiseResolve,
preSelectedDate: "2024-01-01", preSelectedDate: "2024-01-01",
todayOverrideDateString: overrideDate, todayOverrideDateString: overrideDate,
}); });
@ -1025,14 +1107,14 @@ describe("date-picker.vue", () => {
// Act // Act
await wrapper.vm.showAnotherMonth(); await wrapper.vm.showAnotherMonth();
// Assert // // Assert
expect(wrapper.vm.updateSelectableDates).toHaveBeenCalledWith( // expect(wrapper.vm.updateSelectableDates).toHaveBeenCalledWith(
"2023-12-31", // "2023-12-31",
"2023-12-31" // "2023-12-31"
); // );
expect(wrapper.vm.scrollToElement).toHaveBeenCalledWith("December-2023"); // 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 () => { 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...", () => { 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 // Arrange
const { wrapper } = setupMocks({ const { wrapper } = setupMocks({
propsData: { propsData: {
selectableDatesSetting: "custom", selectableDatesSetting: "custom",
todayOverrideDateString: mockValuesForSetDate01.overrideDate, todayOverrideDateString: mockValuesForSetDate01.overrideDate,
customSelectableDatesCallback: jest.fn(() => mockCallbackResponse), getMoreDatesCallback: jest.fn(() => mockCallbackResponse),
}, },
}); });
wrapper.vm.selectableDatesData = [ wrapper.vm.selectableDatesInshop = [
{ {
date: "2023-12-30", date: "2023-12-30",
timeSlots: [ timeSlots: [
@ -1141,7 +1223,8 @@ describe("date-picker.vue", () => {
// Act // Act
await wrapper.vm.updateSelectableDates("2024-01-01", "2024-01-05"); 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"; return obj.date === "2024-01-02";
}); });
@ -1151,16 +1234,16 @@ describe("date-picker.vue", () => {
wrapper.unmount(); 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 // Arrange
const { wrapper } = setupMocks({ const { wrapper } = setupMocks({
propsData: { propsData: {
selectableDatesSetting: "custom", selectableDatesSetting: "custom",
todayOverrideDateString: mockValuesForSetDate01.overrideDate, todayOverrideDateString: mockValuesForSetDate01.overrideDate,
customSelectableDatesCallback: jest.fn(() => mockCallbackResponse), getMoreDatesCallback: jest.fn(() => mockCallbackResponse),
}, },
}); });
wrapper.vm.selectableDatesData = [ wrapper.vm.selectableDatesInshop = [
{ {
date: "2023-12-30", date: "2023-12-30",
timeSlots: [ timeSlots: [
@ -1180,13 +1263,16 @@ describe("date-picker.vue", () => {
const targetMonth = wrapper.vm.months.find((month) => { const targetMonth = wrapper.vm.months.find((month) => {
return month.monthString === "January-2024"; 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"; return date.inputValue === "2024-01-02";
}); });
// Assert // Assert
expect(dateInArray).toMatchObject({ expect(dateInArray).toMatchObject({
isSelectable: true, isAvailable: true,
}); });
wrapper.unmount(); wrapper.unmount();
@ -1194,7 +1280,7 @@ describe("date-picker.vue", () => {
}); });
describe("scrollToElement method...", () => { 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 // Arrange
const container = document.createElement("div"); const container = document.createElement("div");
container.classList.add("page-container-grouped-styles"); container.classList.add("page-container-grouped-styles");

View file

@ -53,18 +53,19 @@
SAT SAT
</div> </div>
<div <div
v-for="date in month.dates" v-show="!isMobileSelected"
v-for="date in month.datesInshop"
:key="date.inputValue" :key="date.inputValue"
:id="date.inputValue" :id="date.inputValue"
class="grid-item radio-wrapper" class="grid-item radio-wrapper inshop-version"
:class="[ :class="[
date.dateNum === 1 ? 'first-day-' + month.startDateDayIndex : '', date.dateNum === 1 ? 'first-day-' + month.startDateDayIndex : '',
date.dayClasses, date.dayClasses,
date.isSelectable ? 'selectable-day' : '', date.isAvailable ? 'selectable-day' : '',
date.isPricingByDayUpchargeDay ? 'upch-day' : '', date.isPricingByDayUpchargeDay ? 'upch-day' : '',
]"> ]">
<input <input
:disabled="!date.isSelectable" :disabled="!date.isAvailable"
type="radio" type="radio"
name="day-of-month" name="day-of-month"
v-model="selectedDate" v-model="selectedDate"
@ -75,7 +76,37 @@
<label :for="`${month.monthLabel}-${date.dateNum.toString()}`"> <label :for="`${month.monthLabel}-${date.dateNum.toString()}`">
<span>{{ date.dateNum.toString() }}</span> <span>{{ date.dateNum.toString() }}</span>
<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"> class="price">
{{ date.priceString }} {{ date.priceString }}
</span> </span>
@ -134,21 +165,16 @@ import {
MONTHS_OF_YEAR, MONTHS_OF_YEAR,
DAYS_OF_WEEK, DAYS_OF_WEEK,
} from "@/digital-components/date-picker/mixins/constants"; } from "@/digital-components/date-picker/mixins/constants";
import { import { PREMIUM_FEE_PART_TYPE } from "@/constants/schedule-constants";
AppointmentTypeStrings,
PREMIUM_FEE_PART_TYPE,
PRICING_BY_DAY_PART_TYPE,
} from "@/constants/schedule-constants";
import { import {
selectableDaysOptions, selectableDaysOptions,
requiredParameter, requiredParameter,
forceTwoDigitString,
} from "@/digital-components/date-picker/mixins/helpers"; } from "@/digital-components/date-picker/mixins/helpers";
import { import {
convertDateToDateString, convertDateToDateString,
convertDateStringToDate, convertDateStringToDate,
} from "@/layouts/schedule/helpers/schedule-helper"; } 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 { deepClone } from "@/helpers/object-helper";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
@ -159,10 +185,10 @@ export default {
isLoading: true, isLoading: true,
months: null, months: null,
disableViewMoreDatesButton: false, disableViewMoreDatesButton: false,
selectableDatesData: [], // NOTE: uses monthNum (1-based), NOT monthIndex (0-based)
hideSomeDaysForInitialView: null, hideSomeDaysForInitialView: null,
selectedTimeSlotInfo: this.getSelectedTimeSlotInfo(), selectedTimeSlotInfo: this.getSelectedTimeSlotInfo(),
selectableDatesInshop: [], // NOTE: this and the mobile version below use monthNum (1-based), NOT monthIndex (0-based)
selectableDatesMobile: [],
}; };
}, },
props: { props: {
@ -182,7 +208,7 @@ export default {
type: String, type: String,
default: null, default: null,
}, },
customSelectableDatesCallback: { getMoreDatesCallback: {
type: Function, type: Function,
default() { default() {
return []; return [];
@ -202,6 +228,7 @@ export default {
estimatedServiceMinutesMinimum: Number, estimatedServiceMinutesMinimum: Number,
estimatedServiceMinutesMaximum: Number, estimatedServiceMinutesMaximum: Number,
displayWaitList: Boolean, displayWaitList: Boolean,
isMobileSelected: Boolean,
}, },
setup(props) { setup(props) {
const uuid = uuidv4(); const uuid = uuidv4();
@ -304,14 +331,16 @@ export default {
const date = new Date(dateStr.split("-")[0], parseInt(dateStr.split("-")[1]), 0); const date = new Date(dateStr.split("-")[0], parseInt(dateStr.split("-")[1]), 0);
return convertDateToDateString(date); 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 // TODO: this only is for future direction; need to create logic for past direction
const weeks = []; const weeks = [];
let weekStartDate = this.getWeekStartDate(todayString); let weekStartDate = this.getWeekStartDate(todayString);
let weekEndDate = this.getWeekEndDate(todayString); let weekEndDate = this.getWeekEndDate(todayString);
if (preSelectedDateString) { if (preSelectedDate) {
let preSelectedDateMonthEnd = this.getMonthEnd(preSelectedDateString); let preSelectedDateMonthEnd = this.getMonthEnd(
preSelectedDate.replace("-mobile", "")
);
let weekIncludesPreSelectedMonthEnd = false; let weekIncludesPreSelectedMonthEnd = false;
let i = 0; let i = 0;
while (!weekIncludesPreSelectedMonthEnd) { while (!weekIncludesPreSelectedMonthEnd) {
@ -402,7 +431,7 @@ export default {
}, },
async loadInitialData(config) { 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 todayDateString;
let calendarViewDirection = "none"; let calendarViewDirection = "none";
@ -421,7 +450,7 @@ export default {
const currentMonthEnd = this.getMonthEnd(todayDateString); const currentMonthEnd = this.getMonthEnd(todayDateString);
// TODO - set up currentMonthStart if direction is PAST: // 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( const initialViewWeeks = this.getInitialViewWeeks(
todayDateString, todayDateString,
config.initialViewRowsToShow, config.initialViewRowsToShow,
@ -451,10 +480,9 @@ export default {
} }
const loadInitialDataPromise = new Promise((resolve, reject) => { const loadInitialDataPromise = new Promise((resolve, reject) => {
const response = config.customSelectableDatesCallback( const response = config.getSelectableDatesCallback(
initialViewStartDate, initialViewStartDate,
initialViewEndDate, initialViewEndDate,
store.getters.order.serviceLocation.appointmentType,
store.getters.order.serviceLocation.provider.providerNumber store.getters.order.serviceLocation.provider.providerNumber
); );
resolve(response); resolve(response);
@ -480,9 +508,17 @@ export default {
const direction = config.calendarViewDirection; const direction = config.calendarViewDirection;
const monthsAfterToLoadOffset = 6; const monthsAfterToLoadOffset = 6;
const monthsBeforeToLoadOffset = 36; 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 // GENERATE MONTHS AND PUSH THEM INTO ARRAY
const months = []; const months = [];
@ -534,7 +570,7 @@ export default {
} }
}, },
async getMonthData(offset = requiredParameter(), options) { async getMonthData(offset = requiredParameter(), options) {
/* options will contain: /* options contains:
calendarViewDirection (string) calendarViewDirection (string)
initialViewStartDateNum (number) initialViewStartDateNum (number)
initialViewEndDateNum (number) initialViewEndDateNum (number)
@ -543,9 +579,14 @@ export default {
hideSecondMonth (boolean), hideSecondMonth (boolean),
preSelectedDate (string) preSelectedDate (string)
data used: data variables used:
todayDate (date object) this.todayString (date string)
this.currentWeekStartDateNum
this.currentWeekEndDateNum
this.todayDateNum
this.hideSomeDaysForInitialView (string) this.hideSomeDaysForInitialView (string)
this.selectableDatesInshop
this.selectableDatesMobile
*/ */
let monthNum = convertDateStringToDate(this.todayString).getMonth() + offset + 1; let monthNum = convertDateStringToDate(this.todayString).getMonth() + offset + 1;
@ -568,7 +609,8 @@ export default {
const preSelectedDateObj = options.preSelectedDate const preSelectedDateObj = options.preSelectedDate
? convertDateStringToDate(options.preSelectedDate) ? convertDateStringToDate(options.preSelectedDate)
: null; : null;
const dates = []; let datesInshop = [];
let datesMobile = [];
const monthEndDate = new Date(yearNum, monthNum, 0); const monthEndDate = new Date(yearNum, monthNum, 0);
const monthStartDateNum = const monthStartDateNum =
offset === 0 && calendarViewDirection === "future" offset === 0 && calendarViewDirection === "future"
@ -620,64 +662,74 @@ export default {
monthClass = monthClass + " last-available-month"; monthClass = monthClass + " last-available-month";
} }
// populate dates array const getDates = (selectableDates, datesArray) => {
for (let i = monthStartDateNum; i <= monthEndDateNum; i++) { // populate dates array
let dayClasses = ""; for (let i = monthStartDateNum; i <= monthEndDateNum; i++) {
const dateString = let dayClasses = "";
yearNum.toString() + const dateString =
"-" + yearNum.toString() +
("0" + monthNum).slice(-2) + "-" +
"-" + ("0" + monthNum).slice(-2) +
("0" + i).slice(-2); "-" +
const dayIndex = convertDateStringToDate(dateString).getDay(); ("0" + i).slice(-2);
const dayObject = DAYS_OF_WEEK[dayIndex]; const dayIndex = convertDateStringToDate(dateString).getDay();
const isSelectable = const dayObject = DAYS_OF_WEEK[dayIndex];
this.selectableDatesData.findIndex((date) => date.date === dateString) > -1 const isAvailable =
? true selectableDates.findIndex((date) => date.date === dateString) > -1
: false; ? true
const isPricingByDayUpchargeDay = dayObject.isPricingByDayUpchargeDay; : false;
const displayPrice = isPricingByDayUpchargeDay const isPricingByDayUpchargeDay = dayObject.isPricingByDayUpchargeDay;
? options.pricingByDayBasePrice + options.pricingByDayUpcharge const displayPrice = isPricingByDayUpchargeDay
: options.pricingByDayBasePrice; ? options.pricingByDayBasePrice + options.pricingByDayUpcharge
const priceString = "$" + displayPrice; : options.pricingByDayBasePrice;
const priceString = "$" + displayPrice;
if (offset === 0 && i === this.todayDateNum) { if (offset === 0 && i === this.todayDateNum) {
dayClasses += " current-day"; 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") { return datesArray;
dayClasses += " unavailable-day"; };
} datesInshop = getDates(this.selectableDatesInshop, datesInshop);
if (offset === 0 && i > this.todayDateNum && calendarViewDirection === "past") { datesMobile = getDates(this.selectableDatesMobile, datesMobile);
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);
}
const monthToAdd = { const monthToAdd = {
monthLabel: MONTHS_OF_YEAR[monthNum - 1], monthLabel: MONTHS_OF_YEAR[monthNum - 1],
monthIndex: monthNum, monthIndex: monthNum,
monthString: MONTHS_OF_YEAR[monthNum - 1] + "-" + yearNum?.toString(), monthString: MONTHS_OF_YEAR[monthNum - 1] + "-" + yearNum?.toString(),
yearNum: yearNum, yearNum: yearNum,
dates: dates, datesInshop: datesInshop,
datesMobile: datesMobile,
startDateDayIndex: startDateDayIndex, startDateDayIndex: startDateDayIndex,
monthClass: monthClass, monthClass: monthClass,
isMonthThatHidesSomeDaysForInitialView: isMonthThatHidesSomeDaysForInitialView, isMonthThatHidesSomeDaysForInitialView: isMonthThatHidesSomeDaysForInitialView,
@ -697,9 +749,13 @@ export default {
); );
// find the first day-hidden to become the next api call start date // find the first day-hidden to become the next api call start date
monthStartDate = monthToShow.dates.find(({ dayClasses }) => monthStartDate = this.isMobileSelected
dayClasses.includes("day-hidden") ? monthToShow.datesMobile.find(({ dayClasses }) =>
); dayClasses.includes("day-hidden")
)
: monthToShow.datesInshop.find(({ dayClasses }) =>
dayClasses.includes("day-hidden")
);
} else { } else {
if (this.calendarViewDirection === "future") { if (this.calendarViewDirection === "future") {
monthToShow = this.months.find((month) => monthToShow = this.months.find((month) =>
@ -714,15 +770,19 @@ export default {
} }
} }
if (monthToShow) { if (monthToShow) {
const monthToShowDates = this.isMobileSelected
? monthToShow.datesMobile
: monthToShow.datesInshop;
const monthStartDateString = monthStartDate const monthStartDateString = monthStartDate
? monthStartDate.inputValue ? monthStartDate.inputValue
: monthToShow.dates[0].inputValue; : monthToShowDates[0].inputValue;
// make new API call with this month's start and end dates // make new API call with this month's start and end dates
const moreSelectableDatesData = await this.updateSelectableDates( const moreSelectableDatesData = await this.updateSelectableDates(
monthStartDateString, monthStartDateString,
monthToShow.dates[monthToShow.dates.length - 1].inputValue monthToShowDates[monthToShowDates.length - 1].inputValue
); );
this.isLoading = false; this.isLoading = false;
this.hideSomeDaysForInitialView = false; // if hid days on initial partial view, this will reveal those days this.hideSomeDaysForInitialView = false; // if hid days on initial partial view, this will reveal those days
monthToShow.monthClass = monthToShow.monthClass.replace(" month-hidden", ""); monthToShow.monthClass = monthToShow.monthClass.replace(" month-hidden", "");
@ -735,27 +795,37 @@ export default {
} }
}, },
async updateSelectableDates(monthStart, monthEnd) { async updateSelectableDates(monthStart, monthEnd) {
const moreSelectableDates = await this.customSelectableDatesCallback( const moreSelectableDates = await this.getMoreDatesCallback(monthStart, monthEnd);
monthStart,
monthEnd,
this.$store.getters.order.serviceLocation.appointmentType,
this.$store.getters.order.serviceLocation.provider.providerNumber
);
moreSelectableDates.days.forEach((selectableDate) => { moreSelectableDates.inshopTimeSlotsData.days.forEach((selectableDate) => {
const index = this.selectableDatesData.findIndex( const index = this.selectableDatesInshop.findIndex(
(dateObj) => dateObj.date === selectableDate.date (dateObj) => dateObj.date === selectableDate.date
); );
if (index === -1) this.selectableDatesData.push(selectableDate); if (index === -1) this.selectableDatesInshop.push(selectableDate);
this.months.forEach((month) => { this.months.forEach((month) => {
// TODO: avoid checking all calendar dates; maybe only ones between monthStart and monthEnd as defined above? // 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) { 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; return moreSelectableDates;
}, },
scrollToElement(elementId, duration = 800, easing = "ease-in-out") { scrollToElement(elementId, duration = 800, easing = "ease-in-out") {
@ -913,8 +983,7 @@ export default {
.month-year { .month-year {
grid-area: 1 / 1 / 2 / 5; grid-area: 1 / 1 / 2 / 5;
font-family: AvertaBold; font-family: UrbanistBold;
letter-spacing: 0.75px;
color: $black; color: $black;
justify-content: center; justify-content: center;
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
@ -1209,7 +1278,7 @@ export default {
border-radius: 0.25rem; border-radius: 0.25rem;
color: $black; color: $black;
span { span {
font-family: AvertaSemibold; font-family: UrbanistSemibold;
font-weight: 400; font-weight: 400;
} }
} }
@ -1240,7 +1309,7 @@ export default {
&.current-day { &.current-day {
label { label {
font-weight: 600; font-weight: 600;
font-family: "AvertaSemibold"; font-family: "UrbanistSemibold";
color: $black; color: $black;
font-size-adjust: 0.5; // needed for Averta fonts baseline alignment font-size-adjust: 0.5; // needed for Averta fonts baseline alignment
} }
@ -1252,11 +1321,10 @@ export default {
text-decoration: none; text-decoration: none;
color: $black; color: $black;
font-weight: 400; font-weight: 400;
font-family: "AvertaSemibold"; font-family: "UrbanistSemibold";
font-size-adjust: 0.5; // needed for Averta fonts baseline alignment font-size-adjust: 0.5; // needed for Averta fonts baseline alignment
&.price { &.price {
font-family: "AvertaRegular";
font-weight: 400; font-weight: 400;
} }
} }
@ -1274,7 +1342,7 @@ export default {
color: $blue; color: $blue;
span { span {
font-weight: 400; 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) { :deep(.button-inner-wrapper) {

View file

@ -136,8 +136,8 @@ export default {
<style lang="scss"> <style lang="scss">
.dropdown-question { .dropdown-question {
label { label {
font-family: UrbanistSemibold;
color: $black; color: $black;
font-weight: 500;
} }
.label-bold { .label-bold {
color: $black; color: $black;

View file

@ -129,7 +129,6 @@ export default {
<style lang="scss"> <style lang="scss">
.dockableContainer { .dockableContainer {
&.showDockableContainer { &.showDockableContainer {
font-family: AvertaRegular;
color: $gray-600; color: $gray-600;
.sidebarBody { .sidebarBody {
button { button {

View file

@ -254,8 +254,8 @@ export default {
justify-content: center !important; justify-content: center !important;
} }
label { label {
font-family: UrbanistSemibold;
color: $black; color: $black;
font-weight: 500;
} }
.label-bold { .label-bold {
font-weight: 600; font-weight: 600;

View file

@ -177,13 +177,13 @@ export default {
p { p {
&.headline { &.headline {
font-family: AvertaBold; font-family: UrbanistBold;
color: $red; color: $red;
font-weight: 600; font-weight: 600;
} }
&.subheadline { &.subheadline {
font-family: AvertaBold; font-family: UrbanistBold;
font-weight: 600; font-weight: 600;
} }
} }

View file

@ -424,7 +424,6 @@ export default {
text-transform: uppercase; text-transform: uppercase;
font-size: 0.875rem; font-size: 0.875rem;
font-weight: 600; font-weight: 600;
letter-spacing: 0.75px;
} }
.special-save-box { .special-save-box {
background-color: $green-100; background-color: $green-100;

View file

@ -476,7 +476,6 @@ export default {
text-transform: uppercase; text-transform: uppercase;
font-size: 0.875rem; font-size: 0.875rem;
font-weight: 600; font-weight: 600;
letter-spacing: 0.75px;
} }
.special-save-box { .special-save-box {
background-color: $green-100; background-color: $green-100;

View file

@ -1312,7 +1312,7 @@ export default {
.amount-paid, .amount-paid,
.donation-amount { .donation-amount {
font-family: 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; color: $black;
} }
.sub-total { .sub-total {
@ -1358,6 +1358,7 @@ export default {
visibility: visible; visibility: visible;
} }
a { a {
font-family: UrbanistSemibold;
text-decoration: none; text-decoration: none;
} }
} }

View file

@ -99,9 +99,7 @@ export default {
lowerCaseParams.append(name.toLowerCase(), value); lowerCaseParams.append(name.toLowerCase(), value);
} }
const zip = const zip = store.getters.order.serviceLocation.zipCode;
peekQueryFromStash(queryStrings.ZIP_CODE) ??
store.getters.order.serviceLocation.zipCode;
var vinByAddressPromise; var vinByAddressPromise;
if (zip) { if (zip) {

View file

@ -118,8 +118,6 @@ import baseMixin from "@/mixins/base-mixin.js";
import store from "@/store"; import store from "@/store";
import vinPagesMixin from "@/mixins/vin-pages-mixin"; import vinPagesMixin from "@/mixins/vin-pages-mixin";
import { routeData } from "@/router/constants/routes"; import { routeData } from "@/router/constants/routes";
import { consumeQueryFromStash } from "@/router/methods/helpers/querystring-stash";
import { queryStrings } from "@/constants/query-strings";
// DEFINE VALIDATION RULES // DEFINE VALIDATION RULES
defineRule("license-plate-required", required(errorMessages.LICENSE_PLATE_REQUIRED)); defineRule("license-plate-required", required(errorMessages.LICENSE_PLATE_REQUIRED));
@ -164,8 +162,7 @@ export default {
data() { data() {
return { return {
licensePlate: this.getLicensePlateFromStore(), licensePlate: this.getLicensePlateFromStore(),
registrationZipCode: registrationZipCode: this.getRegistrationZipFromStore(),
this.getRegistrationZipFromStore() ?? consumeQueryFromStash(queryStrings.ZIP_CODE),
emailOrSms: this.getEmailOrSmsFromStore(), emailOrSms: this.getEmailOrSmsFromStore(),
serviceZipCode: this.getServiceZipFromStore(), serviceZipCode: this.getServiceZipFromStore(),
carId: this.getCarIdfromStore(), carId: this.getCarIdfromStore(),

View file

@ -4,7 +4,7 @@
<textBlock <textBlock
:cmsWidgetName="headerCmsWidgetName" :cmsWidgetName="headerCmsWidgetName"
:customText="customHeaderText" :customText="customHeaderText"
typeStyle="body small bold dark" typeStyle="body small dark"
marginTopSizeOverride="0" /> marginTopSizeOverride="0" />
</div> </div>
<div <div

View file

@ -145,6 +145,10 @@ export default {
} }
} }
:deep(.review-table .text-block span) {
font-family: UrbanistSemibold;
}
.review-toggle { .review-toggle {
&.expanded { &.expanded {
margin-bottom: 0; margin-bottom: 0;
@ -177,8 +181,8 @@ export default {
text-decoration: none; text-decoration: none;
} }
.label { .label {
font-family: UrbanistSemibold;
color: $black; color: $black;
font-weight: 500;
line-height: 1.625; line-height: 1.625;
} }
} }

View file

@ -405,7 +405,6 @@ export default {
text-transform: uppercase; text-transform: uppercase;
font-size: 0.875rem; font-size: 0.875rem;
font-weight: 600; font-weight: 600;
letter-spacing: 0.75px;
} }
.special-save-box { .special-save-box {
background-color: $green-100; background-color: $green-100;

View file

@ -12,7 +12,7 @@ import baseMixin from "../../mixins/base-mixin";
jest.mock("@/mixins/base-mixin.js", () => ({ jest.mock("@/mixins/base-mixin.js", () => ({
methods: { methods: {
dispatchStoreAction: jest.fn().mockImplementation((storeAction) => { dispatchStoreAction: jest.fn().mockImplementation((storeAction) => {
if (storeAction === "getShopTimeSlots") { if (storeAction === "getShopTimeSlots" || storeAction === "getMobileTimeSlots") {
return { return {
data: { data: {
estimatedServiceMinutesMinimum: 90, estimatedServiceMinutesMinimum: 90,
@ -71,7 +71,7 @@ jest.mock("@/mixins/base-mixin.js", () => ({
} }
}), }),
dispatchStoreActionWithLogging: jest.fn().mockImplementation((storeAction) => { dispatchStoreActionWithLogging: jest.fn().mockImplementation((storeAction) => {
if (storeAction === "getShopTimeSlots") { if (storeAction === "getShopTimeSlots" || storeAction === "getMobileTimeSlots") {
return { return {
data: { data: {
estimatedServiceMinutesMinimum: 90, estimatedServiceMinutesMinimum: 90,
@ -243,48 +243,73 @@ describe("schedule.vue...", () => {
expect(arePagePrerequisitesValid).toBe(false); expect(arePagePrerequisitesValid).toBe(false);
}); });
test("should return newShopTimeSlots when getAvailableDatesMethod is called", async () => { test("should return timeslots when getMoreScheduleData is called", async () => {
// Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
wrapper.vm.selectableDatesData = { wrapper.vm.selectableDatesInshop = {
days: [],
};
wrapper.vm.selectableDatesMobile = {
days: [], days: [],
}; };
// Act // Act
const newShopTimeSlots = await wrapper.vm.getAvailableDatesMethod( const newShopTimeSlots = await wrapper.vm.getMoreScheduleData(
"2023-01-01", "2023-01-01",
"2023-01-31" "2023-01-31"
); );
// Assert // Assert
expect(newShopTimeSlots).toStrictEqual({ expect(newShopTimeSlots).toStrictEqual({
days: [ inshopTimeSlotsData: {
{ days: [
date: "2023-12-01", {
timeSlots: [ date: "2023-12-01",
{ timeSlots: [
endTime: "08:00", {
id: "06747-01820-S-B*20424*7 AM", endTime: "08:00",
offerPremium: false, id: "06747-01820-S-B*20424*7 AM",
startTime: "07:00", offerPremium: false,
}, startTime: "07:00",
], },
}, ],
], },
estimatedServiceMinutesMinimum: 90, ],
estimatedServiceMinutesMaximum: 120, 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 // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
wrapper.vm.selectableDatesData = { wrapper.vm.selectableDatesInshop = {
days: [],
};
wrapper.vm.selectableDatesMobile = {
days: [], days: [],
}; };
// Act // Act
await wrapper.vm.getAvailableDates.call( await wrapper.vm.getMoreScheduleData.call(
wrapper.vm, wrapper.vm,
"2023-01-01", "2023-01-01",
"2023-03-31", "2023-03-31",
@ -293,7 +318,7 @@ describe("schedule.vue...", () => {
); );
// Assert // Assert
expect(baseMixin.methods.dispatchStoreActionWithLogging).toHaveBeenCalledTimes(3); expect(baseMixin.methods.dispatchStoreActionWithLogging).toHaveBeenCalledTimes(6);
expect(baseMixin.methods.dispatchStoreActionWithLogging).toHaveBeenCalledWith( expect(baseMixin.methods.dispatchStoreActionWithLogging).toHaveBeenCalledWith(
"getShopTimeSlots", "getShopTimeSlots",
expect.anything(), expect.anything(),
@ -306,7 +331,7 @@ describe("schedule.vue...", () => {
test("should call next() and call all functions within next", async () => { test("should call next() and call all functions within next", async () => {
// Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
wrapper.vm.selectableDatesData = { wrapper.vm.selectableDatesInshop = {
days: [], days: [],
}; };
wrapper.vm.updateFooterButtonText = jest.fn(); wrapper.vm.updateFooterButtonText = jest.fn();
@ -332,7 +357,7 @@ describe("schedule.vue...", () => {
}) })
); );
expect(wrapper.vm.$refs.locationAlerts.initializeComponent).toHaveBeenCalled(); expect(wrapper.vm.$refs.locationAlerts.initializeComponent).toHaveBeenCalled();
expect(wrapper.vm.selectableDatesData).toStrictEqual( expect(wrapper.vm.selectableDatesInshop).toStrictEqual(
expect.objectContaining({ expect.objectContaining({
days: expect.any(Array), days: expect.any(Array),
estimatedServiceMinutesMaximum: expect.any(Number), estimatedServiceMinutesMaximum: expect.any(Number),
@ -353,7 +378,7 @@ describe("schedule.vue...", () => {
test("timeSlotsForSelectedDate should return timeslots if selected date is available", () => { test("timeSlotsForSelectedDate should return timeslots if selected date is available", () => {
// Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
wrapper.vm.selectableDatesData = { wrapper.vm.selectableDatesInshop = {
days: [ days: [
{ {
date: "2022-11-11", date: "2022-11-11",
@ -392,7 +417,7 @@ describe("schedule.vue...", () => {
test("timeSlotsForSelectedDate should be null if no date has been selected", () => { test("timeSlotsForSelectedDate should be null if no date has been selected", () => {
// Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
wrapper.vm.selectableDatesData = { wrapper.vm.selectableDatesInshop = {
days: [ days: [
{ {
date: "2022-11-11", date: "2022-11-11",
@ -430,7 +455,7 @@ describe("schedule.vue...", () => {
test("getServiceZipCtuCodeFromStore should return zipCodeCtu", () => { test("getServiceZipCtuCodeFromStore should return zipCodeCtu", () => {
// Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
wrapper.vm.selectableDatesData = { wrapper.vm.selectableDatesInshop = {
days: [], days: [],
}; };
@ -444,7 +469,7 @@ describe("schedule.vue...", () => {
test("getDisplayTextForMilitaryTime should return the correctly formatted string", () => { test("getDisplayTextForMilitaryTime should return the correctly formatted string", () => {
// Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
wrapper.vm.selectableDatesData = { wrapper.vm.selectableDatesInshop = {
days: [], days: [],
}; };
const timeInput1 = "15:00"; const timeInput1 = "15:00";
@ -466,7 +491,7 @@ describe("schedule.vue...", () => {
test("Clicking back should fire correct navigation", () => { test("Clicking back should fire correct navigation", () => {
// Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
wrapper.vm.selectableDatesData = { wrapper.vm.selectableDatesInshop = {
days: [], days: [],
}; };
wrapper.vm.$router.navigateWithoutSaving = jest.fn(); wrapper.vm.$router.navigateWithoutSaving = jest.fn();
@ -501,51 +526,6 @@ describe("schedule.vue...", () => {
expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled(); 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 () => { test("for Inshop appts, updateSupportingItems should call store action to save supporting items WITHOUT the EARLY BIRD supporting item", async () => {
// Arrange // Arrange
store.getters.order.serviceLocation.appointmentType = "Inshop"; store.getters.order.serviceLocation.appointmentType = "Inshop";

View file

@ -23,8 +23,9 @@
selectableDatesSetting="custom" selectableDatesSetting="custom"
ref="datePicker" ref="datePicker"
v-model="selectedDate" v-model="selectedDate"
:isMobileSelected="isMobileSelected"
class="text-link-small" class="text-link-small"
:customSelectableDatesCallback="getAvailableDatesMethod" :getMoreDatesCallback="getMoreScheduleData"
validationRules="date-required" validationRules="date-required"
@date-clicked="handleDateClicked" @date-clicked="handleDateClicked"
:pricingByDayBasePrice="pricingByDayBasePrice" :pricingByDayBasePrice="pricingByDayBasePrice"
@ -34,12 +35,8 @@
:timeSlotsForSelectedDate="timeSlotsForSelectedDate" :timeSlotsForSelectedDate="timeSlotsForSelectedDate"
:appointmentType="appointmentType" :appointmentType="appointmentType"
:premiumAppointmentFee="mobilePremiumAppointmentFee" :premiumAppointmentFee="mobilePremiumAppointmentFee"
:estimatedServiceMinutesMinimum=" :estimatedServiceMinutesMinimum="getServiceMinutesMin"
selectableDatesData.estimatedServiceMinutesMinimum :estimatedServiceMinutesMaximum="getServiceMinutesMax"
"
:estimatedServiceMinutesMaximum="
selectableDatesData.estimatedServiceMinutesMaximum
"
@TimeSlotSelected="updateTimeSlot" @TimeSlotSelected="updateTimeSlot"
:displayWaitList="displayWaitList" :displayWaitList="displayWaitList"
@waitListRequested="handleWaitListRequested" /> @waitListRequested="handleWaitListRequested" />
@ -67,8 +64,6 @@ import locationAlerts from "@/layouts/schedule/location-alerts/location-alerts";
import textBlock from "@/digital-components/text-block/text-block"; import textBlock from "@/digital-components/text-block/text-block";
// Supporting files // Supporting files
import { queryStrings } from "@/constants/query-strings";
import { getQuerystringParameter } from "@/helpers/querystring-helper";
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import baseMixin from "@/mixins/base-mixin.js"; import baseMixin from "@/mixins/base-mixin.js";
import { storeActions } from "@/constants/store-actions"; import { storeActions } from "@/constants/store-actions";
@ -101,24 +96,19 @@ defineRule("date-required", required(errorMessages.DATE_REQUIRED));
// Define constants // Define constants
const TIME_SLOTS_CALL_DAYS_LIMIT = 34; // needs to be 34 for API limits (35 does not consistently work) 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 ( const getScheduleApiResponse = async (startDateString, endDateString, providerNumber) => {
startDateString,
endDateString,
appointmentType,
providerNumber
) => {
const apiEndDateLimit = sumDateString(startDateString, TIME_SLOTS_CALL_DAYS_LIMIT); const apiEndDateLimit = sumDateString(startDateString, TIME_SLOTS_CALL_DAYS_LIMIT);
const difference = calcDaysBetweenDates(startDateString, endDateString); const difference = calcDaysBetweenDates(startDateString, endDateString);
const apiCallsCount = Math.ceil(difference / TIME_SLOTS_CALL_DAYS_LIMIT); const apiCallsCount = Math.ceil(difference / TIME_SLOTS_CALL_DAYS_LIMIT);
const storeActionConfigs = []; const storeActionConfigs = [];
const timeSlotsData = {};
timeSlotsData.days = [];
let apiStartDate = startDateString; let apiStartDate = startDateString;
let apiEndDate = endDateString; let apiEndDate = endDateString;
for (let i = 1; i <= apiCallsCount; i++) { for (let i = 1; i <= apiCallsCount; i++) {
let storeActionConfig; let storeActionConfigInshop;
let storeActionConfigMobile;
if (i > 1) { if (i > 1) {
apiStartDate = sumDateString(apiEndDate, 1); apiStartDate = sumDateString(apiEndDate, 1);
@ -133,29 +123,32 @@ const getAvailableDates = async (
} }
} }
if (appointmentType === AppointmentTypeStrings.MOBILE) { storeActionConfigMobile = {
storeActionConfig = { storeAction: storeActions.GET_MOBILE_TIME_SLOTS,
storeAction: storeActions.GET_MOBILE_TIME_SLOTS, payload: {
payload: { startDate: apiStartDate,
startDate: apiStartDate, endDate: apiEndDate,
endDate: apiEndDate, },
}, };
}; storeActionConfigInshop = {
} else { storeAction: storeActions.GET_SHOP_TIME_SLOTS,
storeActionConfig = { payload: {
storeAction: storeActions.GET_SHOP_TIME_SLOTS, startDate: apiStartDate,
payload: { endDate: apiEndDate,
startDate: apiStartDate, shopAppointmentType: "InshopOrDropoff",
endDate: apiEndDate, providerNumber: providerNumber,
shopAppointmentType: appointmentType, },
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: [], days: [],
}; };
@ -167,39 +160,71 @@ const getAvailableDates = async (
const makeParallelCalls = async () => { const makeParallelCalls = async () => {
await Promise.all( await Promise.all(
storeActionConfigs.map(async (storeAction) => { storeActionConfigs.map(async (storeActionConfig) => {
const timeSlotsResponse = await baseMixin.methods.dispatchStoreActionWithLogging( if (storeActionConfig.storeAction.indexOf("Mobile") > -1) {
storeAction.storeAction, const mobileTimeSlotsResponse =
storeAction.payload, await baseMixin.methods.dispatchStoreActionWithLogging(
"schedule", storeActionConfig.storeAction,
false storeActionConfig.payload,
); "schedule",
timeSlotsResponsesData.estimatedServiceMinutesMinimum = false
timeSlotsResponse.data.estimatedServiceMinutesMinimum; );
timeSlotsResponsesData.estimatedServiceMinutesMaximum = mobileTimeSlotsData.estimatedServiceMinutesMinimum =
timeSlotsResponse.data.estimatedServiceMinutesMaximum; mobileTimeSlotsResponse.data.estimatedServiceMinutesMinimum;
timeSlotsResponsesData.days = [ mobileTimeSlotsData.estimatedServiceMinutesMaximum =
...timeSlotsResponsesData.days, mobileTimeSlotsResponse.data.estimatedServiceMinutesMaximum;
...timeSlotsResponse.data.days, 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(() => { return makeParallelCalls().then(() => {
// sort days chronologically // sort days chronologically
timeSlotsResponsesData.days.sort(compareDayStrings); inshopTimeSlotsData.days.sort(compareDayStrings);
return timeSlotsResponsesData; 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 { export default {
name: "schedule", name: "schedule",
data() { data() {
return { return {
selectedDate: this.getSelectedDate(), selectedDate: this.getSelectedDate(),
selectedTimeSlotInfo: this.getSelectedTimeSlotInfo(), selectedTimeSlotInfo: this.getSelectedTimeSlotInfo(),
selectableDatesData: [],
mobilePremiumAppointmentFee: null, mobilePremiumAppointmentFee: null,
waitListRequested: null, waitListRequested: null,
displayWaitList: null, displayWaitList: null,
@ -209,6 +234,9 @@ export default {
pricingByDayBasePrice: null, pricingByDayBasePrice: null,
pricingByDayUpcharge: null, pricingByDayUpcharge: null,
showPricingByDay: null, showPricingByDay: null,
selectableDatesInshop: [],
selectableDatesMobile: [],
isMobileSelected: false,
}; };
}, },
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
@ -230,7 +258,6 @@ export default {
isRecalibrationOnOrder && shouldHideRecalibration isRecalibrationOnOrder && shouldHideRecalibration
? getItemsWithoutRecalParts(lineItems.glassParts) ? getItemsWithoutRecalParts(lineItems.glassParts)
: (lineItems.glassParts ?? []); : (lineItems.glassParts ?? []);
const supportingItemsFromStore = lineItems?.supportingItems;
const supportingItemsWithoutFees = baseMixin.methods.filterOutCertainPartTypesOrNumbers( const supportingItemsWithoutFees = baseMixin.methods.filterOutCertainPartTypesOrNumbers(
lineItems.supportingItems, lineItems.supportingItems,
{ {
@ -249,10 +276,14 @@ export default {
const priceString = getAmountDue(lineItemsToBePriced, false); // pass the IncludeTax param as false 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 priceStringIntegerRoundedDown = priceString?.split(".")[0]; // same method used as getDisplayPrice() in service-package-radio used on /quote
const pricingByDayBasePrice = parseInt(priceStringIntegerRoundedDown); const pricingByDayBasePrice = parseInt(priceStringIntegerRoundedDown);
const isMobileSelected =
store.getters.order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE;
let includePricingByDayUpcharge = false; let includePricingByDayUpcharge = false;
// Check to see if date should be pre-selected // Check to see if date should be pre-selected
let preSelectedSlot = await store.getters.order.schedule; 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) { if (!preSelectedSlot.date || preSelectedSlot?.date?.length < 1) {
preSelectedSlot = null; preSelectedSlot = null;
} else { } else {
@ -279,9 +310,9 @@ export default {
const datePickerInitialDataPromise = await datePicker.methods.loadInitialData({ const datePickerInitialDataPromise = await datePicker.methods.loadInitialData({
// setup config options for date-picker // setup config options for date-picker
selectableDatesSetting: "custom", selectableDatesSetting: "custom",
initialViewRowsToShow: 2, initialViewRowsToShow: NUMBER_OF_CALENDAR_ROWS_TO_SHOW_FOR_INITIAL_VIEW,
customSelectableDatesCallback: getAvailableDates, getSelectableDatesCallback: getScheduleApiResponse,
preSelectedDate: preSelectedSlot ? preSelectedSlot.date : preSelectedSlot, preSelectedDate: preSelectedDate,
}); });
// Get pricingByDayUpcharge needed for Pricing By Day // Get pricingByDayUpcharge needed for Pricing By Day
@ -351,7 +382,10 @@ export default {
vm.setCmsContent(resultMap.cmsContent); vm.setCmsContent(resultMap.cmsContent);
vm.$refs.datePicker.initializeComponent(datePickerInitialData); vm.$refs.datePicker.initializeComponent(datePickerInitialData);
vm.$refs.locationAlerts.initializeComponent(resultMap.alertReasons); vm.$refs.locationAlerts.initializeComponent(resultMap.alertReasons);
vm.selectableDatesData = datePickerInitialData.initialShopTimeSlotsResponse; vm.selectableDatesInshop =
datePickerInitialData.initialShopTimeSlotsResponse.inshopTimeSlotsData;
vm.selectableDatesMobile =
datePickerInitialData.initialShopTimeSlotsResponse.mobileTimeSlotsData;
vm.mobilePremiumAppointmentFee = resultMap.premiumFeeWithPrice vm.mobilePremiumAppointmentFee = resultMap.premiumFeeWithPrice
? resultMap.premiumFeeWithPrice[0] ? resultMap.premiumFeeWithPrice[0]
: null; : null;
@ -363,21 +397,33 @@ export default {
vm.pricingByDayBasePrice = pricingByDayBasePrice; vm.pricingByDayBasePrice = pricingByDayBasePrice;
vm.pricingByDayUpcharge = pricingByDayUpcharge; vm.pricingByDayUpcharge = pricingByDayUpcharge;
vm.showPricingByDay = showPricingByDay; vm.showPricingByDay = showPricingByDay;
vm.isMobileSelected = isMobileSelected;
}); });
}, },
mounted() { mounted() {
this.$nextTick(() => { this.$nextTick(() => {
const selectableDatesData = this.selectableDatesData;
// if no date selected on load
if (!this.selectedDate) { if (!this.selectedDate) {
if (selectableDatesData?.days?.length > 0) { // if no date is preselected on load, then select the first available
this.selectedDate = selectableDatesData.days[0].date; if (this.isMobileSelected) {
this.selectedDate = returnFirstDate(this.selectableDatesMobile);
} else { } else {
this.selectedDate = returnFirstDate(this.selectableDatesInshop);
}
// if there is still no selected date, then load more and try again
if (!this.selectedDate) {
setTimeout(() => { setTimeout(() => {
this.$refs.datePicker.showAnotherMonth().then((moreSelectableDatesData) => { this.$refs.datePicker.showAnotherMonth().then((moreSelectableDates) => {
this.selectableDatesData = moreSelectableDatesData; // update global
if (selectableDatesData?.days?.length > 0) { this.selectableDatesInshop.days =
this.selectedDate = selectableDatesData.days[0].date; 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(); this.setDisplayWaitList();
@ -396,27 +442,46 @@ export default {
return this.splitCopyOnCMSPlaceHolder(this.ChangeShopLinkText); return this.splitCopyOnCMSPlaceHolder(this.ChangeShopLinkText);
}, },
appointmentType() { 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() { timeSlotsForSelectedDate() {
if (!this.selectedDate) return null; if (!this.selectedDate) return null;
const selectedDate = this.selectedDate;
return this.selectableDatesData.days?.find( if (this.isMobileSelected) {
(selectableDate) => selectableDate.date === this.selectedDate 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: { methods: {
splitCopyOnCMSPlaceHolder, splitCopyOnCMSPlaceHolder,
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
const serviceLocation = store.getters.order.serviceLocation; const serviceLocation = store.getters.order.serviceLocation; // TODO - MAY NEED TO REMOVE THIS AS A PREREQ
const serviceLocationPreReqs = const serviceLocationPreReqs = serviceLocation.zipCode && serviceLocation.zipCodeCtu; // TODO - MAY NEED TO ADD IN SERVICE LOCATION PREREQS HERE TOO
serviceLocation.zipCode &&
serviceLocation.zipCodeCtu &&
serviceLocation.appointmentType &&
(serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE ||
serviceLocation.provider.providerNumber);
const paymentInfo = store.getters.payment.isInsurance !== null; const paymentInfo = store.getters.payment.isInsurance !== null;
@ -441,26 +506,38 @@ export default {
} }
return preReqResult; return preReqResult;
}, },
async getAvailableDatesMethod(startDate, endDate) { async getMoreScheduleData(startDate, endDate) {
const newShopTimeSlots = await getAvailableDates( // called only when "View more dates" is clicked; not on initial page load
const moreShopTimeSlots = await getScheduleApiResponse(
startDate, startDate,
endDate, endDate,
this.appointmentType,
this.$store.getters.order.serviceLocation.provider.providerNumber this.$store.getters.order.serviceLocation.provider.providerNumber
); );
// ADD API CALL RESULTS TO EXISTING DATE DATA // ADD API CALL RESULTS TO EXISTING DATE DATA
this.selectableDatesData.days = this.selectableDatesData.days.concat( this.selectableDatesInshop.days = this.selectableDatesInshop.days.concat(
newShopTimeSlots.days moreShopTimeSlots.inshopTimeSlotsData.days
); );
return newShopTimeSlots; this.selectableDatesMobile.days = this.selectableDatesMobile.days.concat(
moreShopTimeSlots.mobileTimeSlotsData.days
);
return moreShopTimeSlots;
}, },
getAvailableDates, getScheduleApiResponse,
getServiceZipCtuCodeFromStore() { getServiceZipCtuCodeFromStore() {
return store.getters.order.serviceLocation.zipCodeCtu; return store.getters.order.serviceLocation.zipCodeCtu;
}, },
getSelectedDate() { 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() { getSelectedTimeSlotInfo() {
const supportingItems = this.getSupportingItems(); const supportingItems = this.getSupportingItems();
@ -513,8 +590,7 @@ export default {
}, },
convertSelectedDateToShortMonthAndDay(selectedDate) { convertSelectedDateToShortMonthAndDay(selectedDate) {
// This conversion ensures we don't get get GMT induced date changes // This conversion ensures we don't get get GMT induced date changes
const dateObject = convertDateStringToDate(selectedDate); const dateObject = convertDateStringToDate(selectedDate.replace("-mobile", ""));
// Ex: April 25
return dateObject.toLocaleDateString("en-us", { month: "short", day: "numeric" }); return dateObject.toLocaleDateString("en-us", { month: "short", day: "numeric" });
}, },
getDisplayTextForMilitaryTime(militaryTimeInput, shouldTrimMinutesIfEmpty = false) { getDisplayTextForMilitaryTime(militaryTimeInput, shouldTrimMinutesIfEmpty = false) {
@ -523,10 +599,7 @@ export default {
const minutes = militaryTimeInput.split(":")[1]; const minutes = militaryTimeInput.split(":")[1];
const meridianNotation = hours > 11 ? "PM" : "AM"; const meridianNotation = hours > 11 ? "PM" : "AM";
if (hours > 12) { if (hours > 12) hours -= 12;
hours -= 12;
}
if (shouldTrimMinutesIfEmpty && minutes === "00") { if (shouldTrimMinutesIfEmpty && minutes === "00") {
return `${hours} ${meridianNotation}`; return `${hours} ${meridianNotation}`;
} else { } else {
@ -540,15 +613,25 @@ export default {
); );
}, },
forwardButtonAction() { forwardButtonAction() {
// TODO - WILL NEED TO SAVE THE APPOINTMENT TYPE ALSO (AND ANYTHING ELSE THAT SERVICE-LOCATION DID TOO)
this.updateSupportingItems(); this.updateSupportingItems();
if (this.waitListRequested) { if (this.displayWaitList) {
var gaLabel = store.getters.isMobileAppointment let gaLabel = "";
? "checked_mobile" let status = this.waitListRequested ? "checked" : "unchecked";
: "checked_inshop"; 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 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 [year, month, day] = dateString.split("-").map(Number);
const appointmentDate = new Date(year, month - 1, day); const appointmentDate = new Date(year, month - 1, day);
const timeDifference = appointmentDate - currentDate; const timeDifference = appointmentDate - currentDate;
@ -565,11 +648,14 @@ export default {
store.getters.order.serviceLocation.zipCodeCtu; store.getters.order.serviceLocation.zipCodeCtu;
this.pushEventToGA("waitlist", "add_to_waitlist_check_box_status", gaLabel, true); this.pushEventToGA("waitlist", "add_to_waitlist_check_box_status", gaLabel, true);
} }
if (this.selectedTimeSlotInfo.timeSlot.jobMinMinutes == null) { if (this.selectedTimeSlotInfo.timeSlot.jobMinMinutes == null) {
this.selectedTimeSlotInfo.timeSlot.jobMinMinutes = this.selectedTimeSlotInfo.timeSlot.jobMinMinutes = this.isMobileSelected
this.selectableDatesData?.estimatedServiceMinutesMinimum?.toString(); ? this.selectableDatesMobile.estimatedServiceMinutesMinimum?.toString()
this.selectedTimeSlotInfo.timeSlot.jobMaxMinutes = : this.selectableDatesInshop.estimatedServiceMinutesMinimum?.toString();
this.selectableDatesData?.estimatedServiceMinutesMaximum?.toString(); this.selectedTimeSlotInfo.timeSlot.jobMaxMinutes = this.isMobileSelected
? this.selectableDatesMobile.estimatedServiceMinutesMaximum?.toString()
: this.selectableDatesInshop.estimatedServiceMinutesMaximum?.toString();
} }
this.dispatchStoreAction( this.dispatchStoreAction(
@ -597,9 +683,12 @@ export default {
experimentSettings.DISPLAY_WAITLIST, experimentSettings.DISPLAY_WAITLIST,
"true" "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 [year, month, day] = dateString.split("-").map(Number);
const targetDate = new Date(year, month - 1, day); const targetDate = new Date(year, month - 1, day);
const currentDate = new Date(); const currentDate = new Date();
@ -765,7 +854,7 @@ export default {
} }
} }
.change-location a { .change-location a {
font-family: AvertaSemibold; font-family: UrbanistSemibold;
} }
.time-slots-question { .time-slots-question {
padding: 0 0.75rem; padding: 0 0.75rem;

View file

@ -631,7 +631,6 @@ export default {
margin-left: 0.5rem; margin-left: 0.5rem;
strong { strong {
font-weight: 600; font-weight: 600;
letter-spacing: 0%;
} }
} }
.success-image { .success-image {

View file

@ -561,7 +561,7 @@ export default {
margin-top: 1.5rem; margin-top: 1.5rem;
.time-slot-header { .time-slot-header {
font-family: AvertaBold; font-family: UrbanistBold;
margin-top: 1.5rem; margin-top: 1.5rem;
} }
.text-block.duration-text-block { .text-block.duration-text-block {

View file

@ -96,7 +96,6 @@ import { saveSession } from "@/helpers/heritage-integration/order-helper.js";
import baseMixin from "@/mixins/base-mixin.js"; import baseMixin from "@/mixins/base-mixin.js";
import { queryStrings } from "@/constants/query-strings"; import { queryStrings } from "@/constants/query-strings";
import { routeData } from "@/router/constants/routes"; import { routeData } from "@/router/constants/routes";
import { consumeQueryFromStash } from "@/router/methods/helpers/querystring-stash";
// Define Validation Rules // Define Validation Rules
defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED)); defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
@ -116,7 +115,7 @@ export default {
mixins: [vinPagesMixin], mixins: [vinPagesMixin],
data() { data() {
return { return {
serviceZipCode: this.getZipFromStore() ?? consumeQueryFromStash(queryStrings.ZIP_CODE), serviceZipCode: this.getZipFromStore(),
emailOrSms: this.getEmailOrSmsFromStore(), emailOrSms: this.getEmailOrSmsFromStore(),
carId: this.getCarIdfromStore(), carId: this.getCarIdfromStore(),
isHeavyTruck: this.getIsVehicleHeavyTruckServiceableFromStore(), isHeavyTruck: this.getIsVehicleHeavyTruckServiceableFromStore(),

View file

@ -110,10 +110,6 @@ import { getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper";
import { applicationConfig } from "../../constants/application-config"; import { applicationConfig } from "../../constants/application-config";
import { queryStrings } from "@/constants/query-strings"; import { queryStrings } from "@/constants/query-strings";
import { getQuerystringParameter } from "@/helpers/querystring-helper"; import { getQuerystringParameter } from "@/helpers/querystring-helper";
import {
consumeQueryFromStash,
peekQueryFromStash,
} from "@/router/methods/helpers/querystring-stash";
import { debugLog } from "@/helpers/debug-log-helper"; import { debugLog } from "@/helpers/debug-log-helper";
//define validation rules //define validation rules
@ -138,7 +134,7 @@ export default {
imageUrl: this.getImagefromStore(), imageUrl: this.getImagefromStore(),
imageVifNumber: this.getImageVifNumberfromStore(), imageVifNumber: this.getImageVifNumberfromStore(),
imageVifColor: this.getImageVifColorfromStore(), imageVifColor: this.getImageVifColorfromStore(),
serviceZipCode: this.getZipFromStore() ?? peekQueryFromStash(queryStrings.ZIP_CODE), serviceZipCode: this.getZipFromStore(),
yearOptions: [], yearOptions: [],
makeOptions: [], makeOptions: [],
modelOptions: [], modelOptions: [],

View file

@ -159,8 +159,6 @@ import baseMixin from "@/mixins/base-mixin.js";
import store from "@/store"; import store from "@/store";
import vinPagesMixin from "@/mixins/vin-pages-mixin"; import vinPagesMixin from "@/mixins/vin-pages-mixin";
import { routeData } from "@/router/constants/routes"; import { routeData } from "@/router/constants/routes";
import { consumeQueryFromStash } from "@/router/methods/helpers/querystring-stash";
import { queryStrings } from "@/constants/query-strings";
// DEFINE VALIDATION RULES // DEFINE VALIDATION RULES
defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED)); defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
@ -204,7 +202,7 @@ export default {
data() { data() {
return { return {
vin: this.getVinFromStore(), vin: this.getVinFromStore(),
serviceZipCode: this.getZipFromStore() ?? consumeQueryFromStash(queryStrings.ZIP_CODE), serviceZipCode: this.getZipFromStore(),
emailOrSms: this.getEmailOrSmsFromStore(), emailOrSms: this.getEmailOrSmsFromStore(),
carId: this.getCarIdfromStore(), carId: this.getCarIdfromStore(),
isHeavyTruck: this.getIsVehicleHeavyTruckServiceableFromStore(), isHeavyTruck: this.getIsVehicleHeavyTruckServiceableFromStore(),

View file

@ -4,6 +4,10 @@ import { getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper";
import { routeData } from "@/router/constants/routes"; import { routeData } from "@/router/constants/routes";
import { isVerifiedInsurance } from "@/router/methods/helpers/requires-verified-redirect"; import { isVerifiedInsurance } from "@/router/methods/helpers/requires-verified-redirect";
import store from "@/store"; 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) { export async function vehicleBeforeEnter(to, from) {
// If cookie indicates that vehicle has changed during insurance flow, reset parts info. // 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); 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()) { if (isVerifiedInsurance()) {
return { return {
name: routeData.VEHICLE_DAMAGE.name, name: routeData.VEHICLE_DAMAGE.name,

View file

@ -97,3 +97,11 @@ body {
} }
} }
} }
button,
input,
select,
textarea,
option,
.btn {
letter-spacing: 0.03rem;
}

View file

@ -4,6 +4,7 @@ body {
font-size: 1rem; font-size: 1rem;
line-height: 1.625; line-height: 1.625;
font-weight: 400; font-weight: 400;
letter-spacing: 0.03rem;
} }
//Headings //Headings
@ -43,7 +44,7 @@ h5,
h6, h6,
.h6 { .h6 {
line-height: 1.7; line-height: 1.7;
letter-spacing: 0.75px; letter-spacing: 0.03rem;
text-transform: uppercase; text-transform: uppercase;
} }
@ -78,58 +79,58 @@ caption,
font-weight: 500; font-weight: 500;
} }
// Averta Fonts // Urbanist Fonts
@font-face { @font-face {
font-family: "AvertaBold"; font-family: "UrbanistRegular";
src: src:
url("@/assets/fonts/averta-bold-webfont.woff2") format("woff2"), url("@/assets/fonts/Urbanist-Regular.woff") format("woff2"),
url("@/assets/fonts/averta-bold-webfont.woff") format("woff"); url("@/assets/fonts/Urbanist-Regular.woff2") format("woff");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
} }
@font-face { @font-face {
font-family: "AvertaBold_Italic"; font-family: "UrbanistSemibold";
src: src:
url("@/assets/fonts/averta-bolditalic-webfont.woff2") format("woff2"), url("@/assets/fonts/Urbanist-SemiBold.woff") format("woff2"),
url("@/assets/fonts/averta-bolditalic-webfont.woff") format("woff"); url("@/assets/fonts/Urbanist-SemiBold.woff2") format("woff");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
} }
@font-face { @font-face {
font-family: "AvertaExtraBold"; font-family: "UrbanistBold";
src: src:
url("@/assets/fonts/averta-extrabold-webfont.woff2") format("woff2"), url("@/assets/fonts/Urbanist-Bold.woff") format("woff2"),
url("@/assets/fonts/averta-extrabold-webfont.woff") format("woff"); url("@/assets/fonts/Urbanist-Bold.woff2") format("woff");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
} }
@font-face { @font-face {
font-family: "AvertaRegular"; font-family: "UrbanistExtraBold";
src: src:
url("@/assets/fonts/averta-regular-webfont.woff2") format("woff2"), url("@/assets/fonts/Urbanist-ExtraBold.woff") format("woff2"),
url("@/assets/fonts/averta-regular-webfont.woff") format("woff"); url("@/assets/fonts/Urbanist-ExtraBold.woff2") format("woff");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
} }
@font-face { @font-face {
font-family: "AvertaRegular_Italic"; font-family: "UrbanistMedium_Italic";
src: src:
url("@/assets/fonts/averta-regularitalic-webfont.woff2") format("woff2"), url("@/assets/fonts/Urbanist-MediumItalic.woff") format("woff2"),
url("@/assets/fonts/averta-regularitalic-webfont.woff") format("woff"); url("@/assets/fonts/Urbanist-MediumItalic.woff2") format("woff");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
} }
@font-face { @font-face {
font-family: "AvertaSemibold"; font-family: "UrbanistBold_Italic";
src: src:
url("@/assets/fonts/averta-semibold-webfont.woff2") format("woff2"), url("@/assets/fonts/Urbanist-BoldItalic.woff") format("woff2"),
url("@/assets/fonts/averta-semibold-webfont.woff") format("woff"); url("@/assets/fonts/Urbanist-BoldItalic.woff2") format("woff");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
} }

View file

@ -120,9 +120,9 @@ $theme-colors: (
$body-color: $gray-600; $body-color: $gray-600;
//Fonts //Fonts
$font-family-sans-serif: AvertaRegular, Arial, Helvetica, sans-serif; $font-family-sans-serif: UrbanistRegular, Arial, Helvetica, sans-serif;
$font-family-monospace: $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 // stylelint-enable value-keyword-case
$font-family-base: $font-family-sans-serif; $font-family-base: $font-family-sans-serif;
$font-family-code: $font-family-monospace; $font-family-code: $font-family-monospace;

View file

@ -103,6 +103,7 @@ export default {
justify-content: flex-start; justify-content: flex-start;
p { p {
font-family: UrbanistRegular;
color: $gray-600; color: $gray-600;
text-align: center; text-align: center;
@ -123,15 +124,16 @@ export default {
&:checked + .list-card-content { &:checked + .list-card-content {
p { p {
color: $black; color: $black;
font-weight: 500; font-family: UrbanistSemibold;
} }
.p-bold { .p-bold {
font-family: UrbanistSemibold;
font-weight: 600; font-weight: 600;
} }
.sub-copy { .sub-copy {
font-family: UrbanistSemibold;
color: $gray-600; color: $gray-600;
font-weight: 400;
} }
} }