CSR-1137 | All timeslot implementations working
This commit is contained in:
parent
6ca3d916ba
commit
23711787dd
10 changed files with 343 additions and 52 deletions
|
|
@ -103,6 +103,11 @@ const endpoints = {
|
|||
mockUrl: "https://mockey.qa.sagaws.net/service/shop-time-slots", // TODO: REMOVE MOCKURL
|
||||
method: "POST",
|
||||
},
|
||||
GetMobileEarlyBirdFee: {
|
||||
url: "/parts/api/v1/parts/mobile-early-bird-fee",
|
||||
mockUrl: "https://mockey.qa.sagaws.net/service/mobile-early-bird-fee", // TODO: REMOVE MOCKURL
|
||||
method: "GET",
|
||||
},
|
||||
SaveSession: {
|
||||
url: "/order/api/v1/order/save-session",
|
||||
method: "POST",
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ const storeActions = {
|
|||
GET_MOBILE_FEE_PART: "getMobileFeePart",
|
||||
GET_SERVICEABILITY_DETAILS: "getServiceabilityDetails",
|
||||
GET_SHOP_TIME_SLOTS: "getShopTimeSlots",
|
||||
GET_MOBILE_EARLY_BIRD_FEE: "getMobileEarlyBirdFee",
|
||||
SAVE_SESSION: "saveSession",
|
||||
LOAD_SESSION: "loadSession",
|
||||
UPDATE_STORE_WITH_SAVE_SESSION_RESPONSE: "updateStoreWithSaveSessionResponse",
|
||||
|
|
|
|||
|
|
@ -176,6 +176,7 @@ export default {
|
|||
getComponentLoopWrapperClasses() {
|
||||
let classes;
|
||||
switch (this.buttonTypeString) {
|
||||
case "timeSlotModalListButton":
|
||||
case "listButton":
|
||||
classes = "w-100";
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -56,13 +56,6 @@ export default {
|
|||
onModalClosedCallback: {
|
||||
type: Function,
|
||||
},
|
||||
allowManualFooterDisableOverride: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isFooterDisabledManualOverride: {
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const modalId = `modal-${crypto.randomUUID()}`;
|
||||
|
|
@ -105,13 +98,10 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
isFooterButtonDisabled() {
|
||||
if (this.allowManualFooterDisableOverride !== null) {
|
||||
return this.isFooterDisabledManualOverride;
|
||||
} else if (!this.meta.touched) {
|
||||
if (!this.meta.touched) {
|
||||
return !this.meta.valid;
|
||||
} else {
|
||||
return !this.meta.dirty || !this.meta.valid;
|
||||
}
|
||||
}
|
||||
return !this.meta.dirty || !this.meta.valid;
|
||||
},
|
||||
},
|
||||
components: {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div
|
||||
class="text-block w-100 mt-2 d-flex"
|
||||
class="text-block w-100 mt-2"
|
||||
:class="[justifyText, typeStyle, fontWeight]"
|
||||
v-html="this.TextBlockCopy"></div>
|
||||
</template>
|
||||
|
|
@ -28,6 +28,7 @@ export default {
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.text-block {
|
||||
display: flex;
|
||||
&.left {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,10 +26,20 @@
|
|||
<time-slot-modal-question
|
||||
ref="timeSlotModalQuestion"
|
||||
cmsWidgetName="TimeSlotModalQuestion"
|
||||
earlyBirdCmsWidgetName="EarlyBirdTimeSlotModal"
|
||||
mobileCmsWidgetName="MobileTimeSlotModal"
|
||||
dropoffCmsWidgetName="DropOffTimeSlotModal"
|
||||
v-model="selectedTimeSlotId"
|
||||
:dateAndTimeSlotData="TimeSlotsForSelectedDate"
|
||||
:appointmentType="appointmentType"
|
||||
:mobileEarlyBirdFee="mobileEarlyBirdFee"
|
||||
:dateAndTimeSlotData="timeSlotsForSelectedDate"
|
||||
:estimatedServiceMinutesMinimum="selectableDatesData.estimatedServiceMinutesMinimum"
|
||||
:estimatedServiceMinutesMaximum="selectableDatesData.estimatedServiceMinutesMaximum"/>
|
||||
:estimatedServiceMinutesMaximum="
|
||||
selectableDatesData.estimatedServiceMinutesMaximum"
|
||||
validationRules="time-slot-selection-required"/>
|
||||
<button @click="setAppointmentType('Inshop')">Set to Inshop</button>
|
||||
<button @click="setAppointmentType('Mobile')">Set to Mobile</button>
|
||||
<button @click="setAppointmentType('Dropoff')">Set to Dropoff</button>
|
||||
<button @click="openInshopTimeSlotsModal">Click for timeslots</button>
|
||||
<funnel-footer
|
||||
cmsWidgetName="FunnelFooterWidget"
|
||||
|
|
@ -67,6 +77,7 @@ import { errorMessages } from "@/constants/error-messages";
|
|||
import { required } from "@/helpers/validation-rules";
|
||||
|
||||
defineRule("date-required", required(errorMessages.DATE_REQUIRED));
|
||||
defineRule("time-slot-selection-required", required(errorMessages.DATE_REQUIRED));
|
||||
|
||||
export default {
|
||||
name: "schedule",
|
||||
|
|
@ -75,11 +86,27 @@ export default {
|
|||
selectedDate: null,
|
||||
selectedTimeSlotId: null,
|
||||
selectableDatesData: [],
|
||||
mobileEarlyBirdFee: null,
|
||||
TEMPORARYappointmentType: "Inshop",
|
||||
|
||||
};
|
||||
},
|
||||
async beforeRouteEnter(to, from, next) {
|
||||
// Call APIs
|
||||
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
||||
const earlyBirdPromise = baseMixin.methods.dispatchStoreAction(
|
||||
storeActions.GET_MOBILE_EARLY_BIRD_FEE
|
||||
);
|
||||
// Price EARLY BIRD pre-emptively to allow for asynchronous call to pricing
|
||||
// const pricingPromise = baseMixin.methods.dispatchStoreAction(
|
||||
// storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA,
|
||||
// {
|
||||
// availableLineItems: [
|
||||
// {partNumber: "EARLY BIRD"}
|
||||
// ],
|
||||
// },
|
||||
// false
|
||||
// );
|
||||
|
||||
// Settle promises and get results
|
||||
const promiseResultMap = [
|
||||
|
|
@ -87,6 +114,14 @@ export default {
|
|||
resultKey: "cmsContent",
|
||||
promise: cmsContentPromise,
|
||||
},
|
||||
{
|
||||
resultKey: "earlyBird",
|
||||
promise: earlyBirdPromise,
|
||||
},
|
||||
// {
|
||||
// resultKey: "pricingResults",
|
||||
// promise: pricingPromise,
|
||||
// },
|
||||
];
|
||||
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
|
|
@ -94,6 +129,13 @@ export default {
|
|||
// Call the "next" function to complete the transition to this page.
|
||||
next((vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
vm.mobileEarlyBirdFee = resultMap.earlyBird;
|
||||
vm.mobileEarlyBirdFee.laborAmount = 15;
|
||||
vm.mobileEarlyBirdFee.sellingPrice = 0;
|
||||
vm.mobileEarlyBirdFee.kitPrice = 0;
|
||||
// if (resultMap.earlyBird) {
|
||||
// vm.mobileEarlyBirdFee = resultMap.pricingResults;
|
||||
// }
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -104,17 +146,25 @@ export default {
|
|||
// Splits content when brackets are found in text so that text can be looped through and router-link can be injected when needed
|
||||
return this.splitCopyOnCMSPlaceHolder(this.ChangeShopLinkText);
|
||||
},
|
||||
TimeSlotsForSelectedDate() {
|
||||
appointmentType() {
|
||||
return this.TEMPORARYappointmentType;
|
||||
//return this.$store.getters.order.serviceLocation.appointmentType;
|
||||
},
|
||||
timeSlotsForSelectedDate() {
|
||||
if (this.selectedDate === null) {
|
||||
return null;
|
||||
}
|
||||
return this.selectableDatesData.find(selectableDate => selectableDate.dateString === this.selectedDate.dateString);
|
||||
return this.selectableDatesData.find(
|
||||
(selectableDate) => selectableDate.dateString === this.selectedDate.dateString
|
||||
);
|
||||
},
|
||||
appointmentDateAndTime() {
|
||||
if (!this.selectedTimeSlotId) {
|
||||
return null;
|
||||
}
|
||||
const timeSlotSelectedObject = this.getTimeSlotObjectFromTimeSlotId(this.selectedTimeSlotId);
|
||||
const timeSlotSelectedObject = this.getTimeSlotObjectFromTimeSlotId(
|
||||
this.selectedTimeSlotId
|
||||
);
|
||||
if (timeSlotSelectedObject) {
|
||||
return {
|
||||
date: this.selectedDate.dateString,
|
||||
|
|
@ -125,7 +175,6 @@ export default {
|
|||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -138,6 +187,7 @@ export default {
|
|||
// NEED TODO - WHAT ARE PAGE REQ'S FOR THIS PAGE?
|
||||
},
|
||||
async getAvailableDates(startDate, endDate) {
|
||||
console.log('calling getAvailableDates', startDate, endDate);
|
||||
// USING DATES PASSED, MAKE AN API CALL
|
||||
const newShopTimeSlotsResponse = await baseMixin.methods.dispatchStoreAction(
|
||||
storeActions.GET_SHOP_TIME_SLOTS,
|
||||
|
|
@ -151,13 +201,14 @@ export default {
|
|||
const newShopTimeSlots = newShopTimeSlotsResponse.data;
|
||||
// console.log("newShopTimeSlots ", newShopTimeSlots)
|
||||
|
||||
|
||||
// ADD API CALL RESULTS TO EXISTING DATE DATA
|
||||
this.selectableDatesData = this.selectableDatesData.concat(
|
||||
this.convertApiResponse(newShopTimeSlots.days)
|
||||
);
|
||||
this.selectableDatesData.estimatedServiceMinutesMaximum = newShopTimeSlots.estimatedServiceMinutesMaximum;
|
||||
this.selectableDatesData.estimatedServiceMinutesMinimum = newShopTimeSlots.estimatedServiceMinutesMinimum;
|
||||
this.selectableDatesData.estimatedServiceMinutesMaximum =
|
||||
newShopTimeSlots.estimatedServiceMinutesMaximum;
|
||||
this.selectableDatesData.estimatedServiceMinutesMinimum =
|
||||
newShopTimeSlots.estimatedServiceMinutesMinimum;
|
||||
console.log("this.selectableDatesData is now: ", this.selectableDatesData);
|
||||
|
||||
// RETURN AGGREGATE DATE DATA
|
||||
|
|
@ -175,12 +226,17 @@ export default {
|
|||
});
|
||||
return responseData;
|
||||
},
|
||||
setAppointmentType(appointmentType) {
|
||||
this.TEMPORARYappointmentType = appointmentType;
|
||||
},
|
||||
openInshopTimeSlotsModal() {
|
||||
this.$refs["timeSlotModalQuestion"].openModal();
|
||||
},
|
||||
getTimeSlotObjectFromTimeSlotId(timeSlotId) {
|
||||
const timeSlots = this.selectableDatesData.find(selectableDate => selectableDate.dateString === this.selectedDate.dateString).timeSlots;
|
||||
return timeSlots.find(timeSlot => timeSlot.id === timeSlotId);
|
||||
const timeSlots = this.selectableDatesData.find(
|
||||
(selectableDate) => selectableDate.dateString === this.selectedDate.dateString
|
||||
).timeSlots;
|
||||
return timeSlots.find((timeSlot) => timeSlot.id === timeSlotId);
|
||||
},
|
||||
backButtonAction() {
|
||||
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||
|
|
|
|||
|
|
@ -4,23 +4,32 @@
|
|||
class="time-slots-modal"
|
||||
:headerText="dateSelectedReadableDate"
|
||||
:footerButtonText="footerCloseButtonText"
|
||||
allowManualFooterDisableOverride
|
||||
:isFooterDisabledManualOverride="selectedTimeSlot === null"
|
||||
:onModalOpenedCallback="onModalOpened"
|
||||
:onModalClosedCallback="onModalClosed"
|
||||
@footer-button-event="closeModal">
|
||||
<textBlock
|
||||
:customText="appointmentDurationTextWithTime"
|
||||
v-show="inshopAppointmentDurationTextWithTime"
|
||||
:customText="inshopAppointmentDurationTextWithTime"
|
||||
justifyText="center"
|
||||
typeStyle="small"
|
||||
class="mb-5" />
|
||||
class="duration-text-block" />
|
||||
<buttonQuestion
|
||||
class="radioQuestion"
|
||||
buttonTypeString="timeslotModalListButton"
|
||||
:buttonTypeObject="timeslotModalListButton"
|
||||
:answers="availableTimeSlots"
|
||||
groupName="ChooseTimeSlot"
|
||||
textPosition="text-center"
|
||||
v-model="selectedTimeSlot"
|
||||
isRequired />
|
||||
isRequired
|
||||
validationRules="time-slot-required"
|
||||
class="mt-5"/>
|
||||
<div class="mt-1 mb-2" v-if="supplementalInformationBlock" v-html="supplementalInformationBlock"></div>
|
||||
<textBlock
|
||||
v-show="shouldShowDropoffDisclaimerText"
|
||||
:customText="dropoffDisclaimerText"
|
||||
justifyText="left"
|
||||
typeStyle="caption"
|
||||
class="mb-2" />
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
|
|
@ -29,54 +38,116 @@
|
|||
import modal from "@/digital-components/modal/modal";
|
||||
import textBlock from "@/digital-components/text-block/text-block";
|
||||
import buttonQuestion from "@/digital-components/button-question/button-question";
|
||||
import timeslotModalListButton from "./timeslot-modal-list-button/timeslot-modal-list-button";
|
||||
|
||||
import { daysOfWeek, monthsOfYear } from "@/constants/scheduling.js";
|
||||
import {defineRule, useField} from "vee-validate";
|
||||
import { errorMessages } from "@/constants/error-messages";
|
||||
import { required } from "@/helpers/validation-rules";
|
||||
|
||||
// Validation for the modal button
|
||||
defineRule("time-slot-required", required(errorMessages.OPTION_REQUIRED));
|
||||
|
||||
// Constants
|
||||
const AppointmentTypeStrings = {
|
||||
IN_SHOP: "Inshop",
|
||||
MOBILE: "Mobile",
|
||||
DROP_OFF: "Dropoff"
|
||||
};
|
||||
|
||||
export default {
|
||||
name: "timeSlotModalQuestion",
|
||||
props: {
|
||||
modelValue: Object,
|
||||
cmsWidgetName: String,
|
||||
mobileCmsWidgetName: String,
|
||||
earlyBirdCmsWidgetName: String,
|
||||
dropoffCmsWidgetName: String,
|
||||
appointmentType: String,
|
||||
dateAndTimeSlotData: Object,
|
||||
mobileEarlyBirdFee: Object,
|
||||
estimatedServiceMinutesMinimum: Number,
|
||||
estimatedServiceMinutesMaximum: Number,
|
||||
validationRules: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedTimeSlot: null,
|
||||
timeslotModalListButton: timeslotModalListButton,
|
||||
};
|
||||
},
|
||||
setup(props) {
|
||||
const { handleChange } =
|
||||
useField("time-slot-modal-question", props.validationRules);
|
||||
|
||||
return {
|
||||
handleChange
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
modelValue() {
|
||||
this.selectedTimeSlot = this.modelValue;
|
||||
this.handleChange(this.modelValue);
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
appointmentDurationTextFromCms() {
|
||||
return this.getCmsContent(this.cmsWidgetName, "SubheaderText");
|
||||
},
|
||||
supplementalInformationBlock() {
|
||||
let appointmentTypeCmsWidgetName;
|
||||
let cmsFieldName = "BodyText";
|
||||
if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) {
|
||||
return null;
|
||||
} else if (this.appointmentType === AppointmentTypeStrings.MOBILE) {
|
||||
appointmentTypeCmsWidgetName = this.selectedTimeSlot === this.earlyBirdButtonText ? this.earlyBirdCmsWidgetName : this.mobileCmsWidgetName;
|
||||
} else {
|
||||
appointmentTypeCmsWidgetName = this.dropoffCmsWidgetName;
|
||||
if (this.isSameDay) {
|
||||
cmsFieldName = "BodyText2";
|
||||
}
|
||||
}
|
||||
return this.getCmsContent(appointmentTypeCmsWidgetName, cmsFieldName);
|
||||
},
|
||||
footerCloseButtonText() {
|
||||
return this.getCmsContent(this.cmsWidgetName, "FooterText");
|
||||
},
|
||||
earlyBirdButtonText() {
|
||||
return this.getCmsContent(this.earlyBirdCmsWidgetName, "HeaderText");
|
||||
},
|
||||
dropoffButtonText() {
|
||||
return this.getCmsContent(this.dropoffCmsWidgetName, "HeaderText");
|
||||
},
|
||||
dropoffDisclaimerText() {
|
||||
return this.getCmsContent(this.dropoffCmsWidgetName, "FooterText");
|
||||
},
|
||||
shouldShowDropoffDisclaimerText() {
|
||||
return this.appointmentType === AppointmentTypeStrings.DROP_OFF && !this.isSameDay;
|
||||
},
|
||||
isSameDay() {
|
||||
return false;
|
||||
},
|
||||
dateSelectedReadableDate() {
|
||||
if (this.dateAndTimeSlotData === null) {
|
||||
return null;
|
||||
}
|
||||
// This conversion ensures we don't get get GMT induced date changes
|
||||
const dateObject = new Date (`${this.dateAndTimeSlotData.dateString}T00:00:00`);
|
||||
const dateObject = new Date(`${this.dateAndTimeSlotData.dateString}T00:00:00`);
|
||||
const weekdayName = daysOfWeek[dateObject.getDay()];
|
||||
const month = monthsOfYear[dateObject.getMonth()];
|
||||
const numberDayOfMonth = dateObject.getDate();
|
||||
// Ex. Tuesday, April 23
|
||||
return `${weekdayName}, ${month} ${numberDayOfMonth}`;
|
||||
},
|
||||
appointmentDurationTextWithTime() {
|
||||
inshopAppointmentDurationTextWithTime() {
|
||||
if (this.appointmentType === AppointmentTypeStrings.MOBILE) {
|
||||
return null;
|
||||
}
|
||||
let durationSubHeaderText = this.appointmentDurationTextFromCms + " ";
|
||||
if (this.estimatedServiceMinutesMaximum >= 120) {
|
||||
durationSubHeaderText += `${this.estimatedServiceMinutesMinimum / 60} - ${
|
||||
this.estimatedServiceMinutesMaximum / 60} hours`;
|
||||
if (this.appointmentType === AppointmentTypeStrings.DROP_OFF) {
|
||||
durationSubHeaderText += "All day";
|
||||
} else {
|
||||
durationSubHeaderText += `${this.estimatedServiceMinutesMinimum} - ${this.estimatedServiceMinutesMaximum} minutes`
|
||||
durationSubHeaderText += this.getDisplayTextForDurationLength(this.estimatedServiceMinutesMinimum, this.estimatedServiceMinutesMaximum);
|
||||
}
|
||||
return durationSubHeaderText;
|
||||
},
|
||||
|
|
@ -84,20 +155,33 @@ export default {
|
|||
if (this.dateAndTimeSlotData === null) {
|
||||
return null;
|
||||
}
|
||||
return this.dateAndTimeSlotData.timeSlots.map(timeslot => {
|
||||
let hours = timeslot.startTime.split(":")[0];
|
||||
let minutes = timeslot.startTime.split(":")[1];
|
||||
let meridianNotation = "AM";
|
||||
if (timeslot.startTime.split(":")[0] > 12) {
|
||||
hours -= 12;
|
||||
meridianNotation = "PM";
|
||||
let availableTimeSlots;
|
||||
if (this.appointmentType === AppointmentTypeStrings.DROP_OFF) {
|
||||
availableTimeSlots = [{value: "Dropoff", buttonLabel: this.dropoffButtonText}]
|
||||
} else {
|
||||
availableTimeSlots = this.dateAndTimeSlotData.timeSlots.map((timeslot) => {
|
||||
let readableTime;
|
||||
if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) {
|
||||
readableTime = this.getDisplayTextForMilitaryTime(timeslot.startTime);
|
||||
} else if (this.appointmentType === AppointmentTypeStrings.MOBILE) {
|
||||
readableTime = `${this.getDisplayTextForMilitaryTime(timeslot.startTime)} - ${this.getDisplayTextForMilitaryTime(timeslot.endTime)}`;
|
||||
}
|
||||
return {
|
||||
value: timeslot.id,
|
||||
buttonLabel: readableTime,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (this.appointmentType === AppointmentTypeStrings.MOBILE) {
|
||||
const offerPremium = this.dateAndTimeSlotData.timeSlots[0].offerPremium;
|
||||
const hasEarlyBird = this.mobileEarlyBirdFee?.partType === "EARLY BIRD";
|
||||
if (offerPremium && hasEarlyBird) {
|
||||
availableTimeSlots.unshift({value: this.dateAndTimeSlotData.timeSlots[0].id + "-earlybird", buttonLabel: this.earlyBirdButtonText, buttonLabelSubCopy: this.getTotalLineItemPrice(this.mobileEarlyBirdFee)});
|
||||
}
|
||||
let readableTime = `${hours}:${minutes} ${meridianNotation}`;
|
||||
return {
|
||||
value: timeslot.id,
|
||||
buttonLabel: readableTime,
|
||||
};
|
||||
});
|
||||
}
|
||||
return availableTimeSlots;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -113,6 +197,28 @@ export default {
|
|||
onModalClosed() {
|
||||
this.selectedTimeSlot = this.modelValue;
|
||||
},
|
||||
// Expected input: "HH:MM:SS"
|
||||
getDisplayTextForMilitaryTime(militaryTimeInput) {
|
||||
let hours = militaryTimeInput.split(":")[0];
|
||||
const minutes = militaryTimeInput.split(":")[1];
|
||||
let meridianNotation = "AM";
|
||||
if (militaryTimeInput.split(":")[0] > 12) {
|
||||
hours -= 12;
|
||||
meridianNotation = "PM";
|
||||
}
|
||||
return `${hours}:${minutes} ${meridianNotation}`;
|
||||
},
|
||||
getDisplayTextForDurationLength(durationMinimum, durationMaximum) {
|
||||
let displayTextForDurationLength;
|
||||
if (durationMaximum >= 120) {
|
||||
displayTextForDurationLength = `${durationMinimum / 60} - ${
|
||||
durationMaximum / 60
|
||||
} hours`;
|
||||
} else {
|
||||
displayTextForDurationLength = `${durationMinimum} - ${durationMaximum} minutes`;
|
||||
}
|
||||
return displayTextForDurationLength;
|
||||
}
|
||||
},
|
||||
components: {
|
||||
modal,
|
||||
|
|
@ -131,7 +237,7 @@ export default {
|
|||
padding-bottom: 0;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
.text-block {
|
||||
.text-block.duration-text-block {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,119 @@
|
|||
<template>
|
||||
<baseInputButton
|
||||
v-bind="$props"
|
||||
buttonWrapperClasses="list-group base-input-button list-button rounded-3 d-flex flex-column w-100 mb-2"
|
||||
v-model="selectedValue">
|
||||
<div
|
||||
:aria-label="buttonLabel"
|
||||
class="button-content list-button-content d-flex flex-column justify-content-center py-3 px-4">
|
||||
<span class="m-0" :class="textPosition">
|
||||
{{ buttonLabel }}
|
||||
</span>
|
||||
<span v-if="buttonLabelSubCopy" class="m-0 small" :class="textPosition">
|
||||
{{ formattedButtonLabelSubCopy }}
|
||||
</span>
|
||||
<span v-if="screenReaderOnlyText" class="sr-only">
|
||||
{{ screenReaderOnlyText }}
|
||||
</span>
|
||||
<loader
|
||||
v-if="isLoaderDisplayed && selectingInitiatesLoad"
|
||||
:class="[this.loaderColor, this.loaderPosition]" />
|
||||
</div>
|
||||
</baseInputButton>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import loader from "@/ux-components/loader/loader";
|
||||
import baseInputButton from "@/digital-components/base-input-button/base-input-button";
|
||||
import inputButtonWrapperMixin from "@/mixins/input-button-wrapper-mixin";
|
||||
|
||||
export default {
|
||||
name: "timeslotMOdalListButton",
|
||||
mixins: [inputButtonWrapperMixin],
|
||||
props: {
|
||||
loaderColor: String,
|
||||
loaderPosition: {
|
||||
type: String,
|
||||
default: "right",
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isLoaderDisplayed: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
formattedButtonLabelSubCopy() {
|
||||
return this.buttonLabelSubCopy?.toFixed(2);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
displayLoader() {
|
||||
this.isLoaderDisplayed = true;
|
||||
},
|
||||
preHandleAnswerChange() {
|
||||
if (this.selectingInitiatesLoad) {
|
||||
this.displayLoader();
|
||||
}
|
||||
},
|
||||
},
|
||||
components: {
|
||||
loader,
|
||||
baseInputButton,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.loader {
|
||||
position: absolute;
|
||||
}
|
||||
.list-button {
|
||||
outline: none;
|
||||
input[type="radio"],
|
||||
input[type="checkbox"] {
|
||||
position: static; //override bootstrap
|
||||
|
||||
&:focus-visible + .list-button-content {
|
||||
box-shadow: 0 0 0 2.5px $blue;
|
||||
}
|
||||
&:focus + .list-button-content {
|
||||
box-shadow: 0 0 0 2.5px $blue;
|
||||
}
|
||||
&:checked + .list-button-content {
|
||||
color: $black;
|
||||
font-weight: 500;
|
||||
background: $blue-100;
|
||||
box-shadow: 0 0 0 1px $blue;
|
||||
}
|
||||
&:checked:focus + .list-button-content {
|
||||
box-shadow: 0 0 0 2.5px $blue;
|
||||
}
|
||||
&:checked + .list-button-content p,
|
||||
&:checked + .list-button-content span {
|
||||
font-weight: 500;
|
||||
}
|
||||
&:checked + .list-button-content span:nth-child(2) {
|
||||
font-weight: 400;
|
||||
color: $gray-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
.list-button-content {
|
||||
color: $gray-600;
|
||||
position: relative;
|
||||
background: $white;
|
||||
transition: all 150ms linear;
|
||||
border-radius: $border-radius-lg;
|
||||
border: 1px solid $gray-500;
|
||||
width: 100%;
|
||||
outline: none;
|
||||
|
||||
span {
|
||||
&.small {
|
||||
font-size: 0.75rem;
|
||||
color: $gray-550;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -16,6 +16,7 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
setCmsContent(cmsContent) {
|
||||
console.log(cmsContent);
|
||||
this.$root.cmsContentByWidget = cmsContent;
|
||||
},
|
||||
getCmsContent(widgetName, fieldName) {
|
||||
|
|
|
|||
|
|
@ -1033,7 +1033,18 @@ export const actions = {
|
|||
// logApiCall: false,
|
||||
// });
|
||||
},
|
||||
|
||||
getMobileEarlyBirdFee(context) {
|
||||
const damageType = context.getters.damage.isRepair ? "Repair" : "Replace";
|
||||
const paymentType = context.getters.order.payment.isInsurance ? "Insurance" : "Cash";
|
||||
return globalMethods.callMockHttpClient({
|
||||
method: endpoints.GetMobileEarlyBirdFee.method,
|
||||
endpoint: `${endpoints.GetMobileEarlyBirdFee.mockUrl}/Cash/Replace`,
|
||||
});
|
||||
// return globalMethods.callHttpClient({
|
||||
// method: endpoints.GetMobileEarlyBirdFee.method,
|
||||
// endpoint: `${endpoints.GetMobileEarlyBirdFee.url}/${paymentType}/${damageType}`,
|
||||
// });
|
||||
},
|
||||
// Session API Actions
|
||||
saveSession(context) {
|
||||
const vehicle = context.getters.vehicle;
|
||||
|
|
|
|||
Loading…
Reference in a new issue