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,176 +1,87 @@
<template> <template>
<q-card <q-tabs
clas="q-ma-md" v-model="tab"
bordered inline-label
v-if="!reservations"> class="text-primary">
<q-card-section> <q-tab
<div class="text-h6">You don't have any bookings!</div> name="upcoming"
<div class="text-h8">Why don't you go make one?</div> icon="schedule"
</q-card-section> label="Upcoming" />
<q-card-actions> <q-tab
<q-btn name="past"
color="primary" icon="history"
icon="event" label="Past" />
:size="`1.25em`" </q-tabs>
label="Book Now" <q-separator />
rounded
class="full-width" <q-tab-panels
:align="'left'" v-model="tab"
to="/schedule/book" /> animated>
</q-card-actions> <q-tab-panel
</q-card> name="upcoming"
<template class="q-pa-none">
v-else <q-card
v-for="(reservation, index) in sortedBookings" clas="q-ma-md"
:key="reservation.$id"> v-if="!reservationStore.futureUserReservations.length">
<q-toolbar <q-card-section>
class="bg-secondary glossy text-white" <div class="text-h6">You don't have any upcoming bookings!</div>
v-if="showMarker(index, sortedBookings)"> <div class="text-h8">Why don't you go make one?</div>
Past </q-card-section>
</q-toolbar> <q-card-actions>
<q-card <q-btn
bordered color="primary"
:class="isPast(reservation.end) ? 'text-blue-grey-6' : ''" icon="event"
class="q-ma-md"> :size="`1.25em`"
<q-card-section> label="Book Now"
<div class="row items-center no-wrap"> rounded
<div class="col"> class="full-width"
<div class="text-h6"> :align="'left'"
{{ boatStore.getBoatById(reservation.resource)?.name }} to="/schedule/book" />
</div> </q-card-actions>
<div class="text-subtitle2"> </q-card>
<p> <div v-else>
Start: {{ formatDate(reservation.start) }} <div
<br /> v-for="reservation in reservationStore.futureUserReservations"
End: {{ formatDate(reservation.end) }} :key="reservation.$id">
</p> <ReservationCardComponent :modelValue="reservation" />
</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> </div>
</q-card-section> </div>
</q-tab-panel>
<!-- <q-card-section>Some more information here...</q-card-section> --> <q-tab-panel
name="past"
<q-separator /> class="q-pa-none">
<div
<q-card-actions v-if="!isPast(reservation.end)"> v-for="reservation in reservationStore.pastUserReservations"
<q-btn :key="reservation.$id">
flat <ReservationCardComponent :modelValue="reservation" />
@click="modifyReservation(reservation)"> </div>
Modify </q-tab-panel>
</q-btn> </q-tab-panels>
<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');