/** * 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 BookingType = 'all' | 'standard' | 'csv'; export default function BookingsListPage() { const [searchTerm, setSearchTerm] = useState(''); const [statusFilter, setStatusFilter] = useState(''); const [bookingType, setBookingType] = useState('csv'); // Start with CSV bookings const [page, setPage] = useState(1); // Fetch standard bookings const { data: standardData, isLoading: standardLoading, error: standardError } = useQuery({ queryKey: ['bookings', page, statusFilter, searchTerm], queryFn: () => listBookings({ page, limit: 10, status: statusFilter || undefined, }), enabled: bookingType === 'all' || bookingType === 'standard', retry: false, // Don't retry if it fails }); // Fetch CSV bookings const { data: csvData, isLoading: csvLoading, error: csvError } = useQuery({ queryKey: ['csv-bookings', page, statusFilter], queryFn: () => listCsvBookings({ page, limit: 10, status: statusFilter as 'PENDING' | 'ACCEPTED' | 'REJECTED' | undefined, }), enabled: bookingType === 'all' || bookingType === 'csv', }); // Log errors for debugging if (standardError) console.error('Standard bookings error:', standardError); if (csvError) console.error('CSV bookings error:', csvError); const isLoading = standardLoading || csvLoading; // Combine bookings based on filter const getCombinedBookings = () => { if (bookingType === 'standard') { return (standardData?.bookings || []).map(b => ({ ...b, type: 'standard' as const })); } if (bookingType === 'csv') { return (csvData?.bookings || []).map(b => ({ ...b, type: 'csv' as const })); } // For 'all', combine both const standard = (standardData?.bookings || []).map(b => ({ ...b, type: 'standard' as const })); const csv = (csvData?.bookings || []).map(b => ({ ...b, type: 'csv' as const })); return [...standard, ...csv].sort((a, b) => { const dateA = new Date((a as any).createdAt || (a as any).requestedAt || 0).getTime(); const dateB = new Date((b as any).createdAt || (b as any).requestedAt || 0).getTime(); return dateB - dateA; }); }; const allBookings = getCombinedBookings(); const statusOptions = [ { value: '', label: 'Tous les statuts' }, // Standard booking statuses { value: 'draft', label: 'Brouillon' }, { value: 'pending', label: 'En attente' }, { value: 'confirmed', label: 'Confirmé' }, { value: 'in_transit', label: 'En transit' }, { value: 'delivered', label: 'Livré' }, { value: 'cancelled', label: 'Annulé' }, // CSV booking statuses { value: 'PENDING', label: 'En attente (CSV)' }, { value: 'ACCEPTED', label: 'Accepté (CSV)' }, { value: 'REJECTED', label: 'Refusé (CSV)' }, ]; const getStatusColor = (status: string) => { const colors: Record = { // Standard statuses draft: 'bg-gray-100 text-gray-800', pending: 'bg-yellow-100 text-yellow-800', confirmed: 'bg-green-100 text-green-800', in_transit: 'bg-blue-100 text-blue-800', delivered: 'bg-purple-100 text-purple-800', cancelled: 'bg-red-100 text-red-800', // CSV statuses 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 = { // Standard statuses draft: 'Brouillon', pending: 'En attente', confirmed: 'Confirmé', in_transit: 'En transit', delivered: 'Livré', cancelled: 'Annulé', // CSV statuses 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)} 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="Rechercher par numéro de réservation..." />
{/* Bookings Table */}
{isLoading ? (
Chargement des réservations...
) : allBookings && allBookings.length > 0 ? ( <>
{allBookings.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 */} {((standardData?.total || 0) + (csvData?.total || 0)) > 10 && (

Affichage de {(page - 1) * 10 + 1} à{' '} {Math.min(page * 10, (standardData?.total || 0) + (csvData?.total || 0))} {' '} sur{' '} {(standardData?.total || 0) + (csvData?.total || 0)} {' '} résultats

)} ) : (

Aucune réservation trouvée

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

Nouvelle Réservation
)}
); }