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>
|
<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();
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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');
|
||||||
|
|||||||
Reference in New Issue
Block a user