Merge pull request #1256 from Safelite/rlsmerge/2023.07.27-to-submit-cash

Rlsmerge/2023.07.27 to submit cash
This commit is contained in:
CarlNation 2023-07-20 08:52:30 -04:00 committed by GitHub
commit 50a7e39c6d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 149 additions and 80 deletions

View file

@ -56,15 +56,19 @@
:class="[!isLoading ? 'date-picker-hidden' : '']" :class="[!isLoading ? 'date-picker-hidden' : '']"
loaderColor="blue" loaderColor="blue"
loaderPosition="center" /> loaderPosition="center" />
<div class="row form-test-error">
<ErrorMessage name="date-of-month" class="small mt-1"></ErrorMessage>
</div>
<button
v-if="calendarViewDirection === 'future' && !disableViewMoreDatesButton"
type="button"
class="btn btn-link"
id="viewMoreDates"
:disabled="isLoading"
@click="showAnotherMonth">
View more dates
</button>
</fieldset> </fieldset>
<button
v-if="calendarViewDirection === 'future' && !disableViewMoreDatesButton"
type="button"
class="btn btn-link"
:disabled="isLoading"
@click="showAnotherMonth">
View more dates
</button>
</div> </div>
</template> </template>
@ -78,6 +82,7 @@ import {
convertDateToDateString, convertDateToDateString,
convertDateStringToDate, convertDateStringToDate,
} from "@/layouts/schedule/helpers/schedule-helper"; } from "@/layouts/schedule/helpers/schedule-helper";
import { useField, ErrorMessage } from "vee-validate";
export default { export default {
name: "datePicker", name: "datePicker",
@ -112,6 +117,32 @@ export default {
return []; return [];
}, },
}, },
validationRules: {
type: String,
default: "",
},
},
setup(props) {
const propsClone = Object.assign({}, props);
const modelValue = propsClone.modelValue;
const fieldOptions = {
value: modelValue,
initialValue: modelValue,
};
const { errorMessage, handleBlur, handleChange, meta, validate, errors, resetField } =
useField("date-of-month", props.validationRules, fieldOptions);
return {
errorMessage,
handleBlur,
handleChange,
validate,
meta,
errors,
resetField,
};
}, },
computed: { computed: {
todayString() { todayString() {
@ -615,41 +646,39 @@ export default {
}); });
}); });
}, },
scrollToElement(elementId, speed, easing) { scrollToElement(elementId, duration = 800, easing = "ease-in-out") {
// TODO - needs to be cleaned up & refactored const wrapper = document.querySelector(".page-container-grouped-styles");
function scrollTopSmooth(wrapper, target, duration = 300, timingName = "linear") { const target = document.getElementById(elementId);
const initY = wrapper.scrollTop; const initY = wrapper.scrollTop;
const wrapperRect = wrapper.getBoundingClientRect(); const wrapperRect = wrapper.getBoundingClientRect();
const targetRect = target.getBoundingClientRect(); const targetRect = target.getBoundingClientRect();
const targetY = targetRect.top - wrapperRect.top - BUFFER_OFFSET; const targetY = targetRect.top - wrapperRect.top - BUFFER_OFFSET;
const timingFunc = TIMINGFUNC_MAP[timingName]; const timingFunc = TIMINGFUNC_MAP[easing];
let start = null; let start = null;
const step = (timestamp) => {
const step = (timestamp) => { start = start || timestamp;
start = start || timestamp; const time = Math.min(1, (timestamp - start) / duration);
const progress = timestamp - start, const percentageNew = timingFunc(time);
// Growing from 0 to 1 const distanceToGo = targetY;
time = Math.min(1, (timestamp - start) / duration); const thisDistance = percentageNew * distanceToGo;
const percentageNew = timingFunc(time); wrapper.scrollTo(0, initY + thisDistance);
const distanceToGo = targetY; if (percentageNew < 1) {
const thisDistance = percentageNew * distanceToGo; window.requestAnimationFrame(step);
}
wrapper.scrollTo(0, initY + thisDistance); };
if (percentageNew < 1) { window.requestAnimationFrame(step);
window.requestAnimationFrame(step); },
} },
}; watch: {
window.requestAnimationFrame(step); modelValue(newValue, oldValue) {
} this.resetField({
value: newValue,
const wrapper = document.getElementById("date-picker-fieldset"); });
const targetMonth = document.getElementById(elementId);
scrollTopSmooth(wrapper, targetMonth, 800, "ease-in-out");
}, },
}, },
components: { components: {
loader, loader,
ErrorMessage,
}, },
}; };
</script> </script>
@ -660,27 +689,22 @@ export default {
max-height: 0; max-height: 0;
} }
.date-picker { .date-picker {
overflow: hidden;
position: relative; position: relative;
flex-grow: 1; flex-grow: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
fieldset { fieldset {
overflow-y: auto;
flex-grow: 1; flex-grow: 1;
position: relative; position: relative;
padding-bottom: 6rem; // arbitrary amount to provide enough spacing for scroll animation
max-height: 21.25rem; // 340px... arbitrary amount to not push View More link out of view on iphones
} }
.loader { .loader {
position: absolute;
height: 2rem; height: 2rem;
width: 2rem; width: calc(100% - 1.5rem);
&::after { &::after {
width: 100%; width: 1.5rem;
height: 100%; height: 1.5rem;
} }
} }
.calendar-grid-container { .calendar-grid-container {
@ -813,9 +837,9 @@ export default {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
width: 3rem; min-width: 2.5rem;
width: 2.5rem;
height: 2.5rem; height: 2.5rem;
min-width: 3rem;
span { span {
&.small { &.small {
@ -916,16 +940,15 @@ export default {
margin-bottom: 0; margin-bottom: 0;
overflow: hidden; overflow: hidden;
} }
&.last-available-month:not(&.month-hidden) {
margin-bottom: 14rem;
}
} }
.btn-link { .btn-link {
font-weight: 500; font-weight: 500;
text-underline-offset: 4px; text-underline-offset: 4px;
position: absolute; flex-grow: 0;
bottom: 0; &:focus {
box-shadow: none;
}
} }
.past { .past {
@ -997,4 +1020,10 @@ export default {
text-underline-offset: 4px; text-underline-offset: 4px;
z-index: 2; z-index: 2;
} }
.form-test-error {
margin: 0 auto;
max-width: 414px;
text-align: left;
}
</style> </style>

View file

@ -2,7 +2,7 @@
<div class="funnel-sub-header"> <div class="funnel-sub-header">
<div <div
class="d-flex align-items-center justify-content-center container-fluid overflow-hidden"> class="d-flex align-items-center justify-content-center container-fluid overflow-hidden">
<h5 class="text-center fw-normal mb-2" :class="headerColor"> <h5 class="text-center fw-normal mb-0" :class="headerColor">
<span> <span>
{{ text }} {{ text }}
</span> </span>

View file

@ -162,6 +162,11 @@ describe("quote.vue", () => {
}, },
referralNumber: "1234567", referralNumber: "1234567",
}, },
payment: {
insuranceCoverage: {
isVerified: false,
},
},
}; };
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
@ -185,6 +190,11 @@ describe("quote.vue", () => {
glassParts: ["item", "item2"], glassParts: ["item", "item2"],
}, },
}, },
payment: {
insuranceCoverage: {
isVerified: false,
},
},
}; };
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();

