Tracked down a date bug. Also tried to optimize, but not sure it's necessary.
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 2m2s
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 2m2s
This commit is contained in:
@@ -4,10 +4,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent, onMounted } from 'vue';
|
import { defineComponent, onMounted } from 'vue';
|
||||||
import { useScheduleStore } from './stores/schedule';
|
|
||||||
import { useBoatStore } from './stores/boat';
|
|
||||||
import { useAuthStore } from './stores/auth';
|
import { useAuthStore } from './stores/auth';
|
||||||
import { useReservationStore } from './stores/reservation';
|
|
||||||
|
|
||||||
defineComponent({
|
defineComponent({
|
||||||
name: 'OYS Borrow-a-Boat',
|
name: 'OYS Borrow-a-Boat',
|
||||||
@@ -15,9 +12,5 @@ defineComponent({
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await useAuthStore().init();
|
await useAuthStore().init();
|
||||||
await useScheduleStore().fetchIntervalTemplates();
|
|
||||||
await useScheduleStore().fetchIntervals();
|
|
||||||
await useReservationStore().fetchReservations();
|
|
||||||
await useBoatStore().fetchBoats();
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -70,11 +70,10 @@ import {
|
|||||||
parseTimestamp,
|
parseTimestamp,
|
||||||
parseDate,
|
parseDate,
|
||||||
addToDate,
|
addToDate,
|
||||||
getDate,
|
|
||||||
} from '@quasar/quasar-ui-qcalendar';
|
} from '@quasar/quasar-ui-qcalendar';
|
||||||
import CalendarHeaderComponent from './CalendarHeaderComponent.vue';
|
import CalendarHeaderComponent from './CalendarHeaderComponent.vue';
|
||||||
|
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed, onMounted } from 'vue';
|
||||||
import { useBoatStore } from 'src/stores/boat';
|
import { useBoatStore } from 'src/stores/boat';
|
||||||
import { useScheduleStore } from 'src/stores/schedule';
|
import { useScheduleStore } from 'src/stores/schedule';
|
||||||
import { useAuthStore } from 'src/stores/auth';
|
import { useAuthStore } from 'src/stores/auth';
|
||||||
@@ -90,6 +89,12 @@ const selectedDate = ref(today());
|
|||||||
|
|
||||||
const calendar = ref<QCalendarDay | null>(null);
|
const calendar = ref<QCalendarDay | null>(null);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await useBoatStore().fetchBoats();
|
||||||
|
await scheduleStore.fetchIntervals();
|
||||||
|
await scheduleStore.fetchIntervalTemplates();
|
||||||
|
});
|
||||||
|
|
||||||
function handleSwipe({ ...event }) {
|
function handleSwipe({ ...event }) {
|
||||||
event.direction === 'right' ? calendar.value?.prev() : calendar.value?.next();
|
event.direction === 'right' ? calendar.value?.prev() : calendar.value?.next();
|
||||||
}
|
}
|
||||||
@@ -169,18 +174,24 @@ function selectBlock(event: MouseEvent, scope: DayBodyScope, block: Interval) {
|
|||||||
: (selectedBlock.value = block);
|
: (selectedBlock.value = block);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BoatBlocks {
|
const boatBlocks = computed((): Record<string, Interval[]> => {
|
||||||
[key: string]: Interval[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const boatBlocks = computed((): BoatBlocks => {
|
|
||||||
return scheduleStore
|
return scheduleStore
|
||||||
.getIntervalsForDate(selectedDate.value)
|
.getIntervalsForDate(selectedDate.value)
|
||||||
.reduce((result, tb) => {
|
.reduce((result, tb) => {
|
||||||
if (!result[tb.boatId]) result[tb.boatId] = [];
|
if (!result[tb.boatId]) result[tb.boatId] = [];
|
||||||
result[tb.boatId].push(tb);
|
result[tb.boatId].push(tb);
|
||||||
return result;
|
return result;
|
||||||
}, <BoatBlocks>{});
|
}, <Record<string, Interval[]>>{});
|
||||||
|
});
|
||||||
|
|
||||||
|
const boatReservations = computed((): Record<string, Reservation[]> => {
|
||||||
|
return reservationStore
|
||||||
|
.getReservationsByDate(selectedDate.value)
|
||||||
|
.reduce((result, reservation) => {
|
||||||
|
if (!result[reservation.resource]) result[reservation.resource] = [];
|
||||||
|
result[reservation.resource].push(reservation);
|
||||||
|
return result;
|
||||||
|
}, <Record<string, Reservation[]>>{});
|
||||||
});
|
});
|
||||||
|
|
||||||
function getBoatBlocks(scope: DayBodyScope): Interval[] {
|
function getBoatBlocks(scope: DayBodyScope): Interval[] {
|
||||||
@@ -189,9 +200,7 @@ function getBoatBlocks(scope: DayBodyScope): Interval[] {
|
|||||||
}
|
}
|
||||||
function getBoatReservations(scope: DayBodyScope): Reservation[] {
|
function getBoatReservations(scope: DayBodyScope): Reservation[] {
|
||||||
const boat = boats.value[scope.columnIndex];
|
const boat = boats.value[scope.columnIndex];
|
||||||
return boat
|
return boat ? boatReservations.value[boat.$id] : [];
|
||||||
? reservationStore.getReservationsByDate(getDate(scope.timestamp), boat.$id)
|
|
||||||
: [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// function changeEvent({ start }: { start: string }) {
|
// function changeEvent({ start }: { start: string }) {
|
||||||
|
|||||||
@@ -1,29 +1,109 @@
|
|||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import type { Reservation } from './schedule.types';
|
import type { Reservation } from './schedule.types';
|
||||||
import { ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { AppwriteIds, databases } from 'src/boot/appwrite';
|
import { AppwriteIds, databases } from 'src/boot/appwrite';
|
||||||
import { Query } from 'appwrite';
|
import { Query } from 'appwrite';
|
||||||
import { date } from 'quasar';
|
import { date } from 'quasar';
|
||||||
import { Timestamp, parseDate } from '@quasar/quasar-ui-qcalendar';
|
import { Timestamp, parseDate, today } from '@quasar/quasar-ui-qcalendar';
|
||||||
|
|
||||||
export const useReservationStore = defineStore('reservation', () => {
|
export const useReservationStore = defineStore('reservation', () => {
|
||||||
|
type LoadingTypes = 'loaded' | 'pending' | undefined;
|
||||||
const reservations = ref<Map<string, Reservation>>(new Map());
|
const reservations = ref<Map<string, Reservation>>(new Map());
|
||||||
const datesLoaded = ref<Record<string, boolean>>({});
|
const datesLoaded = ref<Record<string, LoadingTypes>>({});
|
||||||
|
|
||||||
|
// Fetch reservations for a specific date range
|
||||||
|
const fetchReservationsForDateRange = async (
|
||||||
|
start: string = today(),
|
||||||
|
end: string = start
|
||||||
|
) => {
|
||||||
|
const startDate = new Date(start + 'T00:00');
|
||||||
|
const endDate = new Date(end + 'T23:59');
|
||||||
|
if (getUnloadedDates(startDate, endDate).length === 0) return;
|
||||||
|
|
||||||
|
setDateLoaded(startDate, endDate, 'pending');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await databases.listDocuments(
|
||||||
|
AppwriteIds.databaseId,
|
||||||
|
AppwriteIds.collection.reservation,
|
||||||
|
[
|
||||||
|
Query.greaterThanEqual('end', startDate.toISOString()),
|
||||||
|
Query.lessThanEqual('start', endDate.toISOString()),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
response.documents.forEach((d) =>
|
||||||
|
reservations.value.set(d.$id, d as Reservation)
|
||||||
|
);
|
||||||
|
setDateLoaded(startDate, endDate, 'loaded');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch reservations', error);
|
||||||
|
setDateLoaded(startDate, endDate, undefined);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set the loading state for dates
|
||||||
|
const setDateLoaded = (start: Date, end: Date, state: LoadingTypes) => {
|
||||||
|
if (start > end) return [];
|
||||||
|
let curDate = start;
|
||||||
|
while (curDate < end) {
|
||||||
|
datesLoaded.value[(parseDate(curDate) as Timestamp).date] = state;
|
||||||
|
curDate = date.addToDate(curDate, { days: 1 });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getUnloadedDates = (start: Date, end: Date): string[] => {
|
||||||
|
if (start > end) return [];
|
||||||
|
let curDate = start;
|
||||||
|
const unloaded = [];
|
||||||
|
while (curDate < end) {
|
||||||
|
const parsedDate = (parseDate(curDate) as Timestamp).date;
|
||||||
|
if (datesLoaded.value[parsedDate] === undefined)
|
||||||
|
unloaded.push(parsedDate);
|
||||||
|
curDate = date.addToDate(curDate, { days: 1 });
|
||||||
|
}
|
||||||
|
return unloaded;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get reservations by date and optionally filter by boat
|
||||||
|
const getReservationsByDate = (
|
||||||
|
searchDate: string,
|
||||||
|
boat?: string
|
||||||
|
): Reservation[] => {
|
||||||
|
if (!datesLoaded.value[searchDate]) {
|
||||||
|
fetchReservationsForDateRange(searchDate);
|
||||||
|
}
|
||||||
|
const dayStart = new Date(searchDate + 'T00:00');
|
||||||
|
const dayEnd = new Date(searchDate + 'T23:59');
|
||||||
|
|
||||||
|
return computed(() => {
|
||||||
|
return Array.from(reservations.value.values()).filter((reservation) => {
|
||||||
|
const reservationStart = new Date(reservation.start);
|
||||||
|
const reservationEnd = new Date(reservation.end);
|
||||||
|
|
||||||
|
const isWithinDay =
|
||||||
|
reservationStart < dayEnd && reservationEnd > dayStart;
|
||||||
|
const matchesBoat = boat ? boat === reservation.resource : true;
|
||||||
|
return isWithinDay && matchesBoat;
|
||||||
|
});
|
||||||
|
}).value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get conflicting reservations for a resource within a time range
|
||||||
const getConflictingReservations = (
|
const getConflictingReservations = (
|
||||||
resource: string,
|
resource: string,
|
||||||
start: Date,
|
start: Date,
|
||||||
end: Date
|
end: Date
|
||||||
): Reservation[] => {
|
): Reservation[] => {
|
||||||
const overlapped = Array.from(reservations.value.values()).filter(
|
return Array.from(reservations.value.values()).filter(
|
||||||
(entry: Reservation) =>
|
(entry) =>
|
||||||
entry.resource == resource &&
|
entry.resource === resource &&
|
||||||
new Date(entry.start) < end &&
|
new Date(entry.start) < end &&
|
||||||
new Date(entry.end) > start
|
new Date(entry.end) > start
|
||||||
);
|
);
|
||||||
return overlapped;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Check if a resource has time overlap
|
||||||
const isResourceTimeOverlapped = (
|
const isResourceTimeOverlapped = (
|
||||||
resource: string,
|
resource: string,
|
||||||
start: Date,
|
start: Date,
|
||||||
@@ -32,6 +112,7 @@ export const useReservationStore = defineStore('reservation', () => {
|
|||||||
return getConflictingReservations(resource, start, end).length > 0;
|
return getConflictingReservations(resource, start, end).length > 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Check if a reservation overlaps with existing reservations
|
||||||
const isReservationOverlapped = (res: Reservation): boolean => {
|
const isReservationOverlapped = (res: Reservation): boolean => {
|
||||||
return isResourceTimeOverlapped(
|
return isResourceTimeOverlapped(
|
||||||
res.resource,
|
res.resource,
|
||||||
@@ -40,75 +121,9 @@ export const useReservationStore = defineStore('reservation', () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function setDateLoaded(start: Date, end: Date, state: boolean) {
|
|
||||||
let curDate = start;
|
|
||||||
while (curDate < end) {
|
|
||||||
datesLoaded.value[(parseDate(curDate) as Timestamp).date] = state;
|
|
||||||
curDate = date.addToDate(curDate, { days: 1 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async function fetchReservationsForDateRange(start: string, end?: string) {
|
|
||||||
const startDate = new Date(start + 'T00:00');
|
|
||||||
const endDate = new Date(end || start + 'T23:59');
|
|
||||||
|
|
||||||
datesLoaded.value[start] = false;
|
|
||||||
|
|
||||||
if (end) {
|
|
||||||
setDateLoaded(startDate, endDate, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
databases
|
|
||||||
.listDocuments(
|
|
||||||
AppwriteIds.databaseId,
|
|
||||||
AppwriteIds.collection.reservation,
|
|
||||||
[
|
|
||||||
Query.and([
|
|
||||||
Query.greaterThanEqual('end', startDate.toISOString()),
|
|
||||||
Query.lessThanEqual('start', endDate.toISOString()),
|
|
||||||
]),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
.then((response) => {
|
|
||||||
response.documents.forEach((d) =>
|
|
||||||
reservations.value.set(d.$id, d as Reservation)
|
|
||||||
);
|
|
||||||
setDateLoaded(startDate, endDate, true);
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to fetch reservations', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchReservations() {
|
|
||||||
return;
|
|
||||||
// fetchReservationsForDateRange(
|
|
||||||
// today(),
|
|
||||||
// date.addToDate(today(), { days: 7 }).toLocaleDateString()
|
|
||||||
// );
|
|
||||||
}
|
|
||||||
|
|
||||||
const getReservationsByDate = (
|
|
||||||
searchDate: string,
|
|
||||||
boat?: string
|
|
||||||
): Reservation[] => {
|
|
||||||
if (!datesLoaded.value[searchDate]) {
|
|
||||||
fetchReservationsForDateRange(searchDate);
|
|
||||||
}
|
|
||||||
const result = Array.from(reservations.value.values()).filter((x) => {
|
|
||||||
return new Date(x.start) < new Date(searchDate + 'T' + '23:59') &&
|
|
||||||
new Date(x.end) > new Date(searchDate + 'T' + '00:00') && // Part of reservation falls on day
|
|
||||||
boat
|
|
||||||
? boat === x.boat
|
|
||||||
: true; // A specific boat has been passed, and matches
|
|
||||||
});
|
|
||||||
console.log(result);
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getReservationsByDate,
|
getReservationsByDate,
|
||||||
fetchReservations,
|
fetchReservationsForDateRange,
|
||||||
isReservationOverlapped,
|
isReservationOverlapped,
|
||||||
isResourceTimeOverlapped,
|
isResourceTimeOverlapped,
|
||||||
getConflictingReservations,
|
getConflictingReservations,
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ export const useScheduleStore = defineStore('schedule', () => {
|
|||||||
|
|
||||||
const getIntervalsForDate = (date: string): Interval[] => {
|
const getIntervalsForDate = (date: string): Interval[] => {
|
||||||
// TODO: This needs to actually make sure we have the dates we need, stay in sync, etc.
|
// TODO: This needs to actually make sure we have the dates we need, stay in sync, etc.
|
||||||
|
|
||||||
return intervals.value.filter((b) => {
|
return intervals.value.filter((b) => {
|
||||||
return compareDate(
|
return compareDate(
|
||||||
parseDate(new Date(b.start)) as Timestamp,
|
parseDate(new Date(b.start)) as Timestamp,
|
||||||
|
|||||||
Reference in New Issue
Block a user