Add Delete Reservation function

This commit is contained in:
2024-05-26 07:13:20 -04:00
parent 59d2729719
commit 6654132120
4 changed files with 223 additions and 169 deletions

View File

@@ -0,0 +1,114 @@
<template>
<q-card
bordered
:class="isPast(reservation.end) ? 'text-blue-grey-6' : ''"
class="q-ma-md">
<q-card-section>
<div class="row items-center no-wrap">
<div class="col">
<div class="text-h6">
{{ boatStore.getBoatById(reservation.resource)?.name }}
</div>
<div class="text-subtitle2">
<p>
Start: {{ formatDate(reservation.start) }}
<br />
End: {{ formatDate(reservation.end) }}
</p>
</div>
</div>
<!-- <div class="col-auto">
<q-btn
color="grey-7"
round
flat
icon="more_vert">
<q-menu
cover
auto-close>
<q-list>
<q-item clickable>
<q-item-section>remove card</q-item-section>
</q-item>
<q-item clickable>
<q-item-section>send feedback</q-item-section>
</q-item>
<q-item clickable>
<q-item-section>share</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</div> -->
</div>
</q-card-section>
<!-- <q-card-section>Some more information here...</q-card-section> -->
<q-separator />
<q-card-actions v-if="!isPast(reservation.end)">
<q-btn
flat
@click="modifyReservation()">
Modify
</q-btn>
<q-btn
flat
@click="cancelReservation()">
Cancel
</q-btn>
</q-card-actions>
</q-card>
<q-dialog v-model="cancelDialog">
<q-card>
<q-card-section class="row items-center">
<q-avatar
icon="warning"
color="negative"
text-color="white" />
<span class="q-ml-md">Warning!</span>
<p class="q-pt-md">
This will delete your reservation for
{{ boatStore.getBoatById(reservation?.resource)?.name }} on
{{ formatDate(reservation?.start) }}
</p>
</q-card-section>
<q-card-actions align="right">
<q-btn
flat
label="Cancel"
color="primary"
v-close-popup />
<q-btn
flat
label="Delete"
color="negative"
@click="reservationStore.deleteReservation(reservation)"
v-close-popup />
</q-card-actions>
</q-card>
</q-dialog>
</template>
<script setup lang="ts">
import { useBoatStore } from 'src/stores/boat';
import { useReservationStore } from 'src/stores/reservation';
import type { Reservation } from 'src/stores/schedule.types';
import { formatDate, isPast } from 'src/utils/schedule';
import { ref } from 'vue';
const cancelDialog = ref(false);
const boatStore = useBoatStore();
const reservationStore = useReservationStore();
const reservation = defineModel<Reservation>({ required: true });
const cancelReservation = () => {
cancelDialog.value = true;
};
const modifyReservation = () => {
return;
};
</script>

View File

