diff --git a/apps/frontend/app/[locale]/dashboard/bookings/page.tsx b/apps/frontend/app/[locale]/dashboard/bookings/page.tsx
index 40b86b8..4cdfd08 100644
--- a/apps/frontend/app/[locale]/dashboard/bookings/page.tsx
+++ b/apps/frontend/app/[locale]/dashboard/bookings/page.tsx
@@ -5,9 +5,19 @@ import { useQuery } from '@tanstack/react-query';
import { useSearchParams } from 'next/navigation';
import { useLocale, useTranslations } from 'next-intl';
import type { ColumnDef } from '@tanstack/react-table';
-import { CreditCard, Eye, MoreVertical, Package, Pencil, Plus, Sparkles, X } from 'lucide-react';
+import {
+ CreditCard,
+ Eye,
+ MoreVertical,
+ Package,
+ Pencil,
+ Plus,
+ Sparkles,
+ Trash2,
+ X,
+} from 'lucide-react';
-import { listCsvBookings } from '@/lib/api';
+import { deleteCsvBooking, listCsvBookings } from '@/lib/api';
import { Link } from '@/i18n/navigation';
import { PageHeader } from '@/components/ui/PageHeader';
import { Button } from '@/components/ui/button';
@@ -33,6 +43,8 @@ import {
BookingStatusBadge,
useBookingStatusLabel,
} from '@/components/bookings/BookingStatusBadge';
+import { useConfirm } from '@/components/ui/use-confirm';
+import { useToast } from '@/components/ui/toast';
import ExportButton from '@/components/ExportButton';
import { useReservationQuota } from '@/hooks/useReservationQuota';
import { useUrlState } from '@/hooks/use-url-state';
@@ -53,6 +65,8 @@ export default function BookingsListPage() {
const searchParams = useSearchParams();
const url = useUrlState();
const statusLabel = useBookingStatusLabel();
+ const confirm = useConfirm();
+ const { toast } = useToast();
// L'etat de vue vit dans l'URL : la liste est partageable et survit au
// rechargement, ce qui n'etait pas le cas avec un etat purement local.
@@ -80,6 +94,31 @@ export default function BookingsListPage() {
const quota = useReservationQuota();
+ /**
+ * Supprime une reservation impayee, apres confirmation.
+ *
+ * L'action est destructrice et sans annulation : la confirmation nomme la
+ * reservation concernee plutot que de demander « etes-vous sur ? ».
+ */
+ const remove = async (b: any) => {
+ const reference = b.bookingNumber || b.quoteNumber || `${b.origin} → ${b.destination}`;
+ const confirmed = await confirm({
+ title: t('deleteConfirm.title'),
+ description: t('deleteConfirm.body', { reference }),
+ confirmLabel: t('actions.delete'),
+ destructive: true,
+ });
+ if (!confirmed) return;
+
+ try {
+ await deleteCsvBooking(b.id);
+ toast.success(t('deleteConfirm.done'));
+ await refetch();
+ } catch {
+ toast.error(t('deleteConfirm.failed'));
+ }
+ };
+
// « Modifier » rouvre le parcours existant (recherche → transporteur →
// paiement) pre-rempli, afin qu'un changement redonne lieu a une cotation.
const buildEditUrl = (b: any) => {
@@ -291,6 +330,16 @@ export default function BookingsListPage() {
{t('actions.pay')}
+ {/* Suppression reservee aux reservations impayees : une fois
+ la commission reglee, la reservation est partie chez le
+ transporteur et ne peut plus qu'etre annulee. */}
+ remove(b)}
+ className="text-destructive focus:text-destructive"
+ >
+
+ {t('actions.delete')}
+
>
)}
@@ -409,10 +458,7 @@ export default function BookingsListPage() {
}}
filters={
<>
-