'use client'; import { useState, useEffect } from 'react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog'; import { Calendar, Clock, MapPin, Edit, Trash2, User, RefreshCw } from 'lucide-react'; import { format } from 'date-fns'; import { useToast } from '@/hooks/use-toast'; interface Booking { id: string; date: string; startTime: string; endTime: string; status: string; notes?: string; createdAt: Date; court: { id: string; name: string; }; } export function UserBookingManagement() { const [bookings, setBookings] = useState([]); const [loading, setLoading] = useState(true); const [editDialogOpen, setEditDialogOpen] = useState(false); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [selectedBooking, setSelectedBooking] = useState(null); const [editNotes, setEditNotes] = useState(''); const [editPartner, setEditPartner] = useState(''); const [settings, setSettings] = useState<{ allow_booking_modifications: string; booking_modification_hours_before: string; } | null>(null); const { toast } = useToast(); useEffect(() => { fetchBookings(); fetchSettings(); }, []); const fetchBookings = async () => { try { setLoading(true); const response = await fetch('/api/bookings'); if (response.ok) { const data = await response.json(); // Filter to show only future and today's bookings const now = new Date(); const today = now.toISOString().split('T')[0]; const relevantBookings = data.bookings.filter((booking: Booking) => { if (booking.status !== 'active') return false; return booking.date >= today; }); setBookings(relevantBookings); } } catch (error) { console.error('Error fetching bookings:', error); toast({ title: 'Error', description: 'Failed to fetch your bookings', variant: 'destructive', }); } finally { setLoading(false); } }; const fetchSettings = async () => { try { const response = await fetch('/api/settings'); if (response.ok) { const data = await response.json(); const settingsMap = { allow_booking_modifications: 'true', booking_modification_hours_before: '2', }; data.settings?.forEach((setting: any) => { if (setting.key in settingsMap) { settingsMap[setting.key as keyof typeof settingsMap] = setting.value; } }); setSettings(settingsMap); } } catch (error) { console.error('Error fetching settings:', error); // Use default settings if fetch fails setSettings({ allow_booking_modifications: 'true', booking_modification_hours_before: '2', }); } }; const parseBookingNotes = (notes?: string) => { if (!notes) return { partner: '', additionalNotes: '' }; const parts = notes.split(' | '); let partner = ''; let additionalNotes = ''; parts.forEach((part) => { if (part.startsWith('Partner: ')) { partner = part.replace('Partner: ', ''); } else { additionalNotes = additionalNotes ? `${additionalNotes} | ${part}` : part; } }); return { partner, additionalNotes }; }; const handleEditClick = (booking: Booking) => { setSelectedBooking(booking); const { partner, additionalNotes } = parseBookingNotes(booking.notes); setEditPartner(partner); setEditNotes(additionalNotes); setEditDialogOpen(true); }; const handleDeleteClick = (booking: Booking) => { setSelectedBooking(booking); setDeleteDialogOpen(true); }; const handleEditSave = async () => { if (!selectedBooking) return; try { const bookingNotes = []; if (editPartner.trim()) { bookingNotes.push(`Partner: ${editPartner.trim()}`); } if (editNotes.trim()) { bookingNotes.push(editNotes.trim()); } const response = await fetch(`/api/bookings/${selectedBooking.id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ notes: bookingNotes.join(' | '), }), }); if (response.ok) { toast({ title: 'Success', description: 'Booking updated successfully', }); setEditDialogOpen(false); fetchBookings(); } else { const data = await response.json(); toast({ title: 'Error', description: data.error || 'Failed to update booking', variant: 'destructive', }); } } catch (error) { console.error('Error updating booking:', error); toast({ title: 'Error', description: 'Failed to update booking', variant: 'destructive', }); } }; const handleDeleteConfirm = async () => { if (!selectedBooking) return; try { const response = await fetch(`/api/bookings/${selectedBooking.id}`, { method: 'DELETE', }); if (response.ok) { toast({ title: 'Success', description: 'Booking cancelled successfully', }); setDeleteDialogOpen(false); fetchBookings(); } else { const data = await response.json(); toast({ title: 'Error', description: data.error || 'Failed to cancel booking', variant: 'destructive', }); } } catch (error) { console.error('Error cancelling booking:', error); toast({ title: 'Error', description: 'Failed to cancel booking', variant: 'destructive', }); } }; const formatDate = (dateStr: string) => { try { const date = new Date(dateStr); return format(date, 'EEE, MMM dd'); } catch { return dateStr; } }; const isToday = (dateStr: string) => { const today = new Date().toISOString().split('T')[0]; return dateStr === today; }; const canModifyBooking = (booking: Booking) => { if (!settings || settings.allow_booking_modifications !== 'true') { return false; } const bookingDateTime = new Date(`${booking.date}T${booking.startTime}`); const now = new Date(); const timeDiff = bookingDateTime.getTime() - now.getTime(); const hoursDiff = timeDiff / (1000 * 60 * 60); const requiredHours = parseFloat(settings.booking_modification_hours_before) || 2; // Allow modifications if booking is more than the required hours away return hoursDiff > requiredHours; }; if (loading) { return ( Your Bookings
{[1, 2, 3].map((i) => (
))}
); } return ( <> Your Bookings {bookings.length === 0 ? (
No upcoming bookings. Make your first booking!
) : (
{bookings.map((booking) => { const { partner, additionalNotes } = parseBookingNotes(booking.notes); const canModify = canModifyBooking(booking); return (
{booking.court.name} {isToday(booking.date) && ( 🎯 Today )}
{formatDate(booking.date)}
{booking.startTime} - {booking.endTime}
{partner && (
Playing with: {partner}
)} {additionalNotes && (

{additionalNotes}

)}
{!canModify && (

{settings?.allow_booking_modifications !== 'true' ? 'Booking modifications are currently disabled by administrator' : `Booking can only be modified more than ${ settings?.booking_modification_hours_before || '2' } hours before the session`}

)}
); })}
)}
{/* Edit Dialog */} Edit Booking
{selectedBooking && (
{formatDate(selectedBooking.date)}
{selectedBooking.startTime} - {selectedBooking.endTime}
{selectedBooking.court.name}
)}
setEditPartner(e.target.value)} className='pl-10' />