refactor: everything to nuxt.js
This commit is contained in:
283
app/stores/reservation.ts
Normal file
283
app/stores/reservation.ts
Normal file
@@ -0,0 +1,283 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import type { Reservation } from '~/utils/schedule.types';
|
||||
import type { ComputedRef } from 'vue';
|
||||
import { computed, reactive } from 'vue';
|
||||
import { AppwriteIds, databases } from '~/utils/appwrite';
|
||||
import { ID, Query } from 'appwrite';
|
||||
import { date, useQuasar } from 'quasar';
|
||||
import type { Timestamp } from '@quasar/quasar-ui-qcalendar';
|
||||
import { parseDate, today } from '@quasar/quasar-ui-qcalendar';
|
||||
import type { LoadingTypes } from '~/utils/misc';
|
||||
import { useAuthStore } from './auth';
|
||||
import { isPast } from '~/utils/schedule';
|
||||
import { useRealtimeStore } from './realtime';
|
||||
|
||||
export const useReservationStore = defineStore('reservation', () => {
|
||||
const reservations = reactive<Map<string, Reservation>>(new Map());
|
||||
const datesLoaded = reactive<Record<string, LoadingTypes>>({});
|
||||
const userReservations = reactive<Map<string, Reservation>>(new Map());
|
||||
|
||||
const authStore = useAuthStore();
|
||||
const $q = useQuasar();
|
||||
const realtimeStore = useRealtimeStore();
|
||||
|
||||
realtimeStore.register(
|
||||
`databases.${AppwriteIds.databaseId}.collections.${AppwriteIds.collection.reservation}.documents`,
|
||||
(response) => {
|
||||
const payload = response.payload as unknown 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const fetchReservationsForDateRange = async (
|
||||
start: string = today(),
|
||||
end: string = start
|
||||
) => {
|
||||
const startDate = new Date(start < end ? start : end + 'T00:00');
|
||||
const endDate = new Date(start < end ? end : start + '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.set(d.$id, d as unknown as Reservation)
|
||||
);
|
||||
setDateLoaded(startDate, endDate, 'loaded');
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch reservations', error);
|
||||
setDateLoaded(startDate, endDate, 'error');
|
||||
}
|
||||
};
|
||||
|
||||
const getReservationById = async (id: string) => {
|
||||
try {
|
||||
const response = await databases.getDocument(
|
||||
AppwriteIds.databaseId,
|
||||
AppwriteIds.collection.reservation,
|
||||
id
|
||||
);
|
||||
return response as unknown as Reservation;
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch reservation: ', error);
|
||||
}
|
||||
};
|
||||
|
||||
const createOrUpdateReservation = async (
|
||||
reservation: Reservation
|
||||
): Promise<Reservation> => {
|
||||
let response;
|
||||
try {
|
||||
if (reservation.$id) {
|
||||
response = await databases.updateDocument(
|
||||
AppwriteIds.databaseId,
|
||||
AppwriteIds.collection.reservation,
|
||||
reservation.$id,
|
||||
reservation
|
||||
);
|
||||
} else {
|
||||
response = await databases.createDocument(
|
||||
AppwriteIds.databaseId,
|
||||
AppwriteIds.collection.reservation,
|
||||
ID.unique(),
|
||||
reservation
|
||||
);
|
||||
}
|
||||
reservations.set(response.$id, response as unknown as Reservation);
|
||||
userReservations.set(response.$id, response as unknown as Reservation);
|
||||
console.info('Reservation booked: ', response);
|
||||
return response as unknown as Reservation;
|
||||
} catch (e) {
|
||||
console.error('Error creating Reservation: ' + e);
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
const deleteReservation = async (
|
||||
reservation: string | Reservation | null | undefined
|
||||
) => {
|
||||
if (!reservation) return false;
|
||||
const id = typeof reservation === 'string' ? reservation : reservation.$id;
|
||||
if (!id) return false;
|
||||
|
||||
const status = $q.notify({
|
||||
color: 'secondary',
|
||||
textColor: 'white',
|
||||
message: 'Deleting Reservation',
|
||||
spinner: true,
|
||||
closeBtn: 'Dismiss',
|
||||
position: 'top',
|
||||
timeout: 0,
|
||||
group: false,
|
||||
});
|
||||
try {
|
||||
await databases.deleteDocument(
|
||||
AppwriteIds.databaseId,
|
||||
AppwriteIds.collection.reservation,
|
||||
id
|
||||
);
|
||||
reservations.delete(id);
|
||||
userReservations.delete(id);
|
||||
console.info(`Deleted reservation: ${id}`);
|
||||
status({
|
||||
color: 'warning',
|
||||
message: 'Reservation Deleted',
|
||||
spinner: false,
|
||||
icon: 'delete',
|
||||
timeout: 4000,
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('Error deleting reservation: ' + e);
|
||||
status({
|
||||
color: 'negative',
|
||||
message: 'Failed to Delete Reservation',
|
||||
spinner: false,
|
||||
icon: 'error',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const setDateLoaded = (start: Date, end: Date, state: LoadingTypes) => {
|
||||
if (start > end) return [];
|
||||
let curDate = start;
|
||||
while (curDate < end) {
|
||||
datesLoaded[(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[parsedDate] === undefined) unloaded.push(parsedDate);
|
||||
curDate = date.addToDate(curDate, { days: 1 });
|
||||
}
|
||||
return unloaded;
|
||||
};
|
||||
|
||||
const getReservationsByDate = (
|
||||
searchDate: string,
|
||||
boat?: string
|
||||
): ComputedRef<Reservation[]> => {
|
||||
if (!datesLoaded[searchDate]) {
|
||||
fetchReservationsForDateRange(searchDate);
|
||||
}
|
||||
const dayStart = new Date(searchDate + 'T00:00');
|
||||
const dayEnd = new Date(searchDate + 'T23:59');
|
||||
|
||||
return computed(() => {
|
||||
return Array.from(reservations.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;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const getConflictingReservations = (
|
||||
resource: string,
|
||||
start: Date,
|
||||
end: Date
|
||||
): Reservation[] => {
|
||||
return Array.from(reservations.values()).filter(
|
||||
(entry) =>
|
||||
entry.resource === resource &&
|
||||
new Date(entry.start) < end &&
|
||||
new Date(entry.end) > start
|
||||
);
|
||||
};
|
||||
|
||||
const isResourceTimeOverlapped = (
|
||||
resource: string,
|
||||
start: Date,
|
||||
end: Date
|
||||
): boolean => {
|
||||
return getConflictingReservations(resource, start, end).length > 0;
|
||||
};
|
||||
|
||||
const isReservationOverlapped = (res: Reservation): boolean => {
|
||||
return isResourceTimeOverlapped(
|
||||
res.resource,
|
||||
new Date(res.start),
|
||||
new Date(res.end)
|
||||
);
|
||||
};
|
||||
|
||||
const fetchUserReservations = async () => {
|
||||
if (!authStore.currentUser) return;
|
||||
try {
|
||||
const response = await databases.listDocuments(
|
||||
AppwriteIds.databaseId,
|
||||
AppwriteIds.collection.reservation,
|
||||
[Query.equal('user', authStore.currentUser.$id)]
|
||||
);
|
||||
response.documents.forEach((d) =>
|
||||
userReservations.set(d.$id, d as unknown as Reservation)
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch reservations for user: ', error);
|
||||
}
|
||||
};
|
||||
|
||||
const sortedUserReservations = computed((): Reservation[] =>
|
||||
[...userReservations.values()].sort(
|
||||
(a, b) => new Date(b.start).getTime() - new Date(a.start).getTime()
|
||||
)
|
||||
);
|
||||
|
||||
const futureUserReservations = computed((): Reservation[] => {
|
||||
if (!sortedUserReservations.value) return [];
|
||||
return sortedUserReservations.value.filter((b) => !isPast(b.end));
|
||||
});
|
||||
|
||||
const pastUserReservations = computed((): Reservation[] => {
|
||||
if (!sortedUserReservations.value) return [];
|
||||
return sortedUserReservations.value?.filter((b) => isPast(b.end));
|
||||
});
|
||||
|
||||
return {
|
||||
getReservationsByDate,
|
||||
getReservationById,
|
||||
createOrUpdateReservation,
|
||||
deleteReservation,
|
||||
fetchReservationsForDateRange,
|
||||
isReservationOverlapped,
|
||||
isResourceTimeOverlapped,
|
||||
getConflictingReservations,
|
||||
fetchUserReservations,
|
||||
sortedUserReservations,
|
||||
futureUserReservations,
|
||||
pastUserReservations,
|
||||
userReservations,
|
||||
};
|
||||
});
|
||||
Reference in New Issue
Block a user