feat: Enhance reservation functionality
This commit is contained in:
@@ -35,7 +35,13 @@
|
||||
No upcoming reservations.
|
||||
</p>
|
||||
<IonList v-else lines="full">
|
||||
<IonItem v-for="r in upcomingReservations" :key="r.id">
|
||||
<IonItem
|
||||
v-for="r in upcomingReservations"
|
||||
:key="r.id"
|
||||
button
|
||||
detail
|
||||
@click="openActionSheet(r)"
|
||||
>
|
||||
<IonLabel>
|
||||
<h3>{{ boatName(r) }}</h3>
|
||||
<p>{{ formatDateRange(r.start_time, r.end_time) }}</p>
|
||||
@@ -71,9 +77,9 @@ import {
|
||||
IonPage, IonHeader, IonToolbar, IonTitle, IonContent,
|
||||
IonButtons, IonMenuButton, IonButton, IonCard, IonCardHeader,
|
||||
IonCardTitle, IonCardContent, IonList, IonItem, IonLabel,
|
||||
IonBadge, IonSpinner, IonIcon,
|
||||
IonBadge, IonSpinner, IonIcon, actionSheetController, onIonViewWillEnter,
|
||||
} from '@ionic/vue'
|
||||
import { addCircleOutline } from 'ionicons/icons'
|
||||
import { addCircleOutline, createOutline, trashOutline } from 'ionicons/icons'
|
||||
import { useAuthStore } from '~/stores/auth'
|
||||
import type { Database, ReservationStatus } from '~/types/supabase'
|
||||
|
||||
@@ -86,6 +92,7 @@ definePageMeta({ layout: false })
|
||||
const user = useSupabaseUser()
|
||||
const auth = useAuthStore()
|
||||
const supabase = useSupabaseClient<Database>()
|
||||
const router = useRouter()
|
||||
|
||||
const today = new Date()
|
||||
const loadingReservations = ref(true)
|
||||
@@ -99,13 +106,47 @@ async function fetchReservations() {
|
||||
.select('*, boats(name, display_name)')
|
||||
.eq('user_id', user.value.id)
|
||||
.gte('start_time', new Date().toISOString())
|
||||
.neq('status', 'cancelled')
|
||||
.order('start_time', { ascending: true })
|
||||
.limit(3)
|
||||
upcomingReservations.value = (data as ReservationWithBoat[]) ?? []
|
||||
loadingReservations.value = false
|
||||
}
|
||||
|
||||
// Initial load and re-fetch on every page visit (Ionic caches the page, so
|
||||
// watch(user) alone won't re-run when navigating back from create/edit).
|
||||
watch(user, (val) => { if (val) fetchReservations() }, { immediate: true })
|
||||
onIonViewWillEnter(() => { if (user.value) fetchReservations() })
|
||||
|
||||
async function openActionSheet(r: ReservationWithBoat) {
|
||||
const isFuture = new Date(r.start_time) > new Date()
|
||||
const sheet = await actionSheetController.create({
|
||||
header: `${boatName(r)} — ${formatDateRange(r.start_time, r.end_time)}`,
|
||||
buttons: [
|
||||
...(isFuture ? [
|
||||
{
|
||||
text: 'Edit',
|
||||
icon: createOutline,
|
||||
handler: () => void router.push(`/reservations/edit/${r.id}`),
|
||||
},
|
||||
{
|
||||
text: 'Cancel Reservation',
|
||||
role: 'destructive',
|
||||
icon: trashOutline,
|
||||
handler: () => void cancelReservation(r.id),
|
||||
},
|
||||
] : []),
|
||||
{ text: 'Dismiss', role: 'cancel' },
|
||||
],
|
||||
})
|
||||
await sheet.present()
|
||||
}
|
||||
|
||||
async function cancelReservation(id: string) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
await (supabase as any).from('reservations').update({ status: 'cancelled' }).eq('id', id)
|
||||
upcomingReservations.value = upcomingReservations.value.filter(r => r.id !== id)
|
||||
}
|
||||
|
||||
function boatName(r: ReservationWithBoat) {
|
||||
return r.boats?.display_name || r.boats?.name || 'Unknown boat'
|
||||
@@ -122,9 +163,10 @@ function formatDateRange(start: string, end: string) {
|
||||
|
||||
function statusColor(status: ReservationStatus): string {
|
||||
const colors: Record<ReservationStatus, string> = {
|
||||
pending: 'warning',
|
||||
pending: 'warning',
|
||||
tentative: 'medium',
|
||||
confirmed: 'success',
|
||||
cancelled: 'danger',
|
||||
}
|
||||
return colors[status]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user