Add Delete Reservation function
This commit is contained in:
114
src/components/scheduling/ReservationCardComponent.vue
Normal file
114
src/components/scheduling/ReservationCardComponent.vue
Normal 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>
|
||||
@@ -1,176 +1,87 @@
|
||||
<template>
|
||||
<q-card
|
||||
clas="q-ma-md"
|
||||
bordered
|
||||
v-if="!reservations">
|
||||
<q-card-section>
|
||||
<div class="text-h6">You don't have any bookings!</div>
|
||||
<div class="text-h8">Why don't you go make one?</div>
|
||||
</q-card-section>
|
||||
<q-card-actions>
|
||||
<q-btn
|
||||
color="primary"
|
||||
icon="event"
|
||||
:size="`1.25em`"
|
||||
label="Book Now"
|
||||
rounded
|
||||
class="full-width"
|
||||
:align="'left'"
|
||||
to="/schedule/book" />
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
<template
|
||||
v-else
|
||||
v-for="(reservation, index) in sortedBookings"
|
||||
:key="reservation.$id">
|
||||
<q-toolbar
|
||||
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 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> -->
|
||||
<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
|
||||
clas="q-ma-md"
|
||||
v-if="!reservationStore.futureUserReservations.length">
|
||||
<q-card-section>
|
||||
<div class="text-h6">You don't have any upcoming bookings!</div>
|
||||
<div class="text-h8">Why don't you go make one?</div>
|
||||
</q-card-section>
|
||||
<q-card-actions>
|
||||
<q-btn
|
||||
color="primary"
|
||||
icon="event"
|
||||
:size="`1.25em`"
|
||||
label="Book Now"
|
||||
rounded
|
||||
class="full-width"
|
||||
:align="'left'"
|
||||
to="/schedule/book" />
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
<div v-else>
|
||||
<div
|
||||
v-for="reservation in reservationStore.futureUserReservations"
|
||||
:key="reservation.$id">
|
||||
<ReservationCardComponent :modelValue="reservation" />
|
||||
</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(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>
|
||||
</div>
|
||||
</q-tab-panel>
|
||||
<q-tab-panel
|
||||
name="past"
|
||||
class="q-pa-none">
|
||||
<div
|
||||
v-for="reservation in reservationStore.pastUserReservations"
|
||||
:key="reservation.$id">
|
||||
<ReservationCardComponent :modelValue="reservation" />
|
||||
</div>
|
||||
</q-tab-panel>
|
||||
</q-tab-panels>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { useBoatStore } from 'src/stores/boat';
|
||||
import { useReservationStore } from 'src/stores/reservation';
|
||||
import { Reservation } from 'src/stores/schedule.types';
|
||||
import { formatDate } from 'src/utils/schedule';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import ReservationCardComponent from 'src/components/scheduling/ReservationCardComponent.vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
const reservationStore = useReservationStore();
|
||||
const reservations = reservationStore.getUserReservations();
|
||||
const boatStore = useBoatStore();
|
||||
const currentReservation = ref<Reservation>();
|
||||
const cancelDialog = ref(false);
|
||||
const tab = ref('upcoming');
|
||||
|
||||
const sortedBookings = computed(() =>
|
||||
reservations.value
|
||||
?.slice()
|
||||
.sort((a, b) => new Date(b.start).getTime() - new Date(a.start).getTime())
|
||||
);
|
||||
// const showMarker = (
|
||||
// index: number,
|
||||
// items: Reservation[] | undefined
|
||||
// ): boolean => {
|
||||
// if (!items) return false;
|
||||
|
||||
const isPast = (itemDate: Date | string): boolean => {
|
||||
if (!(itemDate instanceof Date)) {
|
||||
itemDate = new Date(itemDate);
|
||||
}
|
||||
console.log(itemDate);
|
||||
const currentDate = new Date();
|
||||
return itemDate < currentDate;
|
||||
};
|
||||
// const currentItemDate = new Date(items[index].start);
|
||||
// const nextItemDate = index > 0 ? new Date(items[index - 1].start) : null;
|
||||
|
||||
const showMarker = (
|
||||
index: number,
|
||||
items: Reservation[] | undefined
|
||||
): 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;
|
||||
};
|
||||
// // Show marker if current item is past and the next item is future or vice versa
|
||||
// return (
|
||||
// isPast(currentItemDate) && (nextItemDate === null || !isPast(nextItemDate))
|
||||
// );
|
||||
// };
|
||||
|
||||
onMounted(() => {
|
||||
boatStore.fetchBoats();
|
||||
|
||||
@@ -7,11 +7,13 @@ import { date } from 'quasar';
|
||||
import { Timestamp, parseDate, today } from '@quasar/quasar-ui-qcalendar';
|
||||
import { LoadingTypes } from 'src/utils/misc';
|
||||
import { useAuthStore } from './auth';
|
||||
import { isPast } from 'src/utils/schedule';
|
||||
|
||||
export const useReservationStore = defineStore('reservation', () => {
|
||||
const reservations = ref<Map<string, Reservation>>(new Map());
|
||||
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();
|
||||
|
||||
// Fetch reservations for a specific date range
|
||||
@@ -73,13 +75,15 @@ export const useReservationStore = defineStore('reservation', () => {
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log(id);
|
||||
try {
|
||||
await databases.deleteDocument(
|
||||
AppwriteIds.databaseId,
|
||||
AppwriteIds.collection.interval,
|
||||
AppwriteIds.collection.reservation,
|
||||
id
|
||||
);
|
||||
reservations.value.delete(id);
|
||||
userReservations.value.delete(id);
|
||||
console.info(`Deleted reservation: ${id}`);
|
||||
} catch (e) {
|
||||
console.error('Error deleting reservation: ' + e);
|
||||
@@ -165,10 +169,6 @@ export const useReservationStore = defineStore('reservation', () => {
|
||||
);
|
||||
};
|
||||
|
||||
const getUserReservations = () => {
|
||||
return userReservations;
|
||||
};
|
||||
|
||||
const fetchUserReservations = async () => {
|
||||
if (!authStore.currentUser) return;
|
||||
try {
|
||||
@@ -177,12 +177,30 @@ export const useReservationStore = defineStore('reservation', () => {
|
||||
AppwriteIds.collection.reservation,
|
||||
[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) {
|
||||
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 {
|
||||
getReservationsByDate,
|
||||
createReservation,
|
||||
@@ -192,6 +210,9 @@ export const useReservationStore = defineStore('reservation', () => {
|
||||
isResourceTimeOverlapped,
|
||||
getConflictingReservations,
|
||||
fetchUserReservations,
|
||||
getUserReservations,
|
||||
sortedUserReservations,
|
||||
futureUserReservations,
|
||||
pastUserReservations,
|
||||
userReservations,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -71,6 +71,14 @@ export function buildInterval(
|
||||
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 {
|
||||
if (!inputDate) return '';
|
||||
return date.formatDate(new Date(inputDate), 'ddd MMM Do hh:mm A');
|
||||
|
||||
Reference in New Issue
Block a user