Merge pull request #2633 from Safelite/feature/CASH-845-stage-4
Feature/cash 845 stage 4
This commit is contained in:
commit
f1f95bb353
4 changed files with 112 additions and 69 deletions
|
|
@ -16,10 +16,19 @@ const RouteCodeFlags = {
|
|||
OVERNIGHT_DROP_OFF: "OVERNIGHT DROP OFF",
|
||||
};
|
||||
|
||||
const cmsWidgetFieldMappings = {
|
||||
MODAL_CLOSE_BUTTON: "FooterText",
|
||||
SUPPLEMENTAL_INFORMATION: "BodyText",
|
||||
TIME_SLOT_BUTTON: "HeaderText",
|
||||
DISCLAIMER: "FooterText",
|
||||
DURATION: "SubheaderText",
|
||||
};
|
||||
|
||||
export {
|
||||
AppointmentTypeStrings,
|
||||
PREMIUM_TIME_SLOT_ID_FLAG,
|
||||
PREMIUM_FEE_PART_TYPE,
|
||||
PRICING_BY_DAY_PART_TYPE,
|
||||
RouteCodeFlags,
|
||||
cmsWidgetFieldMappings,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,17 @@
|
|||
<div
|
||||
class="date-picker text-center"
|
||||
:class="`${calendarViewDirection} calendar-2025 ${showPricingByDayClass}`">
|
||||
<div class="date-picker-header">
|
||||
<funnelSubHeader cmsWidgetName="DatePickerSubHeaderWidget" class="mt-5" />
|
||||
<textBlock
|
||||
v-show="durationTextBlockCopy"
|
||||
:customText="durationTextBlockCopy"
|
||||
justifyText="center"
|
||||
typeStyle="small"
|
||||
marginTopSizeOverride="0"
|
||||
class="duration-text-block" />
|
||||
</div>
|
||||
|
||||
<fieldset id="date-picker-fieldset" ref="datePickerFieldset">
|
||||
<legend class="sr-only">Select a day and time</legend>
|
||||
<div
|
||||
|
|
@ -142,7 +153,7 @@
|
|||
overnightDropOffCmsWidgetName="OvernightDropOffTimeSlotModal"
|
||||
dropOffOrPickATimeQuestionCmsWidgetName="DropOffOrPickATimeQuestionWidget"
|
||||
:selectedDate="selectedDate"
|
||||
:appointmentType="appointmentTypeForTimeSlotQuestion"
|
||||
:appointmentType="appointmentType"
|
||||
:premiumAppointmentFee="premiumAppointmentFee"
|
||||
:displayWaitList="displayWaitList"
|
||||
:timeSlotsForSelectedDate="timeSlotsForSelectedDate"
|
||||
|
|
@ -156,6 +167,8 @@
|
|||
|
||||
<script>
|
||||
import timeSlotQuestion from "@/layouts/schedule/time-slot-question/time-slot-question.vue";
|
||||
import textBlock from "@/digital-components/text-block/text-block";
|
||||
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
|
||||
|
||||
// Supporting files
|
||||
import loader from "@/ux-components/loader/loader";
|
||||
|
|
@ -166,7 +179,12 @@ import {
|
|||
MONTHS_OF_YEAR,
|
||||
DAYS_OF_WEEK,
|
||||
} from "@/digital-components/date-picker/mixins/constants";
|
||||
import { AppointmentTypeStrings, PREMIUM_FEE_PART_TYPE } from "@/constants/schedule-constants";
|
||||
import {
|
||||
AppointmentTypeStrings,
|
||||
RouteCodeFlags,
|
||||
PREMIUM_FEE_PART_TYPE,
|
||||
cmsWidgetFieldMappings,
|
||||
} from "@/constants/schedule-constants";
|
||||
import {
|
||||
selectableDaysOptions,
|
||||
requiredParameter,
|
||||
|
|
@ -178,6 +196,7 @@ import {
|
|||
import { useField, ErrorMessage } from "vee-validate";
|
||||
import { deepClone } from "@/helpers/object-helper";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { getDisplayTextForDurationLength } from "@/helpers/duration-length-helper";
|
||||
|
||||
export default {
|
||||
name: "datePicker",
|
||||
|
|
@ -190,6 +209,7 @@ export default {
|
|||
selectedTimeSlotInfo: this.getSelectedTimeSlotInfo(),
|
||||
selectableDatesInshop: [], // NOTE: this and the mobile version below use monthNum (1-based), NOT monthIndex (0-based)
|
||||
selectableDatesMobile: [],
|
||||
durationTextBlockCopyForInshopOrDropoff: null,
|
||||
};
|
||||
},
|
||||
props: {
|
||||
|
|
@ -224,7 +244,7 @@ export default {
|
|||
pricingByDayUpcharge: Number,
|
||||
isPricingByDayExperiment: Boolean,
|
||||
timeSlotsForSelectedDate: Object,
|
||||
appointmentTypeForTimeSlotQuestion: String,
|
||||
appointmentType: String,
|
||||
premiumAppointmentFee: Object,
|
||||
estimatedServiceMinutesMinimum: Number,
|
||||
estimatedServiceMinutesMaximum: Number,
|
||||
|
|
@ -289,6 +309,54 @@ export default {
|
|||
this.$emit("update:modelValue", newSelectedDate);
|
||||
},
|
||||
},
|
||||
dropOffDurationText() {
|
||||
return this.getCmsContent("DropOffTimeSlotModal", cmsWidgetFieldMappings.DURATION);
|
||||
},
|
||||
sameDayDropoffDurationText() {
|
||||
return this.getCmsContent(
|
||||
"SameDayDropOffTimeSlotModal",
|
||||
cmsWidgetFieldMappings.DURATION
|
||||
);
|
||||
},
|
||||
overnightDropoffDurationText() {
|
||||
return this.getCmsContent(
|
||||
"OvernightDropOffTimeSlotModal",
|
||||
cmsWidgetFieldMappings.DURATION
|
||||
);
|
||||
},
|
||||
inshopDurationText() {
|
||||
const inshopDurationTextWithoutTime = this.getCmsContent(
|
||||
"TimeSlotModalQuestion",
|
||||
cmsWidgetFieldMappings.DURATION
|
||||
);
|
||||
|
||||
const inshopDurationTime = getDisplayTextForDurationLength(
|
||||
this.estimatedServiceMinutesMinimum,
|
||||
this.estimatedServiceMinutesMaximum
|
||||
);
|
||||
|
||||
return `${inshopDurationTextWithoutTime} ${inshopDurationTime}`;
|
||||
},
|
||||
durationTextBlockCopy() {
|
||||
if (this.appointmentType === AppointmentTypeStrings.MOBILE) {
|
||||
return null;
|
||||
} else if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) {
|
||||
return this.inshopDurationText;
|
||||
} else if (
|
||||
this.appointmentType === AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF ||
|
||||
this.appointmentType === AppointmentTypeStrings.DROP_OFF
|
||||
) {
|
||||
return this.durationTextBlockCopyForInshopOrDropoff ?? this.inshopDurationText;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
isSameDay() {
|
||||
if (!this.timeSlotsForSelectedDate) {
|
||||
return false;
|
||||
}
|
||||
const todaysDate = new Date().toISOString().split("T")[0];
|
||||
return this.selectedDate === todaysDate;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
initializeComponent(initialData) {
|
||||
|
|
@ -877,6 +945,18 @@ export default {
|
|||
handleWaitListRequested(value) {
|
||||
this.$emit("waitListRequested", value);
|
||||
},
|
||||
getDurationTextBlockCopyForInshopOrDropoff(selectedRouteCode) {
|
||||
if (selectedRouteCode?.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) {
|
||||
return this.overnightDropoffDurationText;
|
||||
} else if (selectedRouteCode?.includes(RouteCodeFlags.ALL_DAY_DROP_OFF)) {
|
||||
if (this.isSameDay) {
|
||||
return this.sameDayDropoffDurationText;
|
||||
} else {
|
||||
return this.dropOffDurationText;
|
||||
}
|
||||
}
|
||||
return this.inshopDurationText;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
modelValue(newValue) {
|
||||
|
|
@ -891,13 +971,18 @@ export default {
|
|||
}
|
||||
},
|
||||
selectedTimeSlotInfo(newValue) {
|
||||
this.$emit("TimeSlotSelected", newValue); // NEEDED ON SCHEDULE - to update footer button text and to save to Store correctly
|
||||
const routeCode = newValue?.timeSlot?.routeCode;
|
||||
this.durationTextBlockCopyForInshopOrDropoff =
|
||||
this.getDurationTextBlockCopyForInshopOrDropoff(routeCode);
|
||||
this.$emit("TimeSlotSelected", newValue); // needed to update footer button text on Schedule page and to save to Store correctly
|
||||
},
|
||||
},
|
||||
components: {
|
||||
loader,
|
||||
ErrorMessage,
|
||||
timeSlotQuestion,
|
||||
textBlock,
|
||||
funnelSubHeader,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -913,6 +998,10 @@ export default {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.date-picker-header {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
flex-grow: 1;
|
||||
position: relative;
|
||||
|
|
|
|||
|
|
@ -129,7 +129,6 @@
|
|||
<div class="row justify-content-center date-picker-wrapper">
|
||||
<div class="col-md-6 col-xl-4">
|
||||
<locationAlerts cmsWidgetPrefix="LocationAlert-" ref="locationAlerts" />
|
||||
|
||||
<datePicker
|
||||
v-show="appointmentType"
|
||||
customComponentId="dateQuestion"
|
||||
|
|
@ -146,7 +145,7 @@
|
|||
:showPricingByDay="showPricingByDay"
|
||||
:isPricingByDayExperiment="isPricingByDayExperiment"
|
||||
:timeSlotsForSelectedDate="timeSlotsForSelectedDate"
|
||||
:appointmentTypeForTimeSlotQuestion="appointmentType"
|
||||
:appointmentType="appointmentType"
|
||||
:premiumAppointmentFee="mobilePremiumAppointmentFee"
|
||||
:estimatedServiceMinutesMinimum="getServiceMinutesMin"
|
||||
:estimatedServiceMinutesMaximum="getServiceMinutesMax"
|
||||
|
|
@ -863,7 +862,6 @@ export default {
|
|||
appointmentTypeStrings() {
|
||||
return AppointmentTypeStrings;
|
||||
},
|
||||
|
||||
ChangeShopLinkText() {
|
||||
return this.getCmsContent("ChangeShopLink", "Text");
|
||||
},
|
||||
|
|
@ -1703,7 +1701,7 @@ export default {
|
|||
.change-location a {
|
||||
font-family: UrbanistSemibold;
|
||||
}
|
||||
.date-picker-wrapper {
|
||||
.date-picker {
|
||||
border-top: 1px solid $gray-500;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,5 @@
|
|||
<template>
|
||||
<div class="time-slots-question">
|
||||
<textBlock
|
||||
v-show="durationTextBlockCopy"
|
||||
:customText="durationTextBlockCopy"
|
||||
justifyText="center"
|
||||
typeStyle="small"
|
||||
class="duration-text-block" />
|
||||
<buttonQuestion
|
||||
v-if="isDropOffAppointmentAvailable"
|
||||
v-model="selectedAnswerForDropOffOrInshop"
|
||||
|
|
@ -38,6 +32,7 @@
|
|||
groupName="chooseTimeSlot"
|
||||
textPosition="text-center"
|
||||
v-model="selectedAnswerForTimeSlots"
|
||||
questionText="Available times:"
|
||||
isRequired
|
||||
validationRules="time-slot-required" />
|
||||
<div
|
||||
|
|
@ -103,16 +98,9 @@ import {
|
|||
RouteCodeFlags,
|
||||
PREMIUM_TIME_SLOT_ID_FLAG,
|
||||
PREMIUM_FEE_PART_TYPE,
|
||||
cmsWidgetFieldMappings,
|
||||
} from "@/constants/schedule-constants";
|
||||
|
||||
const cmsWidgetFieldMappings = {
|
||||
MODAL_CLOSE_BUTTON: "FooterText",
|
||||
SUPPLEMENTAL_INFORMATION: "BodyText",
|
||||
TIME_SLOT_BUTTON: "HeaderText",
|
||||
DISCLAIMER: "FooterText",
|
||||
DURATION: "SubheaderText",
|
||||
};
|
||||
|
||||
const PICK_A_TIME_BUTTON_VALUE = "pick-a-time-selected";
|
||||
|
||||
defineRule("time-slot-required", required(errorMessages.OPTION_REQUIRED));
|
||||
|
|
@ -194,7 +182,11 @@ export default {
|
|||
supplementalInformationBlock() {
|
||||
let appointmentTypeCmsWidgetName;
|
||||
|
||||
if (this.appointmentType === AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF) {
|
||||
if (
|
||||
this.appointmentType === AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF ||
|
||||
this.appointmentType === AppointmentTypeStrings.IN_SHOP ||
|
||||
this.appointmentType === AppointmentTypeStrings.DROP_OFF
|
||||
) {
|
||||
return null;
|
||||
// This is planned to be used again for drop-off
|
||||
// Wrote this to be ready for that eventuality, is untested and no HTML work done yet
|
||||
|
|
@ -286,51 +278,6 @@ export default {
|
|||
// return null;
|
||||
// }
|
||||
// },
|
||||
dropOffDurationText() {
|
||||
return this.getCmsContent(this.dropoffCmsWidgetName, cmsWidgetFieldMappings.DURATION);
|
||||
},
|
||||
sameDayDropoffDurationText() {
|
||||
return this.getCmsContent(
|
||||
this.sameDayDropOffCmsWidgetName,
|
||||
cmsWidgetFieldMappings.DURATION
|
||||
);
|
||||
},
|
||||
overnightDropoffDurationText() {
|
||||
return this.getCmsContent(
|
||||
this.overnightDropOffCmsWidgetName,
|
||||
cmsWidgetFieldMappings.DURATION
|
||||
);
|
||||
},
|
||||
inshopDurationText() {
|
||||
const inshopDurationTextWithoutTime = this.getCmsContent(
|
||||
this.cmsWidgetName,
|
||||
cmsWidgetFieldMappings.DURATION
|
||||
);
|
||||
|
||||
const inshopDurationTime = getDisplayTextForDurationLength(
|
||||
this.estimatedServiceMinutesMinimum,
|
||||
this.estimatedServiceMinutesMaximum
|
||||
);
|
||||
|
||||
return `${inshopDurationTextWithoutTime} ${inshopDurationTime}`;
|
||||
},
|
||||
durationTextBlockCopy() {
|
||||
if (this.appointmentType === AppointmentTypeStrings.MOBILE) {
|
||||
return null;
|
||||
} else if (this.appointmentType === AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF) {
|
||||
if (this.selectedRouteCode?.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)) {
|
||||
return this.overnightDropoffDurationText;
|
||||
} else if (this.selectedRouteCode?.includes(RouteCodeFlags.ALL_DAY_DROP_OFF)) {
|
||||
if (this.isSameDay) {
|
||||
return this.sameDayDropoffDurationText;
|
||||
} else {
|
||||
return this.dropOffDurationText;
|
||||
}
|
||||
}
|
||||
return this.inshopDurationText;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
isSameDay() {
|
||||
if (!this.timeSlotsForSelectedDate) {
|
||||
return false;
|
||||
|
|
@ -609,6 +556,7 @@ export default {
|
|||
selectedAnswerForDropOffOrInshop(newValue) {
|
||||
if (newValue == PICK_A_TIME_BUTTON_VALUE) {
|
||||
this.selectedRouteCode = null;
|
||||
this.setSelectedTimeSlot();
|
||||
} else {
|
||||
this.selectedAnswerForTimeSlots = null;
|
||||
this.selectedRouteCode = newValue;
|
||||
|
|
@ -631,7 +579,6 @@ export default {
|
|||
|
||||
<style lang="scss">
|
||||
.time-slots-question {
|
||||
border-top: 1px solid $gray-500;
|
||||
margin-top: 1.5rem;
|
||||
|
||||
.time-slot-header {
|
||||
|
|
|
|||
Loading…
Reference in a new issue