'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 { toast } = useToast(); useEffect(() => { fetchBookings(); }, []); 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 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) => { 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); // Allow modifications if booking is more than 2 hours away return hoursDiff > 2; }; 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 && (

Booking can only be modified more than 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' />