@@ -1,10 +1,30 @@
<template> <template>
<q-tabs
v-model="tab"
inline-label
class="text-primary">
<q-tab
name="upcoming"
icon="schedule"
label="Upcoming" />
<q-tab
name="past"
icon="history"
label="Past" />
</q-tabs>
<q-separator />
<q-tab-panels
v-model="tab"
animated>
<q-tab-panel
name="upcoming"
class="q-pa-none">
<q-card <q-card
clas="q-ma-md" clas="q-ma-md"
bordered v-if="!reservationStore.futureUserReservations.length">
v-if="!reservations">
<q-card-section> <q-card-section>
<div class="text-h6">You don't have any 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>
</q-card-section> </q-card-section>
<q-card-actions> <q-card-actions>
@@ -19,158 +39,49 @@
to="/schedule/book" /> to="/schedule/book" />
</q-card-actions> </q-card-actions>
</q-card> </q-card>
<template <div v-else>
v-else <div
v-for="(reservation, index) in sortedBookings" v-for="reservation in reservationStore.futureUserReservations"
:key="reservation.$id"> :key="reservation.$id">
<q-toolbar <ReservationCardComponent :modelValue="reservation" />
class="bg-secondary glossy text-white"
v-if="showMarker(index, sortedBookings)">
Past
</q-toolbar>
<q-card
bordered
:class="isPast(reservation.end) ? 'text-blue-grey-6' : ''"
class="q-ma-md">
<q-card-section>
<div class="row items-center no-wrap">
<div class="col">
<div class="text-h6">
{{ boatStore.getBoatById(reservation.resource)?.name }}
</div>
<div class="text-subtitle2">
<p>
Start: {{ formatDate(reservation.start) }}
<br />
End: {{ formatDate(reservation.end) }}
</p>
</div> </div>
</div> </div>
<!-- <div class="col-auto"> </q-tab-panel>
<q-btn <q-tab-panel
color="grey-7" name="past"
round class="q-pa-none">
flat <div
icon="more_vert"> v-for="reservation in reservationStore.pastUserReservations"
<q-menu :key="reservation.$id">
cover <ReservationCardComponent :modelValue="reservation" />
auto-close>
<q-list>
<q-item clickable>
<q-item-section>remove card</q-item-section>
</q-item>
<q-item clickable>
<q-item-section>send feedback</q-item-section>
</q-item>
<q-item clickable>
<q-item-section>share</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</div> -->
</div> </div>
</q-card-section> </q-tab-panel>
</q-tab-panels>
<!-- <q-card-section>Some more information here...</q-card-section> -->
<q-separator />
<q-card-actions v-if="!isPast(reservation.end)">
<q-btn
flat
@click="modifyReservation(reservation)">
Modify
</q-btn>
<q-btn
flat
@click="cancelReservation(reservation)">
Cancel
</q-btn>
</q-card-actions>
</q-card>
</template>
<q-dialog v-model="cancelDialog">
<q-card>
<q-card-section class="row items-center">
<q-avatar
icon="stop"
color="negative"
text-color="white" />
<span class="q-ml-sm">
This will delete your reservation for
{{ boatStore.getBoatById(currentReservation?.resource) }} on
{{ formatDate(currentReservation?.start) }}
</span>
</q-card-section>
<q-card-actions align="right">
<q-btn
flat
label="Cancel"
color="primary"
v-close-popup />
<q-btn
flat
label="Delete"
color="negative"
@click="reservationStore.deleteReservation(Reservation)"
v-close-popup />
</q-card-actions>
</q-card>
</q-dialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useBoatStore } from 'src/stores/boat'; import { useBoatStore } from 'src/stores/boat';
import { useReservationStore } from 'src/stores/reservation'; import { useReservationStore } from 'src/stores/reservation';
import { Reservation } from 'src/stores/schedule.types'; import ReservationCardComponent from 'src/components/scheduling/ReservationCardComponent.vue';
import { formatDate } from 'src/utils/schedule'; import { onMounted, ref } from 'vue';
import { computed, onMounted, ref } from 'vue';
const reservationStore = useReservationStore(); const reservationStore = useReservationStore();
const reservations = reservationStore.getUserReservations();
const boatStore = useBoatStore(); const boatStore = useBoatStore();
const currentReservation = ref<Reservation>(); const tab = ref('upcoming');
const cancelDialog = ref(false);
const sortedBookings = computed(() => // const showMarker = (
reservations.value // index: number,
?.slice() // items: Reservation[] | undefined
.sort((a, b) => new Date(b.start).getTime() - new Date(a.start).getTime()) // ): boolean => {
); // if (!items) return false;
const isPast = (itemDate: Date | string): boolean => { // const currentItemDate = new Date(items[index].start);
if (!(itemDate instanceof Date)) { // const nextItemDate = index > 0 ? new Date(items[index - 1].start) : null;
itemDate = new Date(itemDate);
}
console.log(itemDate);
const currentDate = new Date();
return itemDate < currentDate;
};
const showMarker = ( // // Show marker if current item is past and the next item is future or vice versa
index: number, // return (
items: Reservation[] | undefined // isPast(currentItemDate) && (nextItemDate === null || !isPast(nextItemDate))
): boolean => { // );
if (!items) return false; // };
const currentItemDate = new Date(items[index].start);
const nextItemDate = index > 0 ? new Date(items[index - 1].start) : null;
// Show marker if current item is past and the next item is future or vice versa
return (
isPast(currentItemDate) && (nextItemDate === null || !isPast(nextItemDate))
);
};
const cancelReservation = (reservation: Reservation) => {
currentReservation.value = reservation;
cancelDialog.value = true;
};
const modifyReservation = (reservation: Reservation) => {
return reservation;
};
onMounted(() => { onMounted(() => {
boatStore.fetchBoats(); boatStore.fetchBoats();

View File

@@ -7,11 +7,13 @@ import { date } from 'quasar';
import { Timestamp, parseDate, today } from '@quasar/quasar-ui-qcalendar'; 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';
export const useReservationStore = defineStore('reservation', () => { export const useReservationStore = defineStore('reservation', () => {
const reservations = ref<Map<string, Reservation>>(new Map()); const reservations = ref<Map<string, Reservation>>(new Map());
const datesLoaded = ref<Record<string, LoadingTypes>>({}); const datesLoaded = ref<Record<string, LoadingTypes>>({});
const userReservations = ref<Reservation[]>(); const userReservations = ref<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();
// Fetch reservations for a specific date range // Fetch reservations for a specific date range
@@ -73,13 +75,15 @@ export const useReservationStore = defineStore('reservation', () => {
return false; return false;
} }
console.log(id);
try { try {
await databases.deleteDocument( await databases.deleteDocument(
AppwriteIds.databaseId, AppwriteIds.databaseId,
AppwriteIds.collection.interval, AppwriteIds.collection.reservation,
id id
); );
reservations.value.delete(id); reservations.value.delete(id);
userReservations.value.delete(id);
console.info(`Deleted reservation: ${id}`); console.info(`Deleted reservation: ${id}`);
} catch (e) { } catch (e) {
console.error('Error deleting reservation: ' + e); console.error('Error deleting reservation: ' + e);
@@ -165,10 +169,6 @@ export const useReservationStore = defineStore('reservation', () => {
); );
}; };
const getUserReservations = () => {
return userReservations;
};
const fetchUserReservations = async () => { const fetchUserReservations = async () => {
if (!authStore.currentUser) return; if (!authStore.currentUser) return;
try { try {
@@ -177,12 +177,30 @@ export const useReservationStore = defineStore('reservation', () => {
AppwriteIds.collection.reservation, AppwriteIds.collection.reservation,
[Query.equal('user', authStore.currentUser.$id)] [Query.equal('user', authStore.currentUser.$id)]
); );
userReservations.value = response.documents as Reservation[]; response.documents.forEach((d) =>
userReservations.value.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);
} }
}; };
const sortedUserReservations = computed(() =>
[...userReservations.value?.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 { return {
getReservationsByDate, getReservationsByDate,
createReservation, createReservation,
@@ -192,6 +210,9 @@ export const useReservationStore = defineStore('reservation', () => {
isResourceTimeOverlapped, isResourceTimeOverlapped,
getConflictingReservations, getConflictingReservations,
fetchUserReservations, fetchUserReservations,
getUserReservations, sortedUserReservations,
futureUserReservations,
pastUserReservations,
userReservations,
}; };
}); });

View File

@@ -71,6 +71,14 @@ export function buildInterval(
return result; return result;
} }
export const isPast = (itemDate: Date | string): boolean => {
if (!(itemDate instanceof Date)) {
itemDate = new Date(itemDate);
}
const currentDate = new Date();
return itemDate < currentDate;
};
export function formatDate(inputDate: string | undefined): string { export function formatDate(inputDate: string | undefined): string {
if (!inputDate) return ''; if (!inputDate) return '';
return date.formatDate(new Date(inputDate), 'ddd MMM Do hh:mm A'); return date.formatDate(new Date(inputDate), 'ddd MMM Do hh:mm A');