View file

@ -150,7 +150,7 @@ export default {
(store.getters.order.damage.isRepair || (store.getters.order.damage.isRepair ||
(store.getters.order.lineItems?.glassParts != null && (store.getters.order.lineItems?.glassParts != null &&
store.getters.order.lineItems.glassParts.length > 0)) && store.getters.order.lineItems.glassParts.length > 0)) &&
store.getters.order.referralNumber?.length !== 6 !store.getters.payment.insuranceCoverage.isVerified
); );
}, },
getDefaultIsInsuranceSelectedValue(availableLineItems) { getDefaultIsInsuranceSelectedValue(availableLineItems) {
@ -166,7 +166,7 @@ export default {
return availableLineItems return availableLineItems
? baseMixin.methods.getTierOnePackagePrice( ? baseMixin.methods.getTierOnePackagePrice(
baseMixin.methods.filterOutFees(availableLineItems) baseMixin.methods.filterOutFees(availableLineItems)
) > 500 ) > 300
: null; : null;
} }
}, },

View file

@ -18,6 +18,7 @@
v-model="selectedDate" v-model="selectedDate"
class="text-link-small" class="text-link-small"
:customSelectableDatesCallback="getAvailableDatesMethod" :customSelectableDatesCallback="getAvailableDatesMethod"
validationRules="date-required"
@date-clicked="openInshopTimeSlotsModal" /> @date-clicked="openInshopTimeSlotsModal" />
<time-slot-modal-question <time-slot-modal-question
ref="timeSlotModalQuestion" ref="timeSlotModalQuestion"
@ -93,7 +94,7 @@ const getAvailableDates = async (
const timeSlotsData = {}; const timeSlotsData = {};
timeSlotsData.days = []; timeSlotsData.days = [];
let apiStartDate = startDateString; let apiStartDate = startDateString;
let apiEndDate = apiEndDateLimit; let apiEndDate = endDateString;
for (let i = 1; i <= apiCallsCount; i++) { for (let i = 1; i <= apiCallsCount; i++) {
let storeActionConfig; let storeActionConfig;
@ -105,6 +106,10 @@ const getAvailableDates = async (
if (i === apiCallsCount) { if (i === apiCallsCount) {
apiEndDate = endDateString; apiEndDate = endDateString;
} }
} else {
if (apiEndDate > apiEndDateLimit) {
apiEndDate = apiEndDateLimit;
}
} }
if (appointmentType === AppointmentTypeStrings.MOBILE) { if (appointmentType === AppointmentTypeStrings.MOBILE) {
@ -126,13 +131,13 @@ const getAvailableDates = async (
}, },
}; };
} }
storeActionConfigs.push(storeActionConfig); if (apiStartDate < apiEndDate) storeActionConfigs.push(storeActionConfig);
} }
// ASYNC METHOD
const timeSlotsResponsesData = { const timeSlotsResponsesData = {
days: [], days: [],
}; };
function compareDayStrings(a, b) { function compareDayStrings(a, b) {
if (a.date < b.date) return -1; if (a.date < b.date) return -1;
if (a.date > b.date) return 1; if (a.date > b.date) return 1;

View file

@ -319,15 +319,19 @@ export default {
return `${hours}:${minutes} ${meridianNotation}`; return `${hours}:${minutes} ${meridianNotation}`;
}, },
getDisplayTextForDurationLength(durationMinimum, durationMaximum) { getDisplayTextForDurationLength(durationMinimum, durationMaximum) {
let displayTextForDurationLength; const isLongAppointment = durationMaximum >= 120;
if (durationMaximum >= 120) { const isDurationRange = durationMinimum !== durationMaximum;
displayTextForDurationLength = `${durationMinimum / 60} - ${
durationMaximum / 60 const adjustedMinimum = isLongAppointment ? durationMinimum / 60 : durationMinimum;
} hours`; const adjustedMaximum = isLongAppointment ? durationMaximum / 60 : durationMaximum;
} else {
displayTextForDurationLength = `${durationMinimum} - ${durationMaximum} minutes`; const durationText = isDurationRange
} ? `${adjustedMinimum} - ${adjustedMaximum}`
return displayTextForDurationLength; : adjustedMinimum;
const unitText = isLongAppointment ? "hours" : "minutes";
return `${durationText} ${unitText}`;
}, },
getRelevantDropOffCmsWidgetNameForSelectedTimeSlot( getRelevantDropOffCmsWidgetNameForSelectedTimeSlot(
selectedTimeSlotId, selectedTimeSlotId,

View file

@ -25,8 +25,10 @@
<span v-if="!isLoaderDisplayed" class="m-0 button-auxillary-copy">{{ <span v-if="!isLoaderDisplayed" class="m-0 button-auxillary-copy">{{
badgeText badgeText
}}</span> }}</span>
<loader v-if="isLoaderDisplayed" :class="['left', 'no-block']" /> <loader
<!-- no-block allows the user to click between shops while availability indicators are loading --> v-if="isLoaderDisplayed"
:loaderPosition="left"
:allowPageInteraction="true" />
</div> </div>
</div> </div>
<div class="row-two"> <div class="row-two">

View file

@ -79,7 +79,12 @@
:manualHeadline="AlertPerfectMatchInsuranceVerifiedHeader" :manualHeadline="AlertPerfectMatchInsuranceVerifiedHeader"
:manualCopy="AlertPerfectMatchInsuranceVerifiedBody" :manualCopy="AlertPerfectMatchInsuranceVerifiedBody"
v-model="customAlertData" v-model="customAlertData"
v-if="vinPopulatedOnPageLoad && isInsuranceVerified && !displayInvalidZipAlert" v-if="
vinPopulatedOnPageLoad &&
isInsuranceVerified &&
!displayInvalidZipAlert &&
!displayNonServiceableZipAlert
"
alertClass="alert-success" /> alertClass="alert-success" />
<alert <alert
class="my-4" class="my-4"
@ -106,7 +111,12 @@
:manualHeadline="AlertPerfectMatchInsuranceNotVerifiedHeader" :manualHeadline="AlertPerfectMatchInsuranceNotVerifiedHeader"
:manualCopy="AlertPerfectMatchInsuranceNotVerifiedBody" :manualCopy="AlertPerfectMatchInsuranceNotVerifiedBody"
v-model="customAlertData" v-model="customAlertData"
v-if="vinPopulatedOnPageLoad && !isInsuranceVerified && !displayInvalidZipAlert" v-if="
vinPopulatedOnPageLoad &&
!isInsuranceVerified &&
!displayInvalidZipAlert &&
!displayNonServiceableZipAlert
"
alertClass="alert-success" /> alertClass="alert-success" />
<funnelFooter <funnelFooter
cmsWidgetName="FunnelFooterWidget" cmsWidgetName="FunnelFooterWidget"

View file

@ -446,9 +446,7 @@ export default {
const payment = store.getters.payment; const payment = store.getters.payment;
if (store.getters.order.referralNumber?.length === 6) { if (payment.isInsurance && payment.insuranceCoverage.isVerified) {
navigateToHeritageFunnel({ loadingModal: self.$refs.loadingModal });
} else if (payment.isInsurance && payment.insuranceCoverage.isVerified) {
navigateToHeritageFunnel({ loadingModal: self.$refs.loadingModal }); navigateToHeritageFunnel({ loadingModal: self.$refs.loadingModal });
} else { } else {
self.$router.navigateWithSaving( self.$router.navigateWithSaving(

View file

@ -96,6 +96,7 @@ export default {
outline: none; outline: none;
display: flex; display: flex;
position: relative; position: relative;
justify-content: space-between;
p { p {
color: $gray-600; color: $gray-600;

View file

@ -3,7 +3,11 @@
class="loader" class="loader"
role="alert" role="alert"
aria-label="Loading new page" aria-label="Loading new page"
v-bind:class="[this.loaderColor, this.loaderPosition]"></div> v-bind:class="[
this.loaderColor,
this.loaderPosition,
this.allowPageInteraction ? 'allow-ui-interaction' : '',
]"></div>
</template> </template>
<script> <script>
@ -19,6 +23,12 @@ export default {
loaderPosition: { loaderPosition: {
type: String, type: String,
}, },
/* Set to true for loaders where UI interaction must be allowed to continue while the loader is active
(See example shop-list-button) */
allowPageInteraction: {
type: Boolean,
default: false,
},
}, },
}; };
</script> </script>
@ -83,8 +93,8 @@ export default {
&.black:after { &.black:after {
background-color: $black; background-color: $black;
} }
//no-block to enable clicking on certain buttons with loaders while the loader is actives &.allow-ui-interaction::before {
&.no-block::before { //no-block to enable clicking on certain buttons with loaders while the loader is actives
z-index: -1; z-index: -1;
} }
} }