Compare commits
6 Commits
bbb544c029
...
a11b2a0568
| Author | SHA1 | Date | |
|---|---|---|---|
|
a11b2a0568
|
|||
|
ff8e54449a
|
|||
|
64a59e856f
|
|||
|
5e8c5a1631
|
|||
|
e97949cab3
|
|||
|
b7a3608e67
|
@@ -108,12 +108,19 @@ module.exports = configure(function ({ dev }) {
|
|||||||
secure: false,
|
secure: false,
|
||||||
rewrite: (path) => path.replace(/^\/api/, ''),
|
rewrite: (path) => path.replace(/^\/api/, ''),
|
||||||
},
|
},
|
||||||
'/function': {
|
'/api/v1/realtime': {
|
||||||
target: 'https://6640382951eacb568371.f.appwrite.toal.ca/',
|
target: 'wss://apidev.bab.toal.ca',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
|
rewrite: (path) => path.replace(/^\/api/, ''),
|
||||||
secure: false,
|
secure: false,
|
||||||
rewrite: (path) => path.replace(/^\/function/, ''),
|
ws: true,
|
||||||
},
|
},
|
||||||
|
// '/function': {
|
||||||
|
// target: 'https://6640382951eacb568371.f.appwrite.toal.ca/',
|
||||||
|
// changeOrigin: true,
|
||||||
|
// secure: false,
|
||||||
|
// rewrite: (path) => path.replace(/^\/function/, ''),
|
||||||
|
// },
|
||||||
},
|
},
|
||||||
// For reverse-proxying via haproxy
|
// For reverse-proxying via haproxy
|
||||||
// hmr: {
|
// hmr: {
|
||||||
|
|||||||
@@ -43,7 +43,11 @@ type AppwriteIDConfig = {
|
|||||||
|
|
||||||
let AppwriteIds = <AppwriteIDConfig>{};
|
let AppwriteIds = <AppwriteIDConfig>{};
|
||||||
|
|
||||||
if (API_ENDPOINT === 'https://apidev.bab.toal.ca/v1') {
|
console.log(API_ENDPOINT);
|
||||||
|
if (
|
||||||
|
API_ENDPOINT === 'https://apidev.bab.toal.ca/v1' ||
|
||||||
|
API_ENDPOINT === 'http://localhost:4000/api/v1'
|
||||||
|
) {
|
||||||
AppwriteIds = {
|
AppwriteIds = {
|
||||||
databaseId: '65ee1cbf9c2493faf15f',
|
databaseId: '65ee1cbf9c2493faf15f',
|
||||||
collection: {
|
collection: {
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ function getEvents(scope: ResourceIntervalScope) {
|
|||||||
scope.resource.$id
|
scope.resource.$id
|
||||||
);
|
);
|
||||||
|
|
||||||
return resourceEvents.map((event) => {
|
return resourceEvents.value.map((event) => {
|
||||||
return {
|
return {
|
||||||
left: scope.timeStartPosX(parsed(event.start)),
|
left: scope.timeStartPosX(parsed(event.start)),
|
||||||
width: scope.timeDurationWidth(
|
width: scope.timeDurationWidth(
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
v-for="block in getAvailableIntervals(
|
v-for="block in getAvailableIntervals(
|
||||||
scope.timestamp,
|
scope.timestamp,
|
||||||
boats[scope.columnIndex]
|
boats[scope.columnIndex]
|
||||||
)"
|
).value"
|
||||||
:key="block.$id">
|
:key="block.$id">
|
||||||
<div
|
<div
|
||||||
class="timeblock"
|
class="timeblock"
|
||||||
@@ -207,7 +207,7 @@ function selectBlock(event: MouseEvent, scope: DayBodyScope, block: Interval) {
|
|||||||
const boatReservations = computed((): Record<string, Reservation[]> => {
|
const boatReservations = computed((): Record<string, Reservation[]> => {
|
||||||
return reservationStore
|
return reservationStore
|
||||||
.getReservationsByDate(selectedDate.value)
|
.getReservationsByDate(selectedDate.value)
|
||||||
.reduce((result, reservation) => {
|
.value.reduce((result, reservation) => {
|
||||||
if (!result[reservation.resource]) result[reservation.resource] = [];
|
if (!result[reservation.resource]) result[reservation.resource] = [];
|
||||||
result[reservation.resource].push(reservation);
|
result[reservation.resource].push(reservation);
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ const currentUser = useAuthStore().currentUser;
|
|||||||
|
|
||||||
const getSortedIntervals = (timestamp: Timestamp, boat?: Boat): Interval[] => {
|
const getSortedIntervals = (timestamp: Timestamp, boat?: Boat): Interval[] => {
|
||||||
return getAvailableIntervals(timestamp, boat)
|
return getAvailableIntervals(timestamp, boat)
|
||||||
.concat(boatReservationEvents(timestamp, boat))
|
.value.concat(boatReservationEvents(timestamp, boat))
|
||||||
.sort((a, b) => Date.parse(a.start) - Date.parse(b.start));
|
.sort((a, b) => Date.parse(a.start) - Date.parse(b.start));
|
||||||
};
|
};
|
||||||
// Method declarations
|
// Method declarations
|
||||||
@@ -134,16 +134,16 @@ const createReservationFromInterval = (interval: Interval | Reservation) => {
|
|||||||
function handleSwipe({ ...event }) {
|
function handleSwipe({ ...event }) {
|
||||||
event.direction === 'right' ? calendar.value?.prev() : calendar.value?.next();
|
event.direction === 'right' ? calendar.value?.prev() : calendar.value?.next();
|
||||||
}
|
}
|
||||||
function boatReservationEvents(
|
const boatReservationEvents = (
|
||||||
timestamp: Timestamp,
|
timestamp: Timestamp,
|
||||||
resource: Boat | undefined
|
resource: Boat | undefined
|
||||||
) {
|
): Reservation[] => {
|
||||||
if (!resource) return [];
|
if (!resource) return [] as Reservation[];
|
||||||
return reservationStore.getReservationsByDate(
|
return reservationStore.getReservationsByDate(
|
||||||
getDate(timestamp),
|
getDate(timestamp),
|
||||||
(resource as Boat).$id
|
(resource as Boat).$id
|
||||||
);
|
).value;
|
||||||
}
|
};
|
||||||
function onToday() {
|
function onToday() {
|
||||||
calendar.value.moveToToday();
|
calendar.value.moveToToday();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
class="q-pa-none">
|
class="q-pa-none">
|
||||||
<q-card
|
<q-card
|
||||||
clas="q-ma-md"
|
clas="q-ma-md"
|
||||||
v-if="!futureUserReservations.length">
|
v-if="!reservationStore.futureUserReservations.length">
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<div class="text-h6">You don't have any upcoming bookings!</div>
|
<div class="text-h6">You don't have any upcoming bookings!</div>
|
||||||
<div class="text-h8">Why don't you go make one?</div>
|
<div class="text-h8">Why don't you go make one?</div>
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
</q-card>
|
</q-card>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<div
|
<div
|
||||||
v-for="reservation in futureUserReservations"
|
v-for="reservation in reservationStore.futureUserReservations"
|
||||||
:key="reservation.$id">
|
:key="reservation.$id">
|
||||||
<ReservationCardComponent :modelValue="reservation" />
|
<ReservationCardComponent :modelValue="reservation" />
|
||||||
</div>
|
</div>
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
name="past"
|
name="past"
|
||||||
class="q-pa-none">
|
class="q-pa-none">
|
||||||
<div
|
<div
|
||||||
v-for="reservation in pastUserReservations"
|
v-for="reservation in reservationStore.pastUserReservations"
|
||||||
:key="reservation.$id">
|
:key="reservation.$id">
|
||||||
<ReservationCardComponent :modelValue="reservation" />
|
<ReservationCardComponent :modelValue="reservation" />
|
||||||
</div>
|
</div>
|
||||||
@@ -63,7 +63,7 @@ import { useReservationStore } from 'src/stores/reservation';
|
|||||||
import ReservationCardComponent from 'src/components/scheduling/ReservationCardComponent.vue';
|
import ReservationCardComponent from 'src/components/scheduling/ReservationCardComponent.vue';
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
|
|
||||||
const { futureUserReservations, pastUserReservations } = useReservationStore();
|
const reservationStore = useReservationStore();
|
||||||
|
|
||||||
onMounted(() => useReservationStore().fetchUserReservations());
|
onMounted(() => useReservationStore().fetchUserReservations());
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,9 @@
|
|||||||
cell-width="150px">
|
cell-width="150px">
|
||||||
<template #day="{ scope }">
|
<template #day="{ scope }">
|
||||||
<div
|
<div
|
||||||
v-if="filteredIntervals(scope.timestamp, scope.resource).length"
|
v-if="
|
||||||
|
filteredIntervals(scope.timestamp, scope.resource).value.length
|
||||||
|
"
|
||||||
style="
|
style="
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -35,10 +37,8 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
">
|
">
|
||||||
<template
|
<template
|
||||||
v-for="block in sortedIntervals(
|
v-for="block in sortedIntervals(scope.timestamp, scope.resource)
|
||||||
scope.timestamp,
|
.value"
|
||||||
scope.resource
|
|
||||||
)"
|
|
||||||
:key="block.id">
|
:key="block.id">
|
||||||
<q-chip class="cursor-pointer">
|
<q-chip class="cursor-pointer">
|
||||||
{{ date.formatDate(block.start, 'HH:mm') }} -
|
{{ date.formatDate(block.start, 'HH:mm') }} -
|
||||||
@@ -163,7 +163,7 @@ import {
|
|||||||
} from '@quasar/quasar-ui-qcalendar';
|
} from '@quasar/quasar-ui-qcalendar';
|
||||||
import { Boat, useBoatStore } from 'src/stores/boat';
|
import { Boat, useBoatStore } from 'src/stores/boat';
|
||||||
import { useIntervalStore } from 'src/stores/interval';
|
import { useIntervalStore } from 'src/stores/interval';
|
||||||
import { onMounted, ref } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
import type {
|
import type {
|
||||||
Interval,
|
Interval,
|
||||||
IntervalTemplate,
|
IntervalTemplate,
|
||||||
@@ -208,8 +208,10 @@ const filteredIntervals = (date: Timestamp, boat: Boat) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const sortedIntervals = (date: Timestamp, boat: Boat) => {
|
const sortedIntervals = (date: Timestamp, boat: Boat) => {
|
||||||
return filteredIntervals(date, boat).sort(
|
return computed(() =>
|
||||||
(a, b) => Date.parse(a.start) - Date.parse(b.start)
|
filteredIntervals(date, boat).value.sort(
|
||||||
|
(a, b) => Date.parse(a.start) - Date.parse(b.start)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -293,7 +295,7 @@ function onDrop(
|
|||||||
overlapped.value = boatsToApply
|
overlapped.value = boatsToApply
|
||||||
.map((boat) =>
|
.map((boat) =>
|
||||||
intervalsOverlapped(
|
intervalsOverlapped(
|
||||||
existingIntervals.concat(
|
existingIntervals.value.concat(
|
||||||
intervalsFromTemplate(boat, templateId, date)
|
intervalsFromTemplate(boat, templateId, date)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,25 +2,44 @@ import { defineStore } from 'pinia';
|
|||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { Boat } from './boat';
|
import { Boat } from './boat';
|
||||||
import { Timestamp, today } from '@quasar/quasar-ui-qcalendar';
|
import { Timestamp, today } from '@quasar/quasar-ui-qcalendar';
|
||||||
|
import { Interval } from './schedule.types';
|
||||||
import { Interval, IntervalRecord } from './schedule.types';
|
|
||||||
import { AppwriteIds, databases } from 'src/boot/appwrite';
|
import { AppwriteIds, databases } from 'src/boot/appwrite';
|
||||||
import { ID, Query } from 'appwrite';
|
import { ID, Query } from 'appwrite';
|
||||||
import { useReservationStore } from './reservation';
|
import { useReservationStore } from './reservation';
|
||||||
|
import { LoadingTypes } from 'src/utils/misc';
|
||||||
|
import { useRealtimeStore } from './realtime';
|
||||||
|
|
||||||
export const useIntervalStore = defineStore('interval', () => {
|
export const useIntervalStore = defineStore('interval', () => {
|
||||||
// TODO: Implement functions to dynamically pull this data.
|
const intervals = ref(new Map<string, Interval>()); // Intervals by DocID
|
||||||
const intervals = ref<Map<string, Interval>>(new Map());
|
const dateStatus = ref(new Map<string, LoadingTypes>()); // State of load by date
|
||||||
const intervalDates = ref<IntervalRecord>({});
|
|
||||||
const reservationStore = useReservationStore();
|
|
||||||
const selectedDate = ref<string>(today());
|
const selectedDate = ref<string>(today());
|
||||||
|
|
||||||
const getIntervals = (date: Timestamp | string, boat?: Boat): Interval[] => {
|
const reservationStore = useReservationStore();
|
||||||
|
|
||||||
|
const realtimeStore = useRealtimeStore();
|
||||||
|
|
||||||
|
realtimeStore.register(
|
||||||
|
`databases.${AppwriteIds.databaseId}.collections.${AppwriteIds.collection.interval}.documents`,
|
||||||
|
(response) => {
|
||||||
|
const payload = response.payload as Interval;
|
||||||
|
if (!payload.$id) return;
|
||||||
|
if (
|
||||||
|
response.events.includes('databases.*.collections.*.documents.*.delete')
|
||||||
|
) {
|
||||||
|
intervals.value.delete(payload.$id);
|
||||||
|
} else {
|
||||||
|
intervals.value.set(payload.$id, payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const getIntervals = (date: Timestamp | string, boat?: Boat) => {
|
||||||
const searchDate = typeof date === 'string' ? date : date.date;
|
const searchDate = typeof date === 'string' ? date : date.date;
|
||||||
const dayStart = new Date(searchDate + 'T00:00');
|
const dayStart = new Date(searchDate + 'T00:00');
|
||||||
const dayEnd = new Date(searchDate + 'T23:59');
|
const dayEnd = new Date(searchDate + 'T23:59');
|
||||||
if (!intervalDates.value[searchDate]) {
|
if (dateStatus.value.get(searchDate) === undefined) {
|
||||||
intervalDates.value[searchDate] = 'pending';
|
dateStatus.value.set(searchDate, 'pending');
|
||||||
fetchIntervals(searchDate);
|
fetchIntervals(searchDate);
|
||||||
}
|
}
|
||||||
return computed(() => {
|
return computed(() => {
|
||||||
@@ -32,22 +51,19 @@ export const useIntervalStore = defineStore('interval', () => {
|
|||||||
const matchesBoat = boat ? boat.$id === interval.resource : true;
|
const matchesBoat = boat ? boat.$id === interval.resource : true;
|
||||||
return isWithinDay && matchesBoat;
|
return isWithinDay && matchesBoat;
|
||||||
});
|
});
|
||||||
}).value;
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAvailableIntervals = (
|
const getAvailableIntervals = (date: Timestamp | string, boat?: Boat) => {
|
||||||
date: Timestamp | string,
|
return computed(() =>
|
||||||
boat?: Boat
|
getIntervals(date, boat).value.filter((interval) => {
|
||||||
): Interval[] => {
|
|
||||||
return computed(() => {
|
|
||||||
return getIntervals(date, boat).filter((interval) => {
|
|
||||||
return !reservationStore.isResourceTimeOverlapped(
|
return !reservationStore.isResourceTimeOverlapped(
|
||||||
interval.resource,
|
interval.resource,
|
||||||
new Date(interval.start),
|
new Date(interval.start),
|
||||||
new Date(interval.end)
|
new Date(interval.end)
|
||||||
);
|
);
|
||||||
});
|
})
|
||||||
}).value;
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
async function fetchInterval(id: string): Promise<Interval> {
|
async function fetchInterval(id: string): Promise<Interval> {
|
||||||
@@ -78,11 +94,11 @@ export const useIntervalStore = defineStore('interval', () => {
|
|||||||
response.documents.forEach((d) =>
|
response.documents.forEach((d) =>
|
||||||
intervals.value.set(d.$id, d as Interval)
|
intervals.value.set(d.$id, d as Interval)
|
||||||
);
|
);
|
||||||
intervalDates.value[dateString] = 'loaded';
|
dateStatus.value.set(dateString, 'loaded');
|
||||||
console.info(`Loaded ${response.documents.length} intervals from server`);
|
console.info(`Loaded ${response.documents.length} intervals from server`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch intervals', error);
|
console.error('Failed to fetch intervals', error);
|
||||||
intervalDates.value[dateString] = 'error';
|
dateStatus.value.set(dateString, 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,5 +156,6 @@ export const useIntervalStore = defineStore('interval', () => {
|
|||||||
updateInterval,
|
updateInterval,
|
||||||
deleteInterval,
|
deleteInterval,
|
||||||
selectedDate,
|
selectedDate,
|
||||||
|
intervals,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
21
src/stores/realtime.ts
Normal file
21
src/stores/realtime.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { client } from 'src/boot/appwrite';
|
||||||
|
import { Interval } from './schedule.types';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { RealtimeResponseEvent } from 'appwrite';
|
||||||
|
|
||||||
|
export const useRealtimeStore = defineStore('realtime', () => {
|
||||||
|
const subscriptions = ref<Map<string, () => void>>(new Map());
|
||||||
|
|
||||||
|
const register = (
|
||||||
|
channel: string,
|
||||||
|
fn: (response: RealtimeResponseEvent<Interval>) => void
|
||||||
|
) => {
|
||||||
|
if (subscriptions.value.has(channel)) return; // Already subscribed. But maybe different callback fn?
|
||||||
|
subscriptions.value.set(channel, client.subscribe(channel, fn));
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
register,
|
||||||
|
};
|
||||||
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import type { Reservation } from './schedule.types';
|
import type { Reservation } from './schedule.types';
|
||||||
import { computed, ref, watch } from 'vue';
|
import { ComputedRef, computed, reactive } from 'vue';
|
||||||
import { AppwriteIds, databases } from 'src/boot/appwrite';
|
import { AppwriteIds, databases } from 'src/boot/appwrite';
|
||||||
import { ID, Query } from 'appwrite';
|
import { ID, Query } from 'appwrite';
|
||||||
import { date, useQuasar } from 'quasar';
|
import { date, useQuasar } from 'quasar';
|
||||||
@@ -8,15 +8,37 @@ import { Timestamp, parseDate, today } from '@quasar/quasar-ui-qcalendar';
|
|||||||
import { LoadingTypes } from 'src/utils/misc';
|
import { LoadingTypes } from 'src/utils/misc';
|
||||||
import { useAuthStore } from './auth';
|
import { useAuthStore } from './auth';
|
||||||
import { isPast } from 'src/utils/schedule';
|
import { isPast } from 'src/utils/schedule';
|
||||||
|
import { useRealtimeStore } from './realtime';
|
||||||
|
|
||||||
export const useReservationStore = defineStore('reservation', () => {
|
export const useReservationStore = defineStore('reservation', () => {
|
||||||
const reservations = ref<Map<string, Reservation>>(new Map());
|
const reservations = reactive<Map<string, Reservation>>(new Map());
|
||||||
const datesLoaded = ref<Record<string, LoadingTypes>>({});
|
const datesLoaded = reactive<Record<string, LoadingTypes>>({});
|
||||||
const userReservations = ref<Map<string, Reservation>>(new Map());
|
const userReservations = reactive<Map<string, Reservation>>(new Map());
|
||||||
// TODO: Come up with a better way of storing reservations by date & reservations for user
|
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
|
const realtimeStore = useRealtimeStore();
|
||||||
|
|
||||||
|
realtimeStore.register(
|
||||||
|
`databases.${AppwriteIds.databaseId}.collections.${AppwriteIds.collection.reservation}.documents`,
|
||||||
|
(response) => {
|
||||||
|
const payload = response.payload as Reservation;
|
||||||
|
if (payload.$id) {
|
||||||
|
if (
|
||||||
|
response.events.includes(
|
||||||
|
'databases.*.collections.*.documents.*.delete'
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
reservations.delete(payload.$id);
|
||||||
|
userReservations.delete(payload.$id);
|
||||||
|
} else {
|
||||||
|
reservations.set(payload.$id, payload);
|
||||||
|
if (payload.user === authStore.currentUser?.$id)
|
||||||
|
userReservations.set(payload.$id, payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
// Fetch reservations for a specific date range
|
// Fetch reservations for a specific date range
|
||||||
const fetchReservationsForDateRange = async (
|
const fetchReservationsForDateRange = async (
|
||||||
start: string = today(),
|
start: string = today(),
|
||||||
@@ -40,7 +62,7 @@ export const useReservationStore = defineStore('reservation', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
response.documents.forEach((d) =>
|
response.documents.forEach((d) =>
|
||||||
reservations.value.set(d.$id, d as Reservation)
|
reservations.set(d.$id, d as Reservation)
|
||||||
);
|
);
|
||||||
setDateLoaded(startDate, endDate, 'loaded');
|
setDateLoaded(startDate, endDate, 'loaded');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -81,8 +103,8 @@ export const useReservationStore = defineStore('reservation', () => {
|
|||||||
reservation
|
reservation
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
reservations.value.set(response.$id, response as Reservation);
|
reservations.set(response.$id, response as Reservation);
|
||||||
userReservations.value.set(response.$id, response as Reservation);
|
userReservations.set(response.$id, response as Reservation);
|
||||||
console.info('Reservation booked: ', response);
|
console.info('Reservation booked: ', response);
|
||||||
return response as Reservation;
|
return response as Reservation;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -95,14 +117,8 @@ export const useReservationStore = defineStore('reservation', () => {
|
|||||||
reservation: string | Reservation | null | undefined
|
reservation: string | Reservation | null | undefined
|
||||||
) => {
|
) => {
|
||||||
if (!reservation) return false;
|
if (!reservation) return false;
|
||||||
let id;
|
const id = typeof reservation === 'string' ? reservation : reservation.$id;
|
||||||
if (typeof reservation === 'string') {
|
if (!id) return false;
|
||||||
id = reservation;
|
|
||||||
} else if ('$id' in reservation && typeof reservation.$id === 'string') {
|
|
||||||
id = reservation.$id;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const status = $q.notify({
|
const status = $q.notify({
|
||||||
color: 'secondary',
|
color: 'secondary',
|
||||||
@@ -120,8 +136,8 @@ export const useReservationStore = defineStore('reservation', () => {
|
|||||||
AppwriteIds.collection.reservation,
|
AppwriteIds.collection.reservation,
|
||||||
id
|
id
|
||||||
);
|
);
|
||||||
reservations.value.delete(id);
|
reservations.delete(id);
|
||||||
userReservations.value.delete(id);
|
userReservations.delete(id);
|
||||||
console.info(`Deleted reservation: ${id}`);
|
console.info(`Deleted reservation: ${id}`);
|
||||||
status({
|
status({
|
||||||
color: 'warning',
|
color: 'warning',
|
||||||
@@ -146,7 +162,7 @@ export const useReservationStore = defineStore('reservation', () => {
|
|||||||
if (start > end) return [];
|
if (start > end) return [];
|
||||||
let curDate = start;
|
let curDate = start;
|
||||||
while (curDate < end) {
|
while (curDate < end) {
|
||||||
datesLoaded.value[(parseDate(curDate) as Timestamp).date] = state;
|
datesLoaded[(parseDate(curDate) as Timestamp).date] = state;
|
||||||
curDate = date.addToDate(curDate, { days: 1 });
|
curDate = date.addToDate(curDate, { days: 1 });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -157,8 +173,7 @@ export const useReservationStore = defineStore('reservation', () => {
|
|||||||
const unloaded = [];
|
const unloaded = [];
|
||||||
while (curDate < end) {
|
while (curDate < end) {
|
||||||
const parsedDate = (parseDate(curDate) as Timestamp).date;
|
const parsedDate = (parseDate(curDate) as Timestamp).date;
|
||||||
if (datesLoaded.value[parsedDate] === undefined)
|
if (datesLoaded[parsedDate] === undefined) unloaded.push(parsedDate);
|
||||||
unloaded.push(parsedDate);
|
|
||||||
curDate = date.addToDate(curDate, { days: 1 });
|
curDate = date.addToDate(curDate, { days: 1 });
|
||||||
}
|
}
|
||||||
return unloaded;
|
return unloaded;
|
||||||
@@ -168,15 +183,15 @@ export const useReservationStore = defineStore('reservation', () => {
|
|||||||
const getReservationsByDate = (
|
const getReservationsByDate = (
|
||||||
searchDate: string,
|
searchDate: string,
|
||||||
boat?: string
|
boat?: string
|
||||||
): Reservation[] => {
|
): ComputedRef<Reservation[]> => {
|
||||||
if (!datesLoaded.value[searchDate]) {
|
if (!datesLoaded[searchDate]) {
|
||||||
fetchReservationsForDateRange(searchDate);
|
fetchReservationsForDateRange(searchDate);
|
||||||
}
|
}
|
||||||
const dayStart = new Date(searchDate + 'T00:00');
|
const dayStart = new Date(searchDate + 'T00:00');
|
||||||
const dayEnd = new Date(searchDate + 'T23:59');
|
const dayEnd = new Date(searchDate + 'T23:59');
|
||||||
|
|
||||||
return computed(() => {
|
return computed(() => {
|
||||||
return Array.from(reservations.value.values()).filter((reservation) => {
|
return Array.from(reservations.values()).filter((reservation) => {
|
||||||
const reservationStart = new Date(reservation.start);
|
const reservationStart = new Date(reservation.start);
|
||||||
const reservationEnd = new Date(reservation.end);
|
const reservationEnd = new Date(reservation.end);
|
||||||
|
|
||||||
@@ -185,7 +200,7 @@ export const useReservationStore = defineStore('reservation', () => {
|
|||||||
const matchesBoat = boat ? boat === reservation.resource : true;
|
const matchesBoat = boat ? boat === reservation.resource : true;
|
||||||
return isWithinDay && matchesBoat;
|
return isWithinDay && matchesBoat;
|
||||||
});
|
});
|
||||||
}).value;
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get conflicting reservations for a resource within a time range
|
// Get conflicting reservations for a resource within a time range
|
||||||
@@ -194,7 +209,7 @@ export const useReservationStore = defineStore('reservation', () => {
|
|||||||
start: Date,
|
start: Date,
|
||||||
end: Date
|
end: Date
|
||||||
): Reservation[] => {
|
): Reservation[] => {
|
||||||
return Array.from(reservations.value.values()).filter(
|
return Array.from(reservations.values()).filter(
|
||||||
(entry) =>
|
(entry) =>
|
||||||
entry.resource === resource &&
|
entry.resource === resource &&
|
||||||
new Date(entry.start) < end &&
|
new Date(entry.start) < end &&
|
||||||
@@ -229,7 +244,7 @@ export const useReservationStore = defineStore('reservation', () => {
|
|||||||
[Query.equal('user', authStore.currentUser.$id)]
|
[Query.equal('user', authStore.currentUser.$id)]
|
||||||
);
|
);
|
||||||
response.documents.forEach((d) =>
|
response.documents.forEach((d) =>
|
||||||
userReservations.value.set(d.$id, d as Reservation)
|
userReservations.set(d.$id, d as Reservation)
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch reservations for user: ', error);
|
console.error('Failed to fetch reservations for user: ', error);
|
||||||
@@ -237,7 +252,7 @@ export const useReservationStore = defineStore('reservation', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const sortedUserReservations = computed((): Reservation[] =>
|
const sortedUserReservations = computed((): Reservation[] =>
|
||||||
[...userReservations.value?.values()].sort(
|
[...userReservations.values()].sort(
|
||||||
(a, b) => new Date(b.start).getTime() - new Date(a.start).getTime()
|
(a, b) => new Date(b.start).getTime() - new Date(a.start).getTime()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -252,27 +267,6 @@ export const useReservationStore = defineStore('reservation', () => {
|
|||||||
return sortedUserReservations.value?.filter((b) => isPast(b.end));
|
return sortedUserReservations.value?.filter((b) => isPast(b.end));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ensure reactivity for computed properties when Map is modified
|
|
||||||
watch(
|
|
||||||
reservations,
|
|
||||||
() => {
|
|
||||||
sortedUserReservations.value;
|
|
||||||
futureUserReservations.value;
|
|
||||||
pastUserReservations.value;
|
|
||||||
},
|
|
||||||
{ deep: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(
|
|
||||||
userReservations,
|
|
||||||
() => {
|
|
||||||
sortedUserReservations.value;
|
|
||||||
futureUserReservations.value;
|
|
||||||
pastUserReservations.value;
|
|
||||||
},
|
|
||||||
{ deep: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getReservationsByDate,
|
getReservationsByDate,
|
||||||
getReservationById,
|
getReservationById,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Models } from 'appwrite';
|
import { Models } from 'appwrite';
|
||||||
import { LoadingTypes } from 'src/utils/misc';
|
|
||||||
|
|
||||||
export type StatusTypes = 'tentative' | 'confirmed' | 'pending' | undefined;
|
export type StatusTypes = 'tentative' | 'confirmed' | 'pending' | undefined;
|
||||||
export type Reservation = Interval & {
|
export type Reservation = Interval & {
|
||||||
@@ -29,7 +28,3 @@ export type IntervalTemplate = Partial<Models.Document> & {
|
|||||||
name: string;
|
name: string;
|
||||||
timeTuples: TimeTuple[];
|
timeTuples: TimeTuple[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IntervalRecord {
|
|
||||||
[key: string]: LoadingTypes;
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user