Merge pull request #1279 from Safelite/feature/CSR-1372

Feature/csr 1372
This commit is contained in:
chloeherdsafelite 2023-08-10 08:58:31 -04:00 committed by GitHub
commit cdb521ab47
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 150 additions and 41 deletions

View file

@ -3,6 +3,7 @@
<div class="d-flex">
<textBlock
:cmsWidgetName="headerCmsWidgetName"
:customText="customHeaderText"
typeStyle="body small bold dark"
marginTopSizeOverride="0" />
<textLink
@ -26,9 +27,10 @@ import textBlock from "@/digital-components/text-block/text-block";
import textLink from "@/ux-components/text-link/text-link";
export default {
name: "vehicle-review",
name: "review-block",
props: {
headerCmsWidgetName: String,
customHeaderText: String,
// Specifically an array of strings.
content: Array,
},

View file

@ -0,0 +1,40 @@
<template>
<reviewBlock
:customHeaderText="headerText"
:content="displayContent"
@edit-clicked="editClicked" />
</template>
<script>
import reviewBlock from "@/layouts/review/review-block/review-block";
export default {
name: "schedule-review",
props: {
cmsWidgetName: String,
appointmentType: String,
},
data() {
return {};
},
methods: {
editClicked() {
this.$emit("edit-clicked");
},
},
computed: {
displayContent() {
return [
this.getCmsContent(this.cmsWidgetName, "BodyText"),
this.getCmsContent(this.cmsWidgetName, "BodyText2"),
];
},
headerText() {
return this.getCmsContent(this.cmsWidgetName, "HeaderText");
},
},
components: {
reviewBlock,
},
};
</script>

View file

@ -10,7 +10,7 @@ import reviewBlock from "@/layouts/review/review-block/review-block";
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
export default {
name: "service-package-review",
name: "service-location-review",
props: {
cmsWidgetName: String,
serviceLocation: Object,

View file

@ -69,6 +69,13 @@
cmsWidgetName="ServiceLocationTitleWidget"
:serviceLocation="serviceLocationInfo"
@edit-clicked="editServiceLocation" />
<hr class="my-0" />
<scheduleReview
cmsWidgetName="ScheduleWidget"
:appointmentType="appointmentType"
@edit-clicked="editSchedule" />
</div>
<div>
@ -94,22 +101,16 @@ import vehicleReview from "@/layouts/review/review-sections/vehicle-review/vehic
import damageReview from "@/layouts/review/review-sections/damage-review/damage-review";
import servicePackageReview from "@/layouts/review/review-sections/service-package-review/service-package-review";
import serviceLocationReview from "@/layouts/review/review-sections/service-location-review/service-location-review";
import scheduleReview from "@/layouts/review/review-sections/schedule-review/schedule-review";
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { settleAllPromises } from "@/helpers/layout-helper";
import baseMixin from "@/mixins/base-mixin.js";
import { storeActions } from "@/constants/store-actions";
export default {
name: "review",
async beforeRouteEnter(to, from, next) {
// Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
// Get rain defense and wiper availability for package review section.
const wipersPromise = baseMixin.methods.dispatchStoreAction(storeActions.GET_WIPERS);
const rainDefensePromise = baseMixin.methods.dispatchStoreAction(
storeActions.GET_RAIN_DEFENSE
);
const servicePackageInitialDataPromise = servicePackageReview.methods.loadInitialData();
@ -168,6 +169,12 @@ export default {
this.$route
);
},
editSchedule() {
this.$router.navigateWithoutSaving(
this.navigationScenarios.CLICKED_SCHEDULE_EDIT,
this.$route
);
},
},
computed: {
subHeaderTitle() {
@ -191,6 +198,12 @@ export default {
serviceLocationInfo() {
return this.$store.getters.order.serviceLocation;
},
appointmentType() {
return this.$store.getters.order.serviceLocation.appointmentType;
},
exposeCms() {
return this.$root.cmsContentByWidget;
},
},
components: {
funnelHeader,
@ -202,6 +215,7 @@ export default {
damageReview,
servicePackageReview,
serviceLocationReview,
scheduleReview,
},
};
</script>

View file

@ -45,3 +45,33 @@ export function sumDateString(dateString, daysToAdd) {
date.setDate(date.getDate() + daysToAdd);
return convertDateToDateString(date);
}
export function militaryToTwelveHourTime(timeString) {
// Expected input: "HH:MM"
if (typeof timeString !== "string") return;
let hours = parseInt(timeString.split(":")[0]);
const minutes = timeString.split(":")[1];
const meridianNotation = hours > 11 ? "PM" : "AM";
if (hours > 12) {
hours -= 12;
}
return `${hours}:${minutes} ${meridianNotation}`;
}
export function getDisplayTextForDurationLength(durationMinimum, durationMaximum) {
const isLongAppointment = durationMaximum >= 120;
const isDurationRange = durationMinimum !== durationMaximum;
const adjustedMinimum = isLongAppointment ? durationMinimum / 60 : durationMinimum;
const adjustedMaximum = isLongAppointment ? durationMaximum / 60 : durationMaximum;
const durationText = isDurationRange
? `${adjustedMinimum} - ${adjustedMaximum}`
: adjustedMinimum;
const unitText = isLongAppointment ? "hours" : "minutes";
return `${durationText} ${unitText}`;
}

View file

@ -459,6 +459,7 @@ export default {
startTime: null,
endTime: null,
jobMaxMinutes: null,
jobMinMinutes: null,
};
}
},

View file

@ -47,7 +47,11 @@ import buttonQuestion from "@/digital-components/button-question/button-question
import timeSlotModalListButton from "./time-slot-modal-list-button/time-slot-modal-list-button";
// Helpers
import { convertDateStringToDate } from "@/layouts/schedule/helpers/schedule-helper";
import {
convertDateStringToDate,
militaryToTwelveHourTime,
getDisplayTextForDurationLength,
} from "@/layouts/schedule/helpers/schedule-helper";
import { deepClone } from "@/helpers/object-helper";
// Validation - TODO: Move this somewhere more global?
@ -88,6 +92,7 @@ export default {
startTime: null,
endTime: null,
jobMaxMinutes: null,
jobMinMinutes: null,
}),
},
cmsWidgetName: String,
@ -263,7 +268,7 @@ export default {
cmsWidgetFieldMappings.DURATION
);
const inshopDurationTime = this.getDisplayTextForDurationLength(
const inshopDurationTime = getDisplayTextForDurationLength(
this.estimatedServiceMinutesMinimum,
this.estimatedServiceMinutesMaximum
);
@ -346,33 +351,6 @@ export default {
this.$emit("update:modelValue", this.selectedValue);
this.closeModal();
},
getDisplayTextForMilitaryTime(militaryTimeInput) {
// Expected input: "HH:MM"
let hours = parseInt(militaryTimeInput.split(":")[0]);
const minutes = militaryTimeInput.split(":")[1];
const meridianNotation = hours > 11 ? "PM" : "AM";
if (hours > 12) {
hours -= 12;
}
return `${hours}:${minutes} ${meridianNotation}`;
},
getDisplayTextForDurationLength(durationMinimum, durationMaximum) {
const isLongAppointment = durationMaximum >= 120;
const isDurationRange = durationMinimum !== durationMaximum;
const adjustedMinimum = isLongAppointment ? durationMinimum / 60 : durationMinimum;
const adjustedMaximum = isLongAppointment ? durationMaximum / 60 : durationMaximum;
const durationText = isDurationRange
? `${adjustedMinimum} - ${adjustedMaximum}`
: adjustedMinimum;
const unitText = isLongAppointment ? "hours" : "minutes";
return `${durationText} ${unitText}`;
},
getRelevantDropOffCmsWidgetNameForSelectedTimeSlot(
selectedTimeSlotId,
isSameDayRelevant = false
@ -387,7 +365,7 @@ export default {
},
getAvailableTimeSlotsForInshop(timeSlotsForSelectedDate) {
return timeSlotsForSelectedDate.map((timeSlot) => {
const readableTime = this.getDisplayTextForMilitaryTime(timeSlot.startTime);
const readableTime = militaryToTwelveHourTime(timeSlot.startTime);
return {
value: timeSlot.id,
buttonLabel: readableTime,
@ -415,9 +393,9 @@ export default {
},
getAvailableTimeSlotsForMobile(timeSlotsForSelectedDate) {
const availableTimeSlots = timeSlotsForSelectedDate.map((timeSlot) => {
const readableTime = `${this.getDisplayTextForMilitaryTime(
const readableTime = `${militaryToTwelveHourTime(
timeSlot.startTime
)} - ${this.getDisplayTextForMilitaryTime(timeSlot.endTime)}`;
)} - ${militaryToTwelveHourTime(timeSlot.endTime)}`;
return {
value: timeSlot.id,
buttonLabel: readableTime,
@ -472,6 +450,7 @@ export default {
startTime: timeSlot.startTime,
endTime: timeSlot.endTime,
jobMaxMinutes: this.estimatedServiceMinutesMaximum.toString(),
jobMinMinutes: this.estimatedServiceMinutesMinimum.toString(),
};
}
@ -481,6 +460,7 @@ export default {
endTime: null,
routeCode: null,
jobMaxMinutes: null,
jobMinMinutes: null,
};
},
},

View file

@ -51,6 +51,7 @@ const navigationScenarios = {
CLICKED_DAMAGE_EDIT: "CLICKED_DAMAGE_EDIT",
CLICKED_SERVICE_PACKAGE_EDIT: "CLICKED_SERVICE_PACKAGE_EDIT",
CLICKED_SERVICE_LOCATION_EDIT: "CLICKED_SERVICE_LOCATION_EDIT",
CLICKED_SCHEDULE_EDIT: "CLICKED_SCHEDULE_EDIT",
};
export { navigationScenarios };

View file

@ -484,6 +484,10 @@ const routingTable = function (store) {
scenario: navigationScenarios.CLICKED_SERVICE_LOCATION_EDIT,
destinationFmgPageValue: fmgPageValues.SERVICE_LOCATION,
},
{
scenario: navigationScenarios.CLICKED_SCHEDULE_EDIT,
destinationFmgPageValue: fmgPageValues.SCHEDULE,
},
],
},
];

View file

@ -14,6 +14,11 @@ import { deleteFunnelCookie } from "@/helpers/heritage-integration/cookie-helper
import { deepEqual } from "@/helpers/object-helper";
import { AppointmentTypeStrings, PREMIUM_FEE_PART_TYPE } from "@/constants/schedule-constants";
import { partTypeStrings } from "@/constants/part-type-strings";
import {
convertDateStringToDate,
militaryToTwelveHourTime,
getDisplayTextForDurationLength,
} from "@/layouts/schedule/helpers/schedule-helper";
// Export State
const getDefaultState = () => {
@ -96,6 +101,7 @@ const getDefaultState = () => {
endTime: null,
routeCode: null,
jobMaxMinutes: null,
jobMinMinutes: null,
},
referralNumber: null,
referralDate: null,
@ -283,6 +289,7 @@ export const mutations = {
state.order.schedule.endTime = scheduleInfo.endTime;
state.order.schedule.routeCode = scheduleInfo.routeCode;
state.order.schedule.jobMaxMinutes = scheduleInfo.jobMaxMinutes;
state.order.schedule.jobMinMinutes = scheduleInfo.jobMinMinutes;
}
},
updateCustomerDetails(state, customerDetails) {
@ -368,6 +375,7 @@ export const mutations = {
state.order.schedule.endTime = null;
state.order.schedule.routeCode = null;
state.order.schedule.jobMaxMinutes = null;
state.order.schedule.jobMinMinutes = null;
//premium appointment fee used on schedule page also needs reset when schedule is reset
const supportingItems = state.order.lineItems.supportingItems;
@ -506,6 +514,7 @@ export const mutations = {
state.order.schedule.endTime = sessionInformation.order.schedule?.endTime;
state.order.schedule.routeCode = sessionInformation.order.schedule?.routeCode;
state.order.schedule.jobMaxMinutes = sessionInformation.order.schedule?.jobMaxMinutes;
state.order.schedule.jobMinMinutes = sessionInformation.order.schedule?.jobMinMinutes;
},
updateExperiments(state, experiments) {
state.applicationUser.experiments = experiments;
@ -540,6 +549,9 @@ export const getters = {
isMobileAppointment: (state) => {
return state.order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE;
},
isDropOffAppointment: (state) => {
return state.order.serviceLocation.appointmentType === AppointmentTypeStrings.DROP_OFF;
},
isRecalibrationOnOrder: (state) => {
return getHasRecalibrationPart(state);
},
@ -548,6 +560,30 @@ export const getters = {
(vap) => vap.partType.toUpperCase() === partTypeStrings.REAR_WIPER.toUpperCase()
);
},
scheduleDisplayText: (state) => {
const dateModel = convertDateStringToDate(state.order.schedule.date);
const readableDate = dateModel?.toLocaleDateString("en-us", {
weekday: "long",
month: "long",
day: "numeric",
year: "numeric",
});
const readableStartTime = militaryToTwelveHourTime(state.order.schedule.startTime);
const readableEndTime = militaryToTwelveHourTime(state.order.schedule.endTime);
const readableDuration = getDisplayTextForDurationLength(
state.order.schedule.jobMinMinutes,
state.order.schedule.jobMaxMinutes
);
return {
date: readableDate,
startTime: readableStartTime,
endTime: readableEndTime,
duration: readableDuration,
};
},
lineItems: (state) => state.order.lineItems,
pageData: (state) => (page) => {
return state.applicationUser.pageData[page];
@ -1458,6 +1494,7 @@ export const actions = {
endTime: order.schedule?.endTime,
routeCode: order.schedule?.routeCode,
jobMaxMinutes: order.schedule?.jobMaxMinutes,
jobMinMinutes: order.schedule?.jobMinMinutes,
},
existingPromoCode: null,
referralCorrelationId: order.referralCorrelationId,