feat(ui): action de suppression dans la liste des reservations
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BAUeCFpDkRD6tU5wGsc1C
This commit is contained in:
parent
efc47aae67
commit
b3564a19f6
@ -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')}
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
{/* Suppression reservee aux reservations impayees : une fois
|
||||
la commission reglee, la reservation est partie chez le
|
||||
transporteur et ne peut plus qu'etre annulee. */}
|
||||
<DropdownMenuItem
|
||||
onSelect={() => remove(b)}
|
||||
className="text-destructive focus:text-destructive"
|
||||
>
|
||||
<Trash2 />
|
||||
{t('actions.delete')}
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
@ -409,10 +458,7 @@ export default function BookingsListPage() {
|
||||
}}
|
||||
filters={
|
||||
<>
|
||||
<Select
|
||||
value={searchType}
|
||||
onValueChange={value => url.set('by', value)}
|
||||
>
|
||||
<Select value={searchType} onValueChange={value => url.set('by', value)}>
|
||||
<SelectTrigger className="w-full md:w-44" aria-label={t('searchType.label')}>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
@ -468,8 +514,7 @@ export default function BookingsListPage() {
|
||||
empty={{
|
||||
icon: Package,
|
||||
title: t('empty.title'),
|
||||
description:
|
||||
searchTerm || statusFilter ? t('empty.hasFilters') : t('empty.noBookings'),
|
||||
description: searchTerm || statusFilter ? t('empty.hasFilters') : t('empty.noBookings'),
|
||||
action: newReservationAction,
|
||||
}}
|
||||
caption={t('title')}
|
||||
|
||||
@ -532,7 +532,14 @@
|
||||
"actions": {
|
||||
"view": "View",
|
||||
"pay": "Pay",
|
||||
"edit": "Edit"
|
||||
"edit": "Edit",
|
||||
"delete": "Delete"
|
||||
},
|
||||
"deleteConfirm": {
|
||||
"title": "Delete this booking?",
|
||||
"body": "{reference} will be permanently deleted. This cannot be undone.",
|
||||
"done": "Booking deleted",
|
||||
"failed": "The booking could not be deleted"
|
||||
},
|
||||
"detail": {
|
||||
"title": "Booking",
|
||||
|
||||
@ -532,7 +532,14 @@
|
||||
"actions": {
|
||||
"view": "Voir",
|
||||
"pay": "Payer",
|
||||
"edit": "Modifier"
|
||||
"edit": "Modifier",
|
||||
"delete": "Supprimer"
|
||||
},
|
||||
"deleteConfirm": {
|
||||
"title": "Supprimer cette réservation ?",
|
||||
"body": "{reference} sera définitivement supprimée. Cette action est irréversible.",
|
||||
"done": "Réservation supprimée",
|
||||
"failed": "La réservation n’a pas pu être supprimée"
|
||||
},
|
||||
"detail": {
|
||||
"title": "Réservation",
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
* Endpoints for managing container bookings
|
||||
*/
|
||||
|
||||
import { get, post, patch, upload } from './client';
|
||||
import { get, post, patch, del, upload } from './client';
|
||||
import type {
|
||||
CreateBookingRequest,
|
||||
BookingResponse,
|
||||
@ -39,6 +39,8 @@ export interface UpdateBookingStatusRequest {
|
||||
export interface CsvBookingResponse {
|
||||
id: string;
|
||||
bookingId: string;
|
||||
/** Référence affichée, ex. « XPD-2026-R9KE8U ». */
|
||||
bookingNumber?: string;
|
||||
carrierName: string;
|
||||
carrierEmail: string;
|
||||
origin: string;
|
||||
@ -51,7 +53,13 @@ export interface CsvBookingResponse {
|
||||
primaryCurrency: string;
|
||||
transitDays: number;
|
||||
containerType: string;
|
||||
status: 'PENDING_PAYMENT' | 'PENDING_BANK_TRANSFER' | 'PENDING' | 'ACCEPTED' | 'REJECTED' | 'CANCELLED';
|
||||
status:
|
||||
| 'PENDING_PAYMENT'
|
||||
| 'PENDING_BANK_TRANSFER'
|
||||
| 'PENDING'
|
||||
| 'ACCEPTED'
|
||||
| 'REJECTED'
|
||||
| 'CANCELLED';
|
||||
documents: Array<{
|
||||
type: string;
|
||||
fileName: string;
|
||||
@ -67,6 +75,19 @@ export interface CsvBookingResponse {
|
||||
commissionRate?: number;
|
||||
commissionAmountEur?: number;
|
||||
options?: Record<string, boolean>;
|
||||
|
||||
// Champs renvoyes par l'API mais absents de ce type : la page de detail les
|
||||
// affichait sans que TypeScript puisse les verifier.
|
||||
requestedAt?: string;
|
||||
respondedAt?: string;
|
||||
rejectionReason?: string;
|
||||
routeDescription?: string;
|
||||
isExpired?: boolean;
|
||||
price?: number;
|
||||
freightTotal?: number;
|
||||
freightCurrency?: string;
|
||||
fobTotal?: number;
|
||||
fobCurrency?: string;
|
||||
}
|
||||
|
||||
export interface CommissionPaymentResponse {
|
||||
@ -279,6 +300,15 @@ export async function cancelCsvBooking(id: string): Promise<SuccessResponse> {
|
||||
return patch<SuccessResponse>(`/api/v1/csv-bookings/${id}/cancel`, {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime définitivement une réservation dont la commission n'a pas été payée.
|
||||
* Une réservation payée est partie chez le transporteur : elle s'annule.
|
||||
* DELETE /api/v1/csv-bookings/:id
|
||||
*/
|
||||
export async function deleteCsvBooking(id: string): Promise<SuccessResponse> {
|
||||
return del<SuccessResponse>(`/api/v1/csv-bookings/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update cargo details of a booking awaiting payment
|
||||
* PATCH /api/v1/csv-bookings/:id/details
|
||||
|
||||
@ -54,6 +54,7 @@ export {
|
||||
listCsvBookings,
|
||||
getCsvBookingStats,
|
||||
cancelCsvBooking,
|
||||
deleteCsvBooking,
|
||||
acceptCsvBooking,
|
||||
rejectCsvBooking,
|
||||
type CsvBookingResponse,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user