Compare commits

3 Commits

Author SHA1 Message Date
27b15a37f7 Bump version
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 2m18s
2024-06-04 16:35:42 -04:00
947b463fe2 Minor UI tweaks 2024-06-03 23:04:32 -04:00
c3098b073f UI Enhancements 2024-06-03 12:01:38 -04:00
12 changed files with 154 additions and 99 deletions

View File

@@ -118,7 +118,6 @@ async function login(email: string, password: string) {
spinner: false, spinner: false,
icon: 'check_circle', icon: 'check_circle',
}); });
console.log('Redirecting to index page');
appRouter.replace({ name: 'index' }); appRouter.replace({ name: 'index' });
} catch (error: unknown) { } catch (error: unknown) {
if (error instanceof AppwriteException) { if (error instanceof AppwriteException) {

View File

@@ -5,7 +5,7 @@
class="col-lg-4 col-md-6 col-sm-8 col-xs-12"> class="col-lg-4 col-md-6 col-sm-8 col-xs-12">
<q-card-section> <q-card-section>
<div class="text-h5 q-mt-none q-mb-xs"> <div class="text-h5 q-mt-none q-mb-xs">
{{ reservation?.value ? 'Modify Booking' : 'New Booking' }} {{ reservation ? 'Modify Booking' : 'New Booking' }}
</div> </div>
<div class="text-caption text-grey-8">for: {{ bookingName }}</div> <div class="text-caption text-grey-8">for: {{ bookingName }}</div>
</q-card-section> </q-card-section>
@@ -98,11 +98,15 @@
</q-item> </q-item>
</q-list> </q-list>
<q-card-actions align="right"> <q-card-actions align="right">
<q-btn
label="Delete"
color="negative"
v-if="reservation?.$id"
@click="onDelete" />
<q-btn <q-btn
label="Reset" label="Reset"
@click="onReset" @click="onReset"
color="secondary" color="secondary" />
size="md" />
<q-btn <q-btn
label="Submit" label="Submit"
@click="onSubmit" @click="onSubmit"
@@ -112,7 +116,9 @@
<q-dialog <q-dialog
v-model="boatSelect" v-model="boatSelect"
full-width> full-width>
<BoatScheduleTableComponent v-model="bookingForm.interval" /> <BoatScheduleTableComponent
:model-value="bookingForm.interval"
@update:model-value="updateInterval" />
</q-dialog> </q-dialog>
</div> </div>
</template> </template>
@@ -163,6 +169,7 @@ watch(reservation, (newReservation) => {
} else { } else {
const updatedReservation = { const updatedReservation = {
...newReservation, ...newReservation,
user: auth.currentUser?.$id,
interval: { interval: {
start: newReservation.start, start: newReservation.start,
end: newReservation.end, end: newReservation.end,
@@ -173,6 +180,11 @@ watch(reservation, (newReservation) => {
} }
}); });
const updateInterval = (interval: Interval) => {
bookingForm.value.interval = interval;
boatSelect.value = false;
};
const bookingDuration = computed((): { hours: number; minutes: number } => { const bookingDuration = computed((): { hours: number; minutes: number } => {
if (bookingForm.value.interval?.start && bookingForm.value.interval?.end) { if (bookingForm.value.interval?.start && bookingForm.value.interval?.end) {
const start = new Date(bookingForm.value.interval.start).getTime(); const start = new Date(bookingForm.value.interval.start).getTime();
@@ -191,10 +203,13 @@ const bookingName = computed(() =>
const boat = computed((): Boat | null => { const boat = computed((): Boat | null => {
const boatId = bookingForm.value.interval?.resource; const boatId = bookingForm.value.interval?.resource;
console.log('Boat Lookup:', boatId);
return boatStore.getBoatById(boatId); return boatStore.getBoatById(boatId);
}); });
const onDelete = () => {
reservationStore.deleteReservation(reservation.value?.id);
};
const onReset = () => { const onReset = () => {
bookingForm.value.interval = null; bookingForm.value.interval = null;
bookingForm.value = reservation.value bookingForm.value = reservation.value

View File

@@ -12,21 +12,20 @@
max-width: 350px; max-width: 350px;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
" ">
>
<span <span
class="q-button" class="q-button"
style="cursor: pointer; user-select: none" style="cursor: pointer; user-select: none"
@click="onPrev" @click="onPrev">
>&lt;</span &lt;
> </span>
{{ formattedMonth }} {{ formattedMonth }}
<span <span
class="q-button" class="q-button"
style="cursor: pointer; user-select: none" style="cursor: pointer; user-select: none"
@click="onNext" @click="onNext">
>&gt;</span &gt;
> </span>
</div> </div>
</div> </div>
<div <div
@@ -35,8 +34,7 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
flex-wrap: nowrap; flex-wrap: nowrap;
" ">
>
<div style="display: flex; width: 100%"> <div style="display: flex; width: 100%">
<q-calendar-month <q-calendar-month
ref="calendar" ref="calendar"
@@ -48,10 +46,10 @@
date-type="rounded" date-type="rounded"
@change="onChange" @change="onChange"
@moved="onMoved" @moved="onMoved"
@click-date="onClickDate" @click-date="onClickDate" />
/> </div>
</div></div </div>
></q-card-section> </q-card-section>
<q-calendar-resource <q-calendar-resource
v-model="selectedDate" v-model="selectedDate"
:model-resources="boatStore.boats" :model-resources="boatStore.boats"
@@ -73,18 +71,25 @@
@click-time="onClickTime" @click-time="onClickTime"
@click-resource="onClickResource" @click-resource="onClickResource"
@click-head-resources="onClickHeadResources" @click-head-resources="onClickHeadResources"
@click-interval="onClickInterval" @click-interval="onClickInterval">
>
<template #resource-intervals="{ scope }"> <template #resource-intervals="{ scope }">
<template v-for="(event, index) in getEvents(scope)" :key="index"> <template
<q-badge outline :label="event.title" :style="getStyle(event)" /> v-for="(event, index) in getEvents(scope)"
:key="index">
<q-badge
outline
:label="event.title"
:style="getStyle(event)" />
</template> </template>
</template> </template>
<template #resource-label="{ scope: { resource } }"> <template #resource-label="{ scope: { resource } }">
<div class="col-12 .col-md-auto"> <div class="col-12 .col-md-auto">
{{ resource.displayName }} {{ resource.displayName }}
<q-icon v-if="resource.defects" name="warning" color="warning" /> <q-icon
v-if="resource.defects"
name="warning"
color="warning" />
</div> </div>
</template> </template>
</q-calendar-resource> </q-calendar-resource>
@@ -97,9 +102,10 @@
dense dense
@update:model-value="onUpdateDuration" @update:model-value="onUpdateDuration"
label="Duration (hours)" label="Duration (hours)"
stack-label stack-label>
><template v-slot:append><q-icon name="timelapse" /></template></q-select <template v-slot:append><q-icon name="timelapse" /></template>
></q-card-section> </q-select>
</q-card-section>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { ref } from 'vue';
@@ -117,6 +123,8 @@ import { useReservationStore } from 'src/stores/reservation';
import { date } from 'quasar'; import { date } from 'quasar';
import { computed } from 'vue'; import { computed } from 'vue';
import type { StatusTypes } from 'src/stores/schedule.types'; import type { StatusTypes } from 'src/stores/schedule.types';
import { useIntervalStore } from 'src/stores/interval';
import { storeToRefs } from 'pinia';
interface EventData { interface EventData {
event: object; event: object;
@@ -146,7 +154,7 @@ const statusLookup = {
const calendar = ref(); const calendar = ref();
const boatStore = useBoatStore(); const boatStore = useBoatStore();
const reservationStore = useReservationStore(); const reservationStore = useReservationStore();
const selectedDate = ref(today()); const { selectedDate } = storeToRefs(useIntervalStore());
const duration = ref(1); const duration = ref(1);
const formattedMonth = computed(() => { const formattedMonth = computed(() => {

View File

@@ -7,16 +7,16 @@
round round
icon="menu" icon="menu"
aria-label="Menu" aria-label="Menu"
@click="toggleLeftDrawer" @click="toggleLeftDrawer" />
/>
<q-toolbar-title> {{ pageTitle }} </q-toolbar-title> <q-toolbar-title>{{ pageTitle }}</q-toolbar-title>
<q-tabs shrink> <q-space />
<q-tab> </q-tab> <div>v2024.6.4.1</div>
</q-tabs>
</q-toolbar> </q-toolbar>
</q-header> </q-header>
<LeftDrawer :drawer="leftDrawerOpen" @drawer-toggle="toggleLeftDrawer" /> <LeftDrawer
:drawer="leftDrawerOpen"
@drawer-toggle="toggleLeftDrawer" />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">

View File

@@ -58,7 +58,7 @@
<q-btn <q-btn
flat flat
@click="cancelReservation()"> @click="cancelReservation()">
Cancel Delete
</q-btn> </q-btn>
</q-card-actions> </q-card-actions>
</q-card> </q-card>

View File

@@ -42,6 +42,7 @@
:key="block.$id"> :key="block.$id">
<div <div
class="timeblock" class="timeblock"
:disabled="beforeNow(new Date(block.end))"
:class="selectedBlock?.$id === block.$id ? 'selected' : ''" :class="selectedBlock?.$id === block.$id ? 'selected' : ''"
:style=" :style="
blockStyles( blockStyles(
@@ -51,8 +52,7 @@
) )
" "
:id="block.id" :id="block.id"
@click="selectBlock($event, scope, block)" @click="selectBlock($event, scope, block)">
v-close-popup>
{{ boats[scope.columnIndex].name }} {{ boats[scope.columnIndex].name }}
<br /> <br />
{{ {{
@@ -96,7 +96,7 @@ import {
} from '@quasar/quasar-ui-qcalendar'; } from '@quasar/quasar-ui-qcalendar';
import CalendarHeaderComponent from './CalendarHeaderComponent.vue'; import CalendarHeaderComponent from './CalendarHeaderComponent.vue';
import { ref, computed, onMounted } from 'vue'; import { ref, computed, onMounted, onUnmounted } from 'vue';
import { useBoatStore } from 'src/stores/boat'; import { useBoatStore } from 'src/stores/boat';
import { useAuthStore } from 'src/stores/auth'; import { useAuthStore } from 'src/stores/auth';
import { Interval, Reservation } from 'src/stores/schedule.types'; import { Interval, Reservation } from 'src/stores/schedule.types';
@@ -112,12 +112,19 @@ const selectedBlock = defineModel<Interval | null>();
const selectedDate = ref(today()); const selectedDate = ref(today());
const { getAvailableIntervals } = useIntervalStore(); const { getAvailableIntervals } = useIntervalStore();
const calendar = ref<QCalendarDay | null>(null); const calendar = ref<QCalendarDay | null>(null);
const now = ref(new Date());
let intervalId: string | number | NodeJS.Timeout | undefined;
onMounted(async () => { onMounted(async () => {
await useBoatStore().fetchBoats(); await useBoatStore().fetchBoats();
await intervalTemplateStore.fetchIntervalTemplates(); await intervalTemplateStore.fetchIntervalTemplates();
intervalId = setInterval(function () {
now.value = new Date();
}, 60000);
}); });
onUnmounted(() => clearInterval(intervalId));
function handleSwipe({ ...event }) { function handleSwipe({ ...event }) {
event.direction === 'right' ? calendar.value?.prev() : calendar.value?.next(); event.direction === 'right' ? calendar.value?.prev() : calendar.value?.next();
} }
@@ -157,6 +164,10 @@ function getBoatDisplayName(scope: DayBodyScope) {
: ''; : '';
} }
function beforeNow(time: Date) {
return time < now.value || null;
}
function genericBlockStyle( function genericBlockStyle(
start: Timestamp, start: Timestamp,
end: Timestamp, end: Timestamp,
@@ -177,9 +188,6 @@ function genericBlockStyle(
1 + 1 +
'px'; 'px';
} }
// if (selectedBlock.value?.id === block.id) {
// s.opacity = '1.0';
// }
return s; return s;
} }
@@ -191,11 +199,9 @@ interface DayBodyScope {
} }
function selectBlock(event: MouseEvent, scope: DayBodyScope, block: Interval) { function selectBlock(event: MouseEvent, scope: DayBodyScope, block: Interval) {
// TODO: Disable blocks before today with updateDisabled and/or comparison if (scope.timestamp.disabled || new Date(block.end) < new Date())
if (scope.timestamp.disabled) return false; return false;
selectedBlock.value === block selectedBlock.value = block;
? (selectedBlock.value = null)
: (selectedBlock.value = block);
} }
const boatReservations = computed((): Record<string, Reservation[]> => { const boatReservations = computed((): Record<string, Reservation[]> => {

View File

@@ -4,8 +4,24 @@
<script setup lang="ts"> <script setup lang="ts">
import BoatReservationComponent from 'src/components/BoatReservationComponent.vue'; import BoatReservationComponent from 'src/components/BoatReservationComponent.vue';
import { Reservation } from 'src/stores/schedule.types'; import { useIntervalStore } from 'src/stores/interval';
import { Interval, Reservation } from 'src/stores/schedule.types';
import { ref } from 'vue'; import { ref } from 'vue';
import { useRoute } from 'vue-router';
const $route = useRoute();
const newReservation = ref<Reservation>(); const newReservation = ref<Reservation>();
if (typeof $route.query.interval === 'string') {
useIntervalStore()
.fetchInterval($route.query.interval)
.then(
(interval: Interval) =>
(newReservation.value = <Reservation>{
resource: interval.resource,
start: interval.start,
end: interval.end,
})
);
}
</script> </script>

View File

@@ -1,10 +1,12 @@
<template> <template>
<q-page> <q-page>
<q-card class="row"> <div class="col">
<navigation-bar <navigation-bar
@today="onToday" @today="onToday"
@prev="onPrev" @prev="onPrev"
@next="onNext" /> @next="onNext" />
</div>
<div class="col q-ma-sm">
<q-calendar-scheduler <q-calendar-scheduler
ref="calendar" ref="calendar"
v-model="selectedDate" v-model="selectedDate"
@@ -16,14 +18,8 @@
v-touch-swipe.mouse.left.right="handleSwipe" v-touch-swipe.mouse.left.right="handleSwipe"
:max-days="$q.screen.lt.sm ? 3 : 7" :max-days="$q.screen.lt.sm ? 3 : 7"
animated animated
style="--calendar-resources-width: 2em" bordered
day-min-height="50px" style="--calendar-resources-width: 40px">
@change="onChange"
@moved="onMoved"
@click-date="onClickDate"
@click-time="onClickTime"
@click-interval="onClickInterval"
@click-head-day="onClickHeadDay">
<template #day="{ scope }"> <template #day="{ scope }">
<div <div
v-for="interval in getSortedIntervals( v-for="interval in getSortedIntervals(
@@ -31,12 +27,15 @@
scope.resource scope.resource
)" )"
:key="interval.$id" :key="interval.$id"
class="row q-pb-xs"> class="q-pb-xs row"
@click="createReservationFromInterval(interval)">
<q-badge <q-badge
multi-line multi-line
class="col-12" :class="!interval.user ? 'cursor-pointer' : null"
class="col-12 q-pa-sm"
:transparent="interval.user != undefined" :transparent="interval.user != undefined"
:color="interval.user ? 'secondary' : 'primary'" :color="interval.user ? 'secondary' : 'primary'"
:outline="!interval.user"
:id="interval.id"> :id="interval.id">
{{ {{
interval.user interval.user
@@ -44,12 +43,14 @@
: 'Available' : 'Available'
}} }}
<br /> <br />
{{ formatTime(interval.start) }} - {{ formatTime(interval.end) }} {{ formatTime(interval.start) }} to
<br />
{{ formatTime(interval.end) }}
</q-badge> </q-badge>
</div> </div>
</template> </template>
</q-calendar-scheduler> </q-calendar-scheduler>
</q-card> </div>
</q-page> </q-page>
</template> </template>
@@ -59,7 +60,7 @@ import { ref } from 'vue';
import { useAuthStore } from 'src/stores/auth'; import { useAuthStore } from 'src/stores/auth';
const reservationStore = useReservationStore(); const reservationStore = useReservationStore();
import { getDate, today } from '@quasar/quasar-ui-qcalendar'; import { getDate } from '@quasar/quasar-ui-qcalendar';
import { QCalendarScheduler } from '@quasar/quasar-ui-qcalendar'; import { QCalendarScheduler } from '@quasar/quasar-ui-qcalendar';
import { Timestamp } from '@quasar/quasar-ui-qcalendar'; import { Timestamp } from '@quasar/quasar-ui-qcalendar';
import { Boat, useBoatStore } from 'src/stores/boat'; import { Boat, useBoatStore } from 'src/stores/boat';
@@ -67,13 +68,17 @@ import NavigationBar from 'src/components/scheduling/NavigationBar.vue';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { formatTime } from 'src/utils/schedule'; import { formatTime } from 'src/utils/schedule';
import { useIntervalStore } from 'src/stores/interval'; import { useIntervalStore } from 'src/stores/interval';
import { Interval } from 'src/stores/schedule.types'; import { Interval, Reservation } from 'src/stores/schedule.types';
import { useRouter } from 'vue-router';
import { storeToRefs } from 'pinia';
const selectedDate = ref(today());
const boatStore = useBoatStore(); const boatStore = useBoatStore();
const calendar = ref(); const calendar = ref();
const $q = useQuasar(); const $q = useQuasar();
const $router = useRouter();
const { getAvailableIntervals } = useIntervalStore(); const { getAvailableIntervals } = useIntervalStore();
const { selectedDate } = storeToRefs(useIntervalStore());
const currentUser = useAuthStore().currentUser;
// interface DayScope { // interface DayScope {
// timestamp: Timestamp; // timestamp: Timestamp;
@@ -111,6 +116,21 @@ const getSortedIntervals = (timestamp: Timestamp, boat?: Boat): Interval[] => {
// return s; // return s;
// } // }
const createReservationFromInterval = (interval: Interval | Reservation) => {
if (interval.user) {
if (interval.user === currentUser?.$id) {
$router.push({ name: 'edit-reservation', params: { id: interval.$id } });
} else {
return false;
}
} else {
$router.push({
name: 'reserve-boat',
query: { interval: interval.$id },
});
}
};
function handleSwipe({ ...event }) { function handleSwipe({ ...event }) {
event.direction === 'right' ? calendar.value?.prev() : calendar.value?.next(); event.direction === 'right' ? calendar.value?.prev() : calendar.value?.next();
} }
@@ -124,24 +144,6 @@ function boatReservationEvents(
(resource as Boat).$id (resource as Boat).$id
); );
} }
function onMoved(data: Event) {
console.log('onMoved', data);
}
function onChange(data: Event) {
console.log('onChange', data);
}
function onClickDate(data: Event) {
console.log('onClickDate', data);
}
function onClickTime(data: Event) {
console.log('onClickTime', data);
}
function onClickInterval(data: Event) {
console.log('onClickInterval', data);
}
function onClickHeadDay(data: Event) {
console.log('onClickHeadDay', data);
}
function onToday() { function onToday() {
calendar.value.moveToToday(); calendar.value.moveToToday();
} }
@@ -152,3 +154,10 @@ function onNext() {
calendar.value.next(); calendar.value.next();
} }
</script> </script>
<style lang="sass">
.q-calendar-scheduler__resource
background-color: $primary
color: white
font-weight: bold
</style>

View File

@@ -256,11 +256,7 @@ function deleteBlock(block: Interval) {
function onDragEnter(e: DragEvent, type: string) { function onDragEnter(e: DragEvent, type: string) {
if (type === 'day' || type === 'head-day') { if (type === 'day' || type === 'head-day') {
e.preventDefault(); e.preventDefault();
if ( if (e.target instanceof HTMLDivElement)
e.target instanceof HTMLDivElement &&
(e.target.classList.contains('q-calendar-scheduler__head--day') ||
e.target.classList.contains('q-calendar-scheduler__day'))
)
e.target.classList.add('bg-secondary'); e.target.classList.add('bg-secondary');
} }
} }
@@ -274,11 +270,7 @@ function onDragOver(e: DragEvent, type: string) {
function onDragLeave(e: DragEvent, type: string) { function onDragLeave(e: DragEvent, type: string) {
if (type === 'day' || type === 'head-day') { if (type === 'day' || type === 'head-day') {
e.preventDefault(); e.preventDefault();
if ( if (e.target instanceof HTMLDivElement)
e.target instanceof HTMLDivElement &&
(e.target.classList.contains('q-calendar-scheduler__head--day') ||
e.target.classList.contains('q-calendar-scheduler__day'))
)
e.target.classList.remove('bg-secondary'); e.target.classList.remove('bg-secondary');
} }
} }

View File

@@ -1,7 +1,9 @@
<template> <template>
<ToolbarComponent pageTitle="Tasks" /> <ToolbarComponent pageTitle="Tasks" />
<q-page padding> <q-page padding>
<div class="q-pa-md" style="max-width: 400px"> <div
class="q-pa-md"
style="max-width: 400px">
<TaskEditComponent :taskId="taskId" /> <TaskEditComponent :taskId="taskId" />
</div> </div>
</q-page> </q-page>
@@ -9,7 +11,6 @@
<script setup lang="ts"> <script setup lang="ts">
const taskId = useRoute().params.id as string; const taskId = useRoute().params.id as string;
console.log(taskId);
import ToolbarComponent from 'src/components/ToolbarComponent.vue'; import ToolbarComponent from 'src/components/ToolbarComponent.vue';
import TaskEditComponent from 'src/components/task/TaskEditComponent.vue'; import TaskEditComponent from 'src/components/task/TaskEditComponent.vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';

View File

@@ -54,7 +54,7 @@ export const useAuthStore = defineStore('auth', () => {
if (!id) return 'No User'; if (!id) return 'No User';
try { try {
if (!userNames.value[id]) { if (!userNames.value[id]) {
userNames.value[id] = ''; userNames.value[id] = 'Loading...';
functions functions
.createExecution( .createExecution(
'userinfo', 'userinfo',

View File

@@ -1,7 +1,7 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
import { Boat } from './boat'; import { Boat } from './boat';
import { Timestamp } from '@quasar/quasar-ui-qcalendar'; import { Timestamp, today } from '@quasar/quasar-ui-qcalendar';
import { Interval, IntervalRecord } from './schedule.types'; import { Interval, IntervalRecord } from './schedule.types';
import { AppwriteIds, databases } from 'src/boot/appwrite'; import { AppwriteIds, databases } from 'src/boot/appwrite';
@@ -13,6 +13,7 @@ export const useIntervalStore = defineStore('interval', () => {
const intervals = ref<Map<string, Interval>>(new Map()); const intervals = ref<Map<string, Interval>>(new Map());
const intervalDates = ref<IntervalRecord>({}); const intervalDates = ref<IntervalRecord>({});
const reservationStore = useReservationStore(); const reservationStore = useReservationStore();
const selectedDate = ref<string>(today());
const getIntervals = (date: Timestamp | string, boat?: Boat): Interval[] => { const getIntervals = (date: Timestamp | string, boat?: Boat): Interval[] => {
const searchDate = typeof date === 'string' ? date : date.date; const searchDate = typeof date === 'string' ? date : date.date;
@@ -39,8 +40,6 @@ export const useIntervalStore = defineStore('interval', () => {
boat?: Boat boat?: Boat
): Interval[] => { ): Interval[] => {
return computed(() => { return computed(() => {
console.log(boat);
console.log(getIntervals(date, boat));
return getIntervals(date, boat).filter((interval) => { return getIntervals(date, boat).filter((interval) => {
return !reservationStore.isResourceTimeOverlapped( return !reservationStore.isResourceTimeOverlapped(
interval.resource, interval.resource,
@@ -51,6 +50,14 @@ export const useIntervalStore = defineStore('interval', () => {
}).value; }).value;
}; };
async function fetchInterval(id: string): Promise<Interval> {
return (await databases.getDocument(
AppwriteIds.databaseId,
AppwriteIds.collection.interval,
id
)) as Interval;
}
async function fetchIntervals(dateString: string) { async function fetchIntervals(dateString: string) {
try { try {
const response = await databases.listDocuments( const response = await databases.listDocuments(
@@ -128,8 +135,10 @@ export const useIntervalStore = defineStore('interval', () => {
getIntervals, getIntervals,
getAvailableIntervals, getAvailableIntervals,
fetchIntervals, fetchIntervals,
fetchInterval,
createInterval, createInterval,
updateInterval, updateInterval,
deleteInterval, deleteInterval,
selectedDate,
}; };
}); });