CSR-1137 | Initial creation of time-slot modal
This commit is contained in:
parent
8fe6155812
commit
491f6b273a
6 changed files with 170 additions and 5 deletions
|
|
@ -13,4 +13,6 @@ const monthsOfYear = [
|
||||||
"December",
|
"December",
|
||||||
];
|
];
|
||||||
|
|
||||||
export { monthsOfYear };
|
const daysOfWeek = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
||||||
|
|
||||||
|
export { monthsOfYear, daysOfWeek };
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,6 @@ export default {
|
||||||
isRequired: Boolean,
|
isRequired: Boolean,
|
||||||
isOverflowScrollable: Boolean,
|
isOverflowScrollable: Boolean,
|
||||||
isWide: Boolean,
|
isWide: Boolean,
|
||||||
isCashOrInsurance: Boolean,
|
|
||||||
modelValue: [Array, Number, String],
|
modelValue: [Array, Number, String],
|
||||||
value: [Number, String],
|
value: [Number, String],
|
||||||
validationRules: String,
|
validationRules: String,
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,13 @@ export default {
|
||||||
onModalClosedCallback: {
|
onModalClosedCallback: {
|
||||||
type: Function,
|
type: Function,
|
||||||
},
|
},
|
||||||
|
allowManualFooterDisableOverride: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
isFooterDisabledManualOverride: {
|
||||||
|
type: Boolean,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const modalId = `modal-${crypto.randomUUID()}`;
|
const modalId = `modal-${crypto.randomUUID()}`;
|
||||||
|
|
@ -98,10 +105,13 @@ export default {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isFooterButtonDisabled() {
|
isFooterButtonDisabled() {
|
||||||
if (!this.meta.touched) {
|
if (this.allowManualFooterDisableOverride !== null) {
|
||||||
|
return this.isFooterDisabledManualOverride;
|
||||||
|
} else if (!this.meta.touched) {
|
||||||
return !this.meta.valid;
|
return !this.meta.valid;
|
||||||
|
} else {
|
||||||
|
return !this.meta.dirty || !this.meta.valid;
|
||||||
}
|
}
|
||||||
return !this.meta.dirty || !this.meta.valid;
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="text-block w-100 mt-2"
|
class="text-block w-100 mt-2 d-flex"
|
||||||
:class="[justifyText, typeStyle, fontWeight]"
|
:class="[justifyText, typeStyle, fontWeight]"
|
||||||
v-html="this.TextBlockCopy"></div>
|
v-html="this.TextBlockCopy"></div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,14 @@
|
||||||
selectableDatesSetting="custom"
|
selectableDatesSetting="custom"
|
||||||
v-model="selectedDate"
|
v-model="selectedDate"
|
||||||
:customSelectableDatesCallback="getAvailableDates" />
|
:customSelectableDatesCallback="getAvailableDates" />
|
||||||
|
<time-slot-selection-modal
|
||||||
|
ref="timeSlotModal"
|
||||||
|
v-model="selectedTimeSlot"
|
||||||
|
:dateForTimeSlotPicker="dateForTimeSlotPicker"
|
||||||
|
:availableTimeSlots="availableTimeSlots"
|
||||||
|
estimatedServiceMinutesMinimum="90"
|
||||||
|
estimatedServiceMinutesMaximum="180" />
|
||||||
|
<button @click="openModal">Click for timeslots</button>
|
||||||
<funnel-footer
|
<funnel-footer
|
||||||
cmsWidgetName="FunnelFooterWidget"
|
cmsWidgetName="FunnelFooterWidget"
|
||||||
ref="funnelFooter"
|
ref="funnelFooter"
|
||||||
|
|
@ -41,6 +49,7 @@ import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-heade
|
||||||
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
|
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
|
||||||
import { Form, defineRule } from "vee-validate";
|
import { Form, defineRule } from "vee-validate";
|
||||||
import datePicker from "@/digital-components/date-picker/date-picker";
|
import datePicker from "@/digital-components/date-picker/date-picker";
|
||||||
|
import timeSlotSelectionModal from "./time-slot-selection-modal/time-slot-selection-modal";
|
||||||
|
|
||||||
// Supporting files
|
// Supporting files
|
||||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
|
|
@ -56,6 +65,7 @@ import {
|
||||||
} from "@/helpers/cms-content-helper";
|
} from "@/helpers/cms-content-helper";
|
||||||
import { errorMessages } from "@/constants/error-messages";
|
import { errorMessages } from "@/constants/error-messages";
|
||||||
import { required } from "@/helpers/validation-rules";
|
import { required } from "@/helpers/validation-rules";
|
||||||
|
import { daysOfWeek, monthsOfYear } from "@/constants/scheduling.js";
|
||||||
|
|
||||||
defineRule("date-required", required(errorMessages.DATE_REQUIRED));
|
defineRule("date-required", required(errorMessages.DATE_REQUIRED));
|
||||||
|
|
||||||
|
|
@ -64,6 +74,31 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
selectedDate: null,
|
selectedDate: null,
|
||||||
|
selectedTimeSlot: null,
|
||||||
|
dateForTimeSlotPicker: new Date("May 23 2023"),
|
||||||
|
availableTimeSlots: [
|
||||||
|
"8:00 AM",
|
||||||
|
"10:30 AM",
|
||||||
|
"11:00 AM",
|
||||||
|
"12:30 AM",
|
||||||
|
"1:00 PM",
|
||||||
|
"2:00 PM",
|
||||||
|
"4:30 PM",
|
||||||
|
"8:00 AM",
|
||||||
|
"10:30 AM",
|
||||||
|
"11:00 AM",
|
||||||
|
"12:30 AM",
|
||||||
|
"1:00 PM",
|
||||||
|
"2:00 PM",
|
||||||
|
"4:30 PM",
|
||||||
|
"8:00 AM",
|
||||||
|
"10:30 AM",
|
||||||
|
"11:00 AM",
|
||||||
|
"12:30 AM",
|
||||||
|
"1:00 PM",
|
||||||
|
"2:00 PM",
|
||||||
|
"4:30 PM",
|
||||||
|
],
|
||||||
mockSelectableDatesData: [
|
mockSelectableDatesData: [
|
||||||
{ dateString: "2023-04-28" },
|
{ dateString: "2023-04-28" },
|
||||||
|
|
||||||
|
|
@ -202,6 +237,12 @@ 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
|
// 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);
|
return this.splitCopyOnCMSPlaceHolder(this.ChangeShopLinkText);
|
||||||
},
|
},
|
||||||
|
DateSelectedReadableDate() {
|
||||||
|
const dayOfWeek = this.dateForTimeSlotPicker.getDay();
|
||||||
|
const month = this.dateForTimeSlotPicker.getMonth();
|
||||||
|
const dayOfMonth = this.dateForTimeSlotPicker.getDate();
|
||||||
|
return `${daysOfWeek[dayOfWeek]}, ${monthsOfYear[month]} ${dayOfMonth}`;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
doesCopyContainRouterLink,
|
doesCopyContainRouterLink,
|
||||||
|
|
@ -250,6 +291,9 @@ export default {
|
||||||
});
|
});
|
||||||
return responseData;
|
return responseData;
|
||||||
},
|
},
|
||||||
|
openModal() {
|
||||||
|
this.$refs["timeSlotModal"].openModal();
|
||||||
|
},
|
||||||
backButtonAction() {
|
backButtonAction() {
|
||||||
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
|
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||||
},
|
},
|
||||||
|
|
@ -264,6 +308,7 @@ export default {
|
||||||
Form,
|
Form,
|
||||||
loadingModal,
|
loadingModal,
|
||||||
datePicker,
|
datePicker,
|
||||||
|
timeSlotSelectionModal,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
<template>
|
||||||
|
<modal
|
||||||
|
ref="timeSlots"
|
||||||
|
class="time-slots-modal"
|
||||||
|
:headerText="dateSelectedReadableDate"
|
||||||
|
footerButtonText="Save time slot"
|
||||||
|
allowManualFooterDisableOverride
|
||||||
|
:isFooterDisabledManualOverride="currentlySelectedTimeSlot === null"
|
||||||
|
:onModalOpenedCallback="onModalOpened"
|
||||||
|
:onModalClosedCallback="onModalClosed"
|
||||||
|
@footer-button-event="closeModal">
|
||||||
|
<textBlock
|
||||||
|
cmsWidgetName="appointmentDuration"
|
||||||
|
:customText="durationSubHeaderText"
|
||||||
|
justifyText="center"
|
||||||
|
typeStyle="small"
|
||||||
|
class="mb-5" />
|
||||||
|
<buttonQuestion
|
||||||
|
class="radioQuestion"
|
||||||
|
:answers="availableTimeSlots"
|
||||||
|
groupName="ChooseTimeSlot"
|
||||||
|
textPosition="text-center"
|
||||||
|
v-model="currentlySelectedTimeSlot"
|
||||||
|
isRequired />
|
||||||
|
</modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Components
|
||||||
|
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 { daysOfWeek, monthsOfYear } from "@/constants/scheduling.js";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "timeSlotSelectionModal",
|
||||||
|
props: {
|
||||||
|
modelValue: Object,
|
||||||
|
dateForTimeSlotPicker: Date,
|
||||||
|
availableTimeSlots: Array,
|
||||||
|
estimatedServiceMinutesMinimum: Number,
|
||||||
|
estimatedServiceMinutesMaximum: Number,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentlySelectedTimeSlot: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue() {
|
||||||
|
this.currentlySelectedTimeSlot = this.modelValue;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
dateSelectedReadableDate() {
|
||||||
|
const weekdayName = daysOfWeek[this.dateForTimeSlotPicker.getDay()];
|
||||||
|
const month = monthsOfYear[this.dateForTimeSlotPicker.getMonth()];
|
||||||
|
const numberDayOfMonth = this.dateForTimeSlotPicker.getDate();
|
||||||
|
// Ex. Tuesday, April 23
|
||||||
|
return `${weekdayName}, ${month} ${numberDayOfMonth}`;
|
||||||
|
},
|
||||||
|
durationSubHeaderText() {
|
||||||
|
let durationSubHeaderText = "Duration: ";
|
||||||
|
if (this.estimatedServiceMinutesMaximum >= 120) {
|
||||||
|
durationSubHeaderText += `${this.estimatedServiceMinutesMinimum / 60} - ${
|
||||||
|
this.estimatedServiceMinutesMaximum / 60} hours`;
|
||||||
|
} else {
|
||||||
|
durationSubHeaderText += `${this.estimatedServiceMinutesMinimum} - ${this.estimatedServiceMinutesMaximum} minutes`
|
||||||
|
}
|
||||||
|
return durationSubHeaderText;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
openModal() {
|
||||||
|
this.$refs["timeSlots"].openModal();
|
||||||
|
},
|
||||||
|
// fires any time the footer button is used, is fired before "onModalClosed"
|
||||||
|
closeModal() {
|
||||||
|
this.$emit("update:modelValue", this.currentlySelectedTimeSlot);
|
||||||
|
this.$refs["timeSlots"].closeModal("test");
|
||||||
|
},
|
||||||
|
// fires any time the modal is closed, AFTER "closeModal" fires if footer button is used
|
||||||
|
onModalClosed() {
|
||||||
|
this.currentlySelectedTimeSlot = this.modelValue;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
modal,
|
||||||
|
textBlock,
|
||||||
|
buttonQuestion,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.time-slots-modal.modal.modal-component {
|
||||||
|
> .modal-dialog > .modal-content {
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
.modal-header {
|
||||||
|
padding-bottom: 0;
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
.text-block {
|
||||||
|
margin-top: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Reference in a new issue