/** * Bookings List Page * * Display all bookings (standard + CSV) with filters and search */ 'use client'; import { useState } from 'react'; import { useQuery } from '@tanstack/react-query'; import { listBookings, listCsvBookings } from '@/lib/api'; import Link from 'next/link'; type SearchType = 'pallets' | 'weight' | 'route' | 'status' | 'date' | 'quote'; export default function BookingsListPage() { const [searchTerm, setSearchTerm] = useState(''); const [searchType, setSearchType] = useState('route'); const [statusFilter, setStatusFilter] = useState(''); const [page, setPage] = useState(1); const ITEMS_PER_PAGE = 20; // Fetch CSV bookings (fetch all for client-side filtering and pagination) const { data: csvData, isLoading, error: csvError } = useQuery({ queryKey: ['csv-bookings'], queryFn: () => listCsvBookings({ page: 1, limit: 1000, // Fetch all bookings for client-side filtering }), }); // Log errors for debugging if (csvError) console.error('CSV bookings error:', csvError); // Filter bookings based on search term, search type, and status const filterBookings = (bookings: any[]) => { let filtered = bookings; // Filter by status first (if status filter is active) if (statusFilter) { filtered = filtered.filter((booking: any) => booking.status === statusFilter); } // Then filter by search term if provided if (searchTerm.trim()) { const term = searchTerm.toLowerCase(); filtered = filtered.filter((booking: any) => { switch (searchType) { case 'pallets': return booking.palletCount?.toString().includes(term); case 'weight': return booking.weightKG?.toString().includes(term); case 'route': const origin = booking.originCity?.toLowerCase() || ''; const destination = booking.destinationCity?.toLowerCase() || ''; return origin.includes(term) || destination.includes(term); case 'status': return booking.status?.toLowerCase().includes(term); case 'date': const date = new Date(booking.requestedPickupDate || booking.requestedAt).toLocaleDateString('fr-FR'); return date.includes(term); case 'quote': return booking.id?.toLowerCase().includes(term) || booking.quoteNumber?.toLowerCase().includes(term); default: return true; } }); } return filtered; }; // Get all filtered bookings const filteredBookings = filterBookings((csvData?.bookings || []).map(b => ({ ...b, type: 'csv' as const }))); // Calculate pagination const totalBookings = filteredBookings.length; const totalPages = Math.ceil(totalBookings / ITEMS_PER_PAGE); const startIndex = (page - 1) * ITEMS_PER_PAGE; const endIndex = startIndex + ITEMS_PER_PAGE; const paginatedBookings = filteredBookings.slice(startIndex, endIndex); // Reset page to 1 when filters change const resetPage = () => setPage(1); const statusOptions = [ { value: '', label: 'Tous les statuts' }, { value: 'PENDING', label: 'En attente' }, { value: 'ACCEPTED', label: 'Accepté' }, { value: 'REJECTED', label: 'Refusé' }, ]; const searchTypeOptions = [ { value: 'route', label: 'Route (Origine/Destination)' }, { value: 'pallets', label: 'Palettes/Colis' }, { value: 'weight', label: 'Poids (kg)' }, { value: 'status', label: 'Statut' }, { value: 'date', label: 'Date' }, { value: 'quote', label: 'N° Devis' }, ]; const getPlaceholder = () => { switch (searchType) { case 'pallets': return 'Rechercher par nombre de palettes...'; case 'weight': return 'Rechercher par poids en kg...'; case 'route': return 'Rechercher par ville (origine ou destination)...'; case 'status': return 'Rechercher par statut...'; case 'date': return 'Rechercher par date (JJ/MM/AAAA)...'; case 'quote': return 'Rechercher par numéro de devis...'; default: return 'Rechercher...'; } }; const getStatusColor = (status: string) => { const colors: Record = { PENDING: 'bg-yellow-100 text-yellow-800', ACCEPTED: 'bg-green-100 text-green-800', REJECTED: 'bg-red-100 text-red-800', }; return colors[status] || 'bg-gray-100 text-gray-800'; }; const getStatusLabel = (status: string) => { const labels: Record = { PENDING: 'En attente', ACCEPTED: 'Accepté', REJECTED: 'Refusé', }; return labels[status] || status; }; return (
{/* Header */}

Réservations

Gérez et suivez vos envois

Nouvelle Réservation
{/* Filters */}
{ setSearchTerm(e.target.value); resetPage(); }} className="block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md leading-5 bg-white placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-blue-500 focus:border-blue-500 sm:text-sm" placeholder={getPlaceholder()} />
{/* Bookings Table */}
{isLoading ? (
Chargement des réservations...
) : paginatedBookings && paginatedBookings.length > 0 ? ( <>
{paginatedBookings.map((booking: any) => ( ))}
Palettes/Colis Poids Route Statut Date N° Devis
{booking.type === 'csv' ? `${booking.palletCount} palette${booking.palletCount > 1 ? 's' : ''}` : `${booking.containers?.length || 0} conteneur${booking.containers?.length > 1 ? 's' : ''}`}
{booking.type === 'csv' ? 'LCL' : booking.containerType || 'FCL'}
{booking.type === 'csv' ? `${booking.weightKG} kg` : booking.totalWeight ? `${booking.totalWeight} kg` : 'N/A'}
{booking.type === 'csv' ? `${booking.volumeCBM} CBM` : booking.totalVolume ? `${booking.totalVolume} CBM` : ''}
{booking.type === 'csv' ? `${booking.origin} → ${booking.destination}` : booking.route || 'N/A'}
{booking.type === 'csv' ? `${booking.carrierName}` : booking.carrier || ''}
{getStatusLabel(booking.status)} {(booking.createdAt || booking.requestedAt) ? new Date(booking.createdAt || booking.requestedAt).toLocaleDateString('fr-FR', { day: '2-digit', month: '2-digit', year: 'numeric', }) : 'N/A'} {booking.type === 'csv' ? `#${booking.bookingId || booking.id.slice(0, 8).toUpperCase()}` : booking.bookingNumber || `#${booking.id.slice(0, 8).toUpperCase()}`}
{/* Pagination */} {totalPages > 1 && (

Affichage de {startIndex + 1} à{' '} {Math.min(endIndex, totalBookings)} sur{' '} {totalBookings} résultat{totalBookings > 1 ? 's' : ''}

)} ) : (

Aucune réservation trouvée

{searchTerm || statusFilter ? 'Essayez d\'ajuster vos filtres' : 'Commencez par créer votre première réservation'}

Nouvelle Réservation
)}
); }