'use client'; import { useQuery } from '@tanstack/react-query'; import { useParams } from 'next/navigation'; import { useLocale, useTranslations } from 'next-intl'; import { ArrowLeft, ArrowRight, CreditCard, FileText, Package, Pencil } from 'lucide-react'; import { getCsvBooking } from '@/lib/api'; import { Link } from '@/i18n/navigation'; import { Button } from '@/components/ui/button'; import { Callout } from '@/components/ui/callout'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { PageHeader } from '@/components/ui/PageHeader'; import { EmptyState, ErrorState, PageSpinner } from '@/components/ui/states'; import { BookingStatusBadge } from '@/components/bookings/BookingStatusBadge'; /** * Detail d'une reservation. * * La page interrogeait `GET /bookings/:id` et affichait un modele — expediteur, * destinataire, conteneurs — qui n'existe pas dans ce produit : la table * `bookings` n'a jamais ete creee, les reservations vivent dans `csv_bookings`. * Elle repondait donc 500 et tournait indefiniment. C'est ce qui donnait * l'impression qu'un clic sur une notification « ne faisait rien » : la * navigation avait bien lieu, la destination ne s'affichait jamais. * * Elle lit desormais `GET /csv-bookings/:id`, la meme source que la liste. */ export default function BookingDetailPage() { const t = useTranslations('dashboard.bookingDetail'); const tList = useTranslations('dashboard.bookingsList'); const tCommon = useTranslations('common'); const locale = useLocale(); const dateLocale = locale === 'fr' ? 'fr-FR' : 'en-US'; const params = useParams(); const bookingId = params.id as string; const { data: booking, isPending, isError, error, refetch, } = useQuery({ queryKey: ['csv-booking', bookingId], queryFn: () => getCsvBooking(bookingId), enabled: Boolean(bookingId), // Une reservation introuvable ne le deviendra pas en reessayant : trois // tentatives ne faisaient que prolonger le tourniquet. retry: (failureCount, err) => (err as { statusCode?: number })?.statusCode === 404 ? false : failureCount < 1, }); const dateTime = (value?: string) => value ? new Date(value).toLocaleString(dateLocale, { dateStyle: 'long', timeStyle: 'short' }) : '—'; if (isPending) return ; if (isError) { const missing = (error as { statusCode?: number })?.statusCode === 404; return missing ? ( {t('back')} } /> ) : ( refetch()} /> ); } const unpaid = booking.status === 'PENDING_PAYMENT'; const price = booking.priceEUR ? `${booking.priceEUR} €` : booking.priceUSD ? `${booking.priceUSD} $` : null; return ( <> {unpaid && ( {tList('detail.payNow')} )} > } /> {unpaid && ( {tList('detail.pendingPaymentNotice')} )} {booking.status === 'REJECTED' && booking.rejectionReason && ( {booking.rejectionReason} )} {/* La route est ce qu'un transitaire regarde en premier : elle occupe la tete de page, pas une ligne de tableau parmi d'autres. */} {booking.origin} {booking.transitDays ? ( {t('transitDays', { days: booking.transitDays })} ) : null} {booking.destination} {booking.carrierName && ( {t('carrier', { carrier: booking.carrierName })} )} {tList('detail.cargo')} {booking.palletCount ? tList('units.palletsCount', { count: booking.palletCount }) : '—'} {booking.volumeCBM ? tList('units.cbm', { value: booking.volumeCBM }) : '—'} {booking.weightKG ? tList('units.kg', { value: booking.weightKG }) : '—'} {booking.notes && ( {booking.notes} )} {t('documents', { count: booking.documents?.length ?? 0 })} {booking.documents?.length ? ( {booking.documents.map((document, index) => ( {document.fileName} {document.type} ))} ) : ( )} {price && ( {tList('detail.price')} {price} {booking.freightTotal ? ( {booking.freightTotal} {booking.freightCurrency} ) : null} {booking.fobTotal ? ( {booking.fobTotal} {booking.fobCurrency} ) : null} {booking.commissionAmountEur ? ( {booking.commissionAmountEur} € ) : null} )} {t('timeline')} {dateTime(booking.requestedAt)} {booking.respondedAt && ( {dateTime(booking.respondedAt)} )} {booking.id} {t('back')} > ); } function Field({ label, children }: { label: string; children: React.ReactNode }) { return ( {label} {children} ); }
{t('carrier', { carrier: booking.carrierName })}
{booking.notes}
{price}