Merge pull request #2426 from Safelite/CASH-499-calendar-ui-updates

Cash 499 calendar UI updates
This commit is contained in:
bmauger 2025-04-16 09:30:09 -04:00 committed by GitHub
commit f06da57711
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 184 additions and 1259 deletions

View file

@ -1,5 +1,7 @@
<template>
<div class="date-picker text-center" :class="calendarViewDirection">
<div
class="date-picker text-center"
:class="`${calendarViewDirection} ${isPricingByDayClass} ${showPricingByDayClass}`">
<fieldset id="date-picker-fieldset" ref="datePickerFieldset">
<legend class="sr-only">Select a day and time</legend>
<div
@ -60,18 +62,24 @@
date.dateNum === 1 ? 'first-day-' + month.startDateDayIndex : '',
date.dayClasses,
date.isSelectable ? 'selectable-day' : '',
date.isPricingByDayUpchargeDay ? 'upch-day' : '',
]">
<input
:disabled="!date.isSelectable"
type="radio"
name="day-of-month"
v-model="selectedDate"
@click="fireDateSelectedEvent"
@keypress.enter="fireDateSelectedEvent"
@click="fireDateSelectedEvent($event, date)"
@keypress.enter="fireDateSelectedEvent($event, date)"
:value="date.inputValue"
:id="`${month.monthLabel}-${date.dateNum.toString()}`" />
<label :for="`${month.monthLabel}-${date.dateNum.toString()}`">
<span>{{ date.dateNum.toString() }}</span>
<span
v-if="isPricingByDayExperiment && showPricingByDay && date.isSelectable"
class="price">
{{ date.priceString }}
</span>
</label>
</div>
</div>
@ -99,8 +107,17 @@
// Supporting files
import loader from "@/ux-components/loader/loader";
import store from "@/store";
import { TIMINGFUNC_MAP, BUFFER_OFFSET, MONTHS_OF_YEAR } from "./mixins/constants";
import { selectableDaysOptions, requiredParameter, forceTwoDigitString } from "./mixins/helpers";
import {
TIMINGFUNC_MAP,
BUFFER_OFFSET,
MONTHS_OF_YEAR,
DAYS_OF_WEEK,
} from "@/digital-components/date-picker/mixins/constants";
import {
selectableDaysOptions,
requiredParameter,
forceTwoDigitString,
} from "@/digital-components/date-picker/mixins/helpers";
import {
convertDateToDateString,
convertDateStringToDate,
@ -148,6 +165,10 @@ export default {
type: String,
default: "",
},
showPricingByDay: Boolean,
pricingByDayBasePrice: Number,
pricingByDayUpcharge: Number,
isPricingByDayExperiment: Boolean,
},
setup(props) {
const uuid = uuidv4();
@ -199,6 +220,12 @@ export default {
if (this.selectableDatesSetting === "custom") return "future";
return "past";
},
isPricingByDayClass() {
return this.isPricingByDayExperiment ? "pricing-by-day" : "";
},
showPricingByDayClass() {
return this.showPricingByDay ? "show-pricing-by-day" : "";
},
selectedDate: {
get() {
return this.modelValue;
@ -212,12 +239,12 @@ export default {
initializeComponent(initialData) {
this.setCalendarData(initialData);
},
fireDateSelectedEvent(event) {
fireDateSelectedEvent(event, date) {
// Ignore if arrow key selected radioButton
if (event.screenX === 0 && event.screenY === 0) {
return;
}
this.$emit("date-clicked");
this.$emit("date-clicked", date);
},
getWeekStartDate(dateString) {
const date = convertDateStringToDate(dateString);
@ -440,6 +467,8 @@ export default {
initialViewEndDate: config.initialViewEndDate,
hideSecondMonth: hideSecondMonth,
preSelectedDate: config.preSelectedDate,
pricingByDayBasePrice: config.pricingByDayBasePrice,
pricingByDayUpcharge: config.pricingByDayUpcharge,
};
if (direction === "future") {
// first 0, then 1
@ -573,6 +602,17 @@ export default {
("0" + monthNum).slice(-2) +
"-" +
("0" + i).slice(-2);
const dayIndex = convertDateStringToDate(dateString).getDay();
const dayObject = DAYS_OF_WEEK[dayIndex];
const isSelectable =
this.selectableDatesData.findIndex((date) => date.date === dateString) > -1
? true
: false;
const isPricingByDayUpchargeDay = dayObject.isPricingByDayUpchargeDay;
const displayPrice = isPricingByDayUpchargeDay
? options.pricingByDayBasePrice + options.pricingByDayUpcharge
: options.pricingByDayBasePrice;
const priceString = "$" + displayPrice;
if (offset === 0 && i === this.todayDateNum) {
dayClasses += " current-day";
@ -583,8 +623,8 @@ export default {
if (offset === 0 && i > this.todayDateNum && calendarViewDirection === "past") {
dayClasses += " unavailable-day";
}
if (convertDateStringToDate(dateString).getDay() === 0) {
dayClasses += " sunday";
if (dayIndex === 0) {
dayClasses += " " + dayObject.cssClass;
}
if (
this.hideSomeDaysForInitialView &&
@ -598,10 +638,9 @@ export default {
dateNum: i,
dayClasses: dayClasses,
inputValue: dateString,
isSelectable:
this.selectableDatesData.findIndex((date) => date.date === dateString) > -1
? true
: false,
priceString: priceString,
isSelectable: isSelectable,
isPricingByDayUpchargeDay: isPricingByDayUpchargeDay,
};
dates.push(dateObject);
}
@ -897,7 +936,6 @@ export default {
}
}
&:hover,
&:checked {
@include media-breakpoint-up(sm) {
box-shadow: 0 0 0 4px transparent;
@ -924,9 +962,7 @@ export default {
&.selectable-day {
label {
color: $blue;
background-color: $blue-100;
border: 1px solid $blue;
min-width: 2.5rem;
width: 2.5rem;
border-radius: 50%;
@ -934,12 +970,11 @@ export default {
}
&.unavailable-day {
label {
color: $gray-500;
background-color: $gray-100;
border: none;
pointer-events: none;
}
}
//NEEDED?
&.unavailable-day:not(&.sunday) {
label {
&::before {
@ -949,7 +984,6 @@ export default {
left: -1rem;
width: 1rem;
height: 2.5rem;
background: $gray-100;
}
}
}
@ -1056,7 +1090,107 @@ export default {
}
}
}
// pricing by day override styles
&.pricing-by-day {
.calendar-grid-container .grid-item {
margin: 0 0 0.15rem 0;
}
.month-year {
grid-area: 1 / 1 / 2 / 8;
}
.legend {
display: none;
}
.radio-wrapper {
align-items: flex-start;
width: 100%;
height: 2.5rem;
color: $black;
input[type="radio"] {
&:focus-visible + label {
box-shadow: none;
}
&:focus + label,
&:checked:focus + label {
box-shadow: none;
background-color: $white;
color: $black;
}
&:checked + label {
background: $blue-100;
border: 2px solid $blue;
border-radius: 0.25rem;
color: $black;
span {
font-family: AvertaSemibold;
font-weight: 400;
}
}
}
label {
flex-direction: column;
height: 2.5rem;
width: 100%;
font-size: 0.75rem;
line-height: 1.2;
justify-content: center;
padding: 0.25rem;
}
&.selectable-day {
label {
color: $black;
background-color: $blue-100;
min-width: 2.5rem;
width: 2.5rem;
border-radius: 0.25rem;
}
}
&.current-day {
label:after {
margin-top: 0.2rem;
position: relative;
top: 0;
}
}
}
&.show-pricing-by-day {
.radio-wrapper {
&.selectable-day label span {
text-decoration: none;
color: $black;
font-weight: 400;
font-family: "AvertaSemibold";
&.price {
font-family: "AvertaRegular";
font-weight: 400;
}
}
&.upch-day label span.price {
color: $gray-600;
font-weight: 400;
font-family: $font-family-sans-serif;
}
input[type="radio"] {
&:focus + label,
&:checked:focus + label {
color: $gray-600;
}
&:checked + label {
color: $blue;
span {
font-weight: 400;
font-family: "AvertaSemibold";
}
}
}
}
}
}
}
.btn-link {
display: block;
position: relative;

File diff suppressed because it is too large Load diff

View file

@ -11,6 +11,7 @@
</div>
</div>
<div class="footer-inner-wrapper">
<p class="mx-3 my-0 pb-1">&copy; 2025 Safelite Group</p>
<textLink
linkType="newWindowLink"
text="Terms of service"
@ -32,11 +33,6 @@
linkType="navigation"
text="Cookie preferences"
href="javascript:OneTrust.ToggleInfoDisplay();" />
<textLink
linkType="newWindowLink"
text="Warranty"
href="//www.safelite.com/national-lifetime-warranty"
target="_blank" />
<textLink
linkType="newWindowLink"
text="Notice at collection"
@ -83,6 +79,14 @@ export default {
justify-content: center;
align-items: center;
p {
font-size: 0.875rem;
color: $black;
@include media-breakpoint-up(md) {
font-size: 1rem;
}
}
:deep(a) {
text-decoration: none;
&.new-window-link {

View file

@ -56,7 +56,10 @@
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" />
<textBlock cmsWidgetName="DisclaimerCopyWidget" typeStyle="caption" />
<textBlock
class="mb-5"
cmsWidgetName="DisclaimerCopyWidget"
typeStyle="caption" />
</div>
</div>
</div>

View file

@ -203,3 +203,9 @@ export default {
},
};
</script>
<style lang="scss">
.ui-autocomplete {
z-index: 3;
}
</style>

View file

@ -275,8 +275,8 @@ export default {
const emailFromStore = store.getters.order.customer.emailAddress;
const isEmailInStoreOnPageLoad = emailFromStore?.length > 0;
const isPopupSkipped =
store.getters.pageData(fmgPageValues.QUOTE).saveProgressPopupSkipped === true;
const showSaveProgressPopup = isEmailInStoreOnPageLoad || isPopupSkipped ? false : true;
store.getters.pageData(fmgPageValues.QUOTE)?.saveProgressPopupSkipped === true;
const showSaveProgressPopup = !(isEmailInStoreOnPageLoad || isPopupSkipped);
const showSaveProgressModal = isEmailInStoreOnPageLoad ? false : true;
const shouldSkipToInsurance =
getBoolFromString(

View file

@ -18,7 +18,7 @@
marginTopSizeOverride="1" />
</template>
<locationAlerts cmsWidgetPrefix="LocationAlert-" ref="locationAlerts" />
<datePickerForPricingByDay
<datePicker
customComponentId="dateQuestion"
selectableDatesSetting="custom"
ref="datePicker"
@ -77,7 +77,7 @@ import navbar from "@/fmg-components/nav-bar/nav-bar";
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
import { Form, defineRule } from "vee-validate";
import datePickerForPricingByDay from "@/experiment-components/date-picker-for-pricing-by-day";
import datePicker from "@/digital-components/date-picker/date-picker";
import locationAlerts from "@/layouts/schedule/location-alerts/location-alerts";
import timeSlotModalQuestion from "./time-slot-modal-question/time-slot-modal-question";
import textBlock from "@/digital-components/text-block/text-block";
@ -298,14 +298,13 @@ export default {
);
// While Pricing By Day Experiment is active, using the updated datePicker
const datePickerInitialDataPromise =
await datePickerForPricingByDay.methods.loadInitialData({
// setup config options for date-picker
selectableDatesSetting: "custom",
initialViewRowsToShow: 5,
customSelectableDatesCallback: getAvailableDates,
preSelectedDate: preSelectedDate,
});
const datePickerInitialDataPromise = await datePicker.methods.loadInitialData({
// setup config options for date-picker
selectableDatesSetting: "custom",
initialViewRowsToShow: 5,
customSelectableDatesCallback: getAvailableDates,
preSelectedDate: preSelectedDate,
});
// Get pricingByDayUpcharge needed for Pricing By Day
const pricingByDayUpchargePartPromise = showPricingByDay
@ -735,7 +734,7 @@ export default {
funnelSubHeader,
Form,
loadingModal,
datePickerForPricingByDay,
datePicker,
locationAlerts,
timeSlotModalQuestion,
textBlock,