import { EventDetailsModal } from "@ui/components/EventDetailsModal"; import { LoadingSpinner } from "@ui/components/LoadingSpinner"; import { getTextColorFromTabloColor } from "@xtablo/shared"; import { EventAndTablo } from "@xtablo/shared/types/events.types"; import { Button } from "@xtablo/ui/components/button"; import { ButtonGroup } from "@xtablo/ui/components/button-group"; import { Input } from "@xtablo/ui/components/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@xtablo/ui/components/select"; import { Strong, Text, TypographyH3, TypographyMuted } from "@xtablo/ui/components/typography"; import { Calendar as CalendarIcon, ChevronLeft, ChevronRight, SearchIcon } from "lucide-react"; import { useEffect, useMemo, useState } from "react"; import { useNavigate, useParams } from "react-router-dom"; import { twMerge } from "tailwind-merge"; import { useEventsByTablo } from "../hooks/events"; import { useGetAllTabloAccess, useTablosList } from "../hooks/tablos"; type BookingStatus = "all" | "upcoming" | "past"; interface BookingStatusOption { id: BookingStatus; name: string; } const statusOptions: BookingStatusOption[] = [ { id: "upcoming", name: "À venir" }, { id: "past", name: "Passés" }, ]; export const BookingsPage = () => { const { tablo_id } = useParams(); const navigate = useNavigate(); const [selectedTabloId, setSelectedTabloId] = useState(tablo_id || "all"); const [searchTerm, setSearchTerm] = useState(""); const [statusFilter, setStatusFilter] = useState("upcoming"); const [selectedEvent, setSelectedEvent] = useState(null); const [isDetailsModalOpen, setIsDetailsModalOpen] = useState(false); const [currentPage, setCurrentPage] = useState(1); const [itemsPerPage, setItemsPerPage] = useState(10); // Fetch tablos and events const { data: tablos, isLoading: tablosLoading } = useTablosList(); const { data: events = [], isLoading: eventsLoading } = useEventsByTablo( selectedTabloId !== "all" ? selectedTabloId : null ); // Fetch all tablo accesses for permissions const { data: tabloAccess } = useGetAllTabloAccess(); // Filter and search events const filteredEvents = useMemo(() => { if (!events) return []; let filtered = events; // Search filter if (searchTerm) { filtered = filtered.filter( (event) => event.title?.toLowerCase().includes(searchTerm.toLowerCase()) || event.description?.toLowerCase().includes(searchTerm.toLowerCase()) || event.tablo_name?.toLowerCase().includes(searchTerm.toLowerCase()) ); } // Status filter if (statusFilter !== "all") { const today = new Date(); today.setHours(0, 0, 0, 0); const tomorrow = new Date(today); tomorrow.setDate(tomorrow.getDate() + 1); filtered = filtered.filter((event) => { if (!event.start_date) return false; const eventDate = new Date(event.start_date); eventDate.setHours(0, 0, 0, 0); switch (statusFilter) { case "upcoming": return eventDate >= today; case "past": return eventDate < today; default: return true; } }); } return filtered.sort((a, b) => { if (!a.start_date || !b.start_date) return 0; return new Date(a.start_date).getTime() - new Date(b.start_date).getTime(); }); }, [events, searchTerm, statusFilter]); // Pagination logic const totalItems = filteredEvents.length; const totalPages = Math.ceil(totalItems / itemsPerPage); const startIndex = (currentPage - 1) * itemsPerPage; const endIndex = startIndex + itemsPerPage; const paginatedEvents = filteredEvents.slice(startIndex, endIndex); // Reset to first page when filters or page size change useEffect(() => { setCurrentPage(1); }, [searchTerm, statusFilter, selectedTabloId, itemsPerPage]); const formatEventDateTime = (event: EventAndTablo) => { if (!event.start_date) return "Date non définie"; try { const date = new Date(event.start_date); const options: Intl.DateTimeFormatOptions = { weekday: "long", year: "numeric", month: "long", day: "numeric", }; let formatted = date.toLocaleDateString("fr-FR", options); if (event.start_time) { // Remove seconds from time (HH:MM:SS -> HH:MM) const startTime = event.start_time.substring(0, 5); formatted += ` à ${startTime}`; if (event.end_time) { const endTime = event.end_time.substring(0, 5); formatted += ` - ${endTime}`; } } return formatted; } catch { return "Date invalide"; } }; const getEventStatusBadge = (event: EventAndTablo) => { if (!event.start_date) return null; const today = new Date(); today.setHours(0, 0, 0, 0); const eventDate = new Date(event.start_date); eventDate.setHours(0, 0, 0, 0); if (eventDate.getTime() === today.getTime()) { return ( Aujourd'hui ); } else if (eventDate > today) { return ( À venir ); } else { return ( Passé ); } }; const handleCreateEvent = () => { const today = new Date(); const dateParam = today.toISOString(); const tabloParam = selectedTabloId !== "all" ? `&tablo_id=${selectedTabloId}` : ""; navigate(`/planning/create?date=${dateParam}${tabloParam}`); }; // Check if an event can be edited (admin access required) const canEditEvent = (event: EventAndTablo) => { return tabloAccess?.find((access) => access.tablo_id === event.tablo_id && access.is_admin) ? true : false; }; const handleEditEvent = (event: EventAndTablo) => { if (event.event_id && event.tablo_id && canEditEvent(event)) { navigate(`/planning/${event.tablo_id}/events/${event.event_id}/edit`); } }; const handleViewEvent = (event: EventAndTablo) => { setSelectedEvent(event); setIsDetailsModalOpen(true); }; return (
{/* Header */}
Réservations Gérez vos événements et réservations
{/* Main Content */}
{/* Filters */}
{/* Search */}
setSearchTerm(e.target.value)} className="pl-10 h-10" />
{/* Tablo Filter */}
{/* Status Filter */} {statusOptions.map((option) => ( ))}
{/* Events List */}
{tablosLoading || eventsLoading ? (
) : paginatedEvents.length === 0 ? (

Aucun événement trouvé

{searchTerm || statusFilter !== "all" ? "Essayez de modifier vos filtres de recherche." : "Commencez par créer votre premier événement."}

) : (
{paginatedEvents.map((event) => (
{event.title || "Événement sans titre"} {getEventStatusBadge(event)}
{formatEventDateTime(event)} {event.tablo_name && ( {event.tablo_name} )}
{event.description && ( {event.description} )}
))}
)}
{/* Pagination Controls */} {totalItems > 0 && (
Affichage de {startIndex + 1} à {Math.min(endIndex, totalItems)} sur {totalItems}{" "} événements
Éléments par page:
{totalPages > 1 && (
{Array.from({ length: totalPages }, (_, i) => i + 1) .filter((page) => { // Show first page, last page, current page, and pages around current return ( page === 1 || page === totalPages || Math.abs(page - currentPage) <= 1 ); }) .map((page, index, array) => { // Add ellipsis if there's a gap const prevPage = array[index - 1]; const showEllipsis = prevPage && page - prevPage > 1; return (
{showEllipsis && ( ... )}
); })}
)}
)} {/* Stats Summary */} {filteredEvents.length > 0 && (
{filteredEvents.length}
Événements trouvés
{ filteredEvents.filter((e) => { if (!e.start_date) return false; const eventDate = new Date(e.start_date); return eventDate >= new Date(); }).length }
À venir
{ filteredEvents.filter((e) => { if (!e.start_date) return false; const today = new Date(); today.setHours(0, 0, 0, 0); const eventDate = new Date(e.start_date); eventDate.setHours(0, 0, 0, 0); return eventDate.getTime() === today.getTime(); }).length }
Aujourd'hui
)} {/* Event Details Modal */} { setIsDetailsModalOpen(false); setSelectedEvent(null); }} onEdit={() => selectedEvent && handleEditEvent(selectedEvent)} canEdit={selectedEvent ? canEditEvent(selectedEvent) : false} />
); };