diff --git a/apps/backend/src/application/controllers/csv-bookings.controller.ts b/apps/backend/src/application/controllers/csv-bookings.controller.ts
index 3601f6d..a6b4721 100644
--- a/apps/backend/src/application/controllers/csv-bookings.controller.ts
+++ b/apps/backend/src/application/controllers/csv-bookings.controller.ts
@@ -48,6 +48,7 @@ import {
CsvBookingListResponseDto,
CsvBookingStatsDto,
UpdateCsvBookingDetailsDto,
+ UpdateCsvBookingRateDto,
} from '../dto/csv-booking.dto';
/**
@@ -592,6 +593,37 @@ export class CsvBookingsController {
return await this.csvBookingService.updateBookingDetails(id, userId, dto);
}
+ /**
+ * Re-apply a full rate selection before payment
+ *
+ * PATCH /api/v1/csv-bookings/:id/rate
+ */
+ @Patch(':id/rate')
+ @UseGuards(JwtAuthGuard)
+ @ApiBearerAuth()
+ @ApiOperation({
+ summary: 'Update booking rate/route before payment',
+ description:
+ 'Re-apply a rate selection (carrier, route, container, transit, cargo, price) to a PENDING_PAYMENT booking. Only the owner can edit.',
+ })
+ @ApiParam({ name: 'id', description: 'Booking ID (UUID)' })
+ @ApiResponse({
+ status: 200,
+ description: 'Booking rate updated successfully',
+ type: CsvBookingResponseDto,
+ })
+ @ApiResponse({ status: 400, description: 'Booking cannot be edited (invalid status or values)' })
+ @ApiResponse({ status: 401, description: 'Unauthorized' })
+ @ApiResponse({ status: 404, description: 'Booking not found' })
+ async updateBookingRate(
+ @Param('id') id: string,
+ @Body() dto: UpdateCsvBookingRateDto,
+ @Request() req: any
+ ): Promise {
+ const userId = req.user.id;
+ return await this.csvBookingService.updateBookingRate(id, userId, dto);
+ }
+
/**
* Add documents to an existing booking
*
diff --git a/apps/backend/src/application/dto/csv-booking.dto.ts b/apps/backend/src/application/dto/csv-booking.dto.ts
index 5d4137d..d553dbf 100644
--- a/apps/backend/src/application/dto/csv-booking.dto.ts
+++ b/apps/backend/src/application/dto/csv-booking.dto.ts
@@ -249,6 +249,105 @@ export class UpdateCsvBookingDetailsDto {
fobCurrency?: string;
}
+/**
+ * Update CSV Booking Rate DTO
+ *
+ * Full re-selection of a rate before payment (carrier + route + container +
+ * transit + cargo + price). Used when the user re-runs the search and picks a
+ * (possibly different) rate for a PENDING_PAYMENT booking.
+ */
+export class UpdateCsvBookingRateDto {
+ @ApiProperty({ example: 'SSC Consolidation' })
+ @IsString()
+ @MinLength(2)
+ @MaxLength(200)
+ carrierName: string;
+
+ @ApiProperty({ example: 'bookings@example.com' })
+ @IsEmail()
+ carrierEmail: string;
+
+ @ApiProperty({ example: 'FRLIO' })
+ @IsString()
+ @MaxLength(5)
+ origin: string;
+
+ @ApiProperty({ example: 'AUADL' })
+ @IsString()
+ @MaxLength(5)
+ destination: string;
+
+ @ApiProperty({ example: 'LCL' })
+ @IsString()
+ @MaxLength(50)
+ containerType: string;
+
+ @ApiProperty({ example: 46 })
+ @IsNumber()
+ @Min(1)
+ transitDays: number;
+
+ @ApiProperty({ example: 12.5 })
+ @IsNumber()
+ @Min(0.01)
+ volumeCBM: number;
+
+ @ApiProperty({ example: 1500 })
+ @IsNumber()
+ @Min(0.01)
+ weightKG: number;
+
+ @ApiProperty({ example: 5 })
+ @IsNumber()
+ @Min(0)
+ palletCount: number;
+
+ @ApiProperty({ example: 0 })
+ @IsNumber()
+ @Min(0)
+ priceUSD: number;
+
+ @ApiProperty({ example: 258 })
+ @IsNumber()
+ @Min(0)
+ priceEUR: number;
+
+ @ApiProperty({ example: 'EUR' })
+ @IsString()
+ @MaxLength(3)
+ primaryCurrency: string;
+
+ @ApiPropertyOptional({ example: 150 })
+ @IsOptional()
+ @IsNumber()
+ @Min(0)
+ freightTotal?: number;
+
+ @ApiPropertyOptional({ example: 'EUR' })
+ @IsOptional()
+ @IsString()
+ @MaxLength(3)
+ freightCurrency?: string;
+
+ @ApiPropertyOptional({ example: 108 })
+ @IsOptional()
+ @IsNumber()
+ @Min(0)
+ fobTotal?: number;
+
+ @ApiPropertyOptional({ example: 'EUR' })
+ @IsOptional()
+ @IsString()
+ @MaxLength(3)
+ fobCurrency?: string;
+
+ @ApiPropertyOptional({ example: 'Handle with care' })
+ @IsOptional()
+ @IsString()
+ @MaxLength(2000)
+ notes?: string;
+}
+
/**
* Document DTO for response
*/
diff --git a/apps/backend/src/application/services/csv-booking.service.ts b/apps/backend/src/application/services/csv-booking.service.ts
index 84e4805..f4abcd6 100644
--- a/apps/backend/src/application/services/csv-booking.service.ts
+++ b/apps/backend/src/application/services/csv-booking.service.ts
@@ -32,6 +32,7 @@ import {
CsvBookingListResponseDto,
CsvBookingStatsDto,
UpdateCsvBookingDetailsDto,
+ UpdateCsvBookingRateDto,
} from '../dto/csv-booking.dto';
import { CarrierDocumentsResponseDto } from '../dto/carrier-documents.dto';
import { SubscriptionService } from './subscription.service';
@@ -1074,6 +1075,60 @@ export class CsvBookingService {
return this.toResponseDto(updatedBooking);
}
+ /**
+ * Re-apply a full rate selection to a booking before payment (user action).
+ *
+ * Used when the user re-runs the search and picks a (possibly different) rate:
+ * carrier, route, container, transit, cargo and price are all replaced. Only
+ * the owner can edit, and only while the booking is PENDING_PAYMENT.
+ */
+ async updateBookingRate(
+ id: string,
+ userId: string,
+ dto: UpdateCsvBookingRateDto
+ ): Promise {
+ this.logger.log(`Updating booking rate ${id} by user ${userId}`);
+
+ const booking = await this.csvBookingRepository.findById(id);
+
+ if (!booking) {
+ throw new NotFoundException('Booking not found');
+ }
+
+ if (booking.userId !== userId) {
+ throw new NotFoundException('Booking not found');
+ }
+
+ try {
+ booking.editFromRate({
+ carrierName: dto.carrierName,
+ carrierEmail: dto.carrierEmail,
+ origin: PortCode.create(dto.origin),
+ destination: PortCode.create(dto.destination),
+ containerType: dto.containerType,
+ transitDays: dto.transitDays,
+ volumeCBM: dto.volumeCBM,
+ weightKG: dto.weightKG,
+ palletCount: dto.palletCount,
+ priceUSD: dto.priceUSD,
+ priceEUR: dto.priceEUR,
+ primaryCurrency: dto.primaryCurrency,
+ freightTotal: dto.freightTotal,
+ freightCurrency: dto.freightCurrency,
+ fobTotal: dto.fobTotal,
+ fobCurrency: dto.fobCurrency,
+ notes: dto.notes,
+ });
+ } catch (err) {
+ throw new BadRequestException(err instanceof Error ? err.message : 'Invalid booking rate');
+ }
+
+ const updatedBooking = await this.csvBookingRepository.update(booking);
+ this.logger.log(`Booking ${id} rate updated`);
+
+ return this.toResponseDto(updatedBooking);
+ }
+
/**
* Get bookings for a user (paginated)
*/
diff --git a/apps/backend/src/domain/entities/csv-booking.entity.ts b/apps/backend/src/domain/entities/csv-booking.entity.ts
index d37b6f4..d85e6dc 100644
--- a/apps/backend/src/domain/entities/csv-booking.entity.ts
+++ b/apps/backend/src/domain/entities/csv-booking.entity.ts
@@ -63,10 +63,12 @@ export class CsvBooking {
public readonly id: string,
public readonly userId: string,
public readonly organizationId: string,
- public readonly carrierName: string,
- public readonly carrierEmail: string,
- public readonly origin: PortCode,
- public readonly destination: PortCode,
+ // Carrier + route + transit + container — editable before payment when the
+ // user re-runs the search and picks another rate (see editFromRate).
+ public carrierName: string,
+ public carrierEmail: string,
+ public origin: PortCode,
+ public destination: PortCode,
// Cargo characteristics — editable before payment (see editDetails).
public volumeCBM: number,
public weightKG: number,
@@ -75,8 +77,8 @@ export class CsvBooking {
public priceUSD: number,
public priceEUR: number,
public primaryCurrency: string,
- public readonly transitDays: number,
- public readonly containerType: string,
+ public transitDays: number,
+ public containerType: string,
public status: CsvBookingStatus,
public readonly documents: CsvBookingDocument[],
public readonly confirmationToken: string,
@@ -367,6 +369,69 @@ export class CsvBooking {
}
}
+ /**
+ * Re-apply a full rate selection before payment: the user re-ran the search
+ * and picked a rate, so carrier, route, container, transit, cargo and price
+ * are all replaced. Only allowed while the booking is PENDING_PAYMENT.
+ *
+ * @throws Error if the booking is not PENDING_PAYMENT or values are invalid
+ */
+ editFromRate(data: {
+ carrierName: string;
+ carrierEmail: string;
+ origin: PortCode;
+ destination: PortCode;
+ containerType: string;
+ transitDays: number;
+ volumeCBM: number;
+ weightKG: number;
+ palletCount: number;
+ priceUSD: number;
+ priceEUR: number;
+ primaryCurrency: string;
+ freightTotal?: number;
+ freightCurrency?: string;
+ fobTotal?: number;
+ fobCurrency?: string;
+ notes?: string;
+ }): void {
+ if (this.status !== CsvBookingStatus.PENDING_PAYMENT) {
+ throw new Error(
+ `Cannot edit booking with status ${this.status}. Only PENDING_PAYMENT bookings can be edited.`
+ );
+ }
+ if (!data.carrierName || data.carrierName.trim().length === 0) {
+ throw new Error('Carrier name is required');
+ }
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+ if (!data.carrierEmail || !emailRegex.test(data.carrierEmail)) {
+ throw new Error('Invalid carrier email format');
+ }
+ if (data.volumeCBM <= 0) throw new Error('Volume must be positive');
+ if (data.weightKG <= 0) throw new Error('Weight must be positive');
+ if (data.palletCount < 0) throw new Error('Pallet count cannot be negative');
+ if (data.transitDays <= 0) throw new Error('Transit days must be positive');
+ if (data.priceUSD < 0 || data.priceEUR < 0) throw new Error('Price cannot be negative');
+
+ this.carrierName = data.carrierName;
+ this.carrierEmail = data.carrierEmail;
+ this.origin = data.origin;
+ this.destination = data.destination;
+ this.containerType = data.containerType;
+ this.transitDays = data.transitDays;
+ this.volumeCBM = data.volumeCBM;
+ this.weightKG = data.weightKG;
+ this.palletCount = data.palletCount;
+ this.priceUSD = data.priceUSD;
+ this.priceEUR = data.priceEUR;
+ this.primaryCurrency = data.primaryCurrency;
+ this.freightTotal = data.freightTotal;
+ this.freightCurrency = data.freightCurrency;
+ this.fobTotal = data.fobTotal;
+ this.fobCurrency = data.fobCurrency;
+ if (data.notes !== undefined) this.notes = data.notes;
+ }
+
/**
* Check if booking has expired (7 days without response)
*
diff --git a/apps/frontend/app/[locale]/dashboard/booking/[id]/edit/page.tsx b/apps/frontend/app/[locale]/dashboard/booking/[id]/edit/page.tsx
deleted file mode 100644
index af6baea..0000000
--- a/apps/frontend/app/[locale]/dashboard/booking/[id]/edit/page.tsx
+++ /dev/null
@@ -1,593 +0,0 @@
-/**
- * Edit Booking Page (before payment)
- *
- * Lets the owner adjust cargo details (volume, weight, pallets, notes) and manage
- * documents of a booking that is still awaiting payment (PENDING_PAYMENT). When the
- * cargo changes, the price is recomputed by re-quoting the same rate (carrier +
- * container + transit) for the new volume/weight. Then they can continue to payment.
- */
-
-'use client';
-
-import { useState, useEffect, useRef } from 'react';
-import { useRouter, useParams } from 'next/navigation';
-import {
- ArrowLeft,
- Loader2,
- AlertTriangle,
- Save,
- CreditCard,
- FileText,
- Trash2,
- Upload,
- Package,
- RefreshCw,
-} from 'lucide-react';
-import { getCsvBooking, updateCsvBookingDetails } from '@/lib/api/bookings';
-import { searchCsvRatesWithOffers } from '@/lib/api/rates';
-import { upload, del } from '@/lib/api/client';
-
-interface BookingDoc {
- id?: string;
- type: string;
- fileName: string;
- url: string;
-}
-
-interface BookingData {
- id: string;
- bookingNumber?: string;
- carrierName: string;
- origin: string;
- destination: string;
- volumeCBM: number;
- weightKG: number;
- palletCount: number;
- containerType: string;
- transitDays: number;
- status: string;
- notes?: string;
- primaryCurrency?: string;
- priceUSD?: number;
- priceEUR?: number;
- freightTotal?: number;
- freightCurrency?: string;
- fobTotal?: number;
- fobCurrency?: string;
- documents: BookingDoc[];
-}
-
-interface PricingState {
- freightTotal: number;
- freightCurrency: string;
- fobTotal: number;
- fobCurrency: string;
- primaryCurrency: string;
- priceUSD: number;
- priceEUR: number;
-}
-
-function formatPrice(price: number, currency: string) {
- return new Intl.NumberFormat('fr-FR', { style: 'currency', currency }).format(price || 0);
-}
-
-export default function EditBookingPage() {
- const router = useRouter();
- const params = useParams();
- const bookingId = params.id as string;
-
- const [booking, setBooking] = useState(null);
- const [loading, setLoading] = useState(true);
- const [saving, setSaving] = useState(false);
- const [error, setError] = useState(null);
- const [success, setSuccess] = useState(null);
-
- const [volumeCBM, setVolumeCBM] = useState('');
- const [weightKG, setWeightKG] = useState('');
- const [palletCount, setPalletCount] = useState('');
- const [notes, setNotes] = useState('');
-
- const [pricing, setPricing] = useState(null);
- const [repricing, setRepricing] = useState(false);
- const [priceWarning, setPriceWarning] = useState(null);
-
- const [uploadingDocs, setUploadingDocs] = useState(false);
- const [deletingDocId, setDeletingDocId] = useState(null);
- const fileInputRef = useRef(null);
-
- const applyBooking = (data: BookingData) => {
- setBooking(data);
- setVolumeCBM(String(data.volumeCBM ?? ''));
- setWeightKG(String(data.weightKG ?? ''));
- setPalletCount(String(data.palletCount ?? ''));
- setNotes(data.notes ?? '');
- setPricing({
- freightTotal: data.freightTotal ?? 0,
- freightCurrency: data.freightCurrency ?? data.primaryCurrency ?? 'USD',
- fobTotal: data.fobTotal ?? 0,
- fobCurrency: data.fobCurrency ?? 'EUR',
- primaryCurrency: data.primaryCurrency ?? 'USD',
- priceUSD: data.priceUSD ?? 0,
- priceEUR: data.priceEUR ?? 0,
- });
- };
-
- const loadBooking = async () => {
- try {
- const data = (await getCsvBooking(bookingId)) as any as BookingData;
- if (data.status !== 'PENDING_PAYMENT') {
- // Only bookings awaiting payment can be edited.
- router.replace('/dashboard/bookings');
- return;
- }
- applyBooking(data);
- } catch (err) {
- setError('Impossible de charger la réservation');
- } finally {
- setLoading(false);
- }
- };
-
- useEffect(() => {
- if (bookingId) loadBooking();
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [bookingId]);
-
- // Recompute the price by re-quoting the same rate for the new volume/weight.
- // Returns the new pricing, or null if no matching rate could be found.
- const recomputePricing = async (vol: number, wgt: number): Promise => {
- if (!booking) return null;
- const response = await searchCsvRatesWithOffers({
- origin: booking.origin,
- destination: booking.destination,
- volumeCBM: vol,
- weightKG: wgt,
- hasDangerousGoods: false,
- });
- const results = response.results || [];
-
- // The rate search returns the carrier as a slug (e.g. "ssc-consolidation")
- // while the booking stores the display name ("SSC Consolidation"), so carriers
- // are compared on a normalized form. Container + transit uniquely identify the
- // originally chosen offer for the route, and are the primary match keys.
- const norm = (s: string) => (s || '').toLowerCase().replace(/[^a-z0-9]/g, '');
- const carrier = norm(booking.carrierName);
- const sameContainer = (r: any) => r.containerType === booking.containerType;
- const sameTransit = (r: any) =>
- (r.adjustedTransitDays ?? r.transitDays) === booking.transitDays;
- const sameCarrier = (r: any) => norm(r.companyName) === carrier;
-
- const match =
- results.find((r: any) => sameCarrier(r) && sameContainer(r) && sameTransit(r)) ||
- results.find((r: any) => sameContainer(r) && sameTransit(r)) ||
- results.find((r: any) => sameCarrier(r) && sameContainer(r)) ||
- results.find((r: any) => sameCarrier(r)) ||
- results.find((r: any) => sameContainer(r)) ||
- null;
-
- if (!match) return null;
-
- const pb = match.priceBreakdown;
- const freightTotal = pb.totalFreight;
- const freightCurrency = pb.freightCurrency;
- const fobTotal = pb.totalFob;
- const fobCurrency = pb.fobCurrency;
- return {
- freightTotal,
- freightCurrency,
- fobTotal,
- fobCurrency,
- primaryCurrency: pb.primaryCurrency || 'USD',
- priceUSD:
- (freightCurrency === 'USD' ? freightTotal : 0) + (fobCurrency === 'USD' ? fobTotal : 0),
- priceEUR:
- (freightCurrency === 'EUR' ? freightTotal : 0) + (fobCurrency === 'EUR' ? fobTotal : 0),
- };
- };
-
- // Debounced auto-recompute when volume/weight change (price depends on both).
- useEffect(() => {
- if (!booking) return;
- const vol = parseFloat(volumeCBM);
- const wgt = parseFloat(weightKG);
- if (!Number.isFinite(vol) || vol <= 0 || !Number.isFinite(wgt) || wgt <= 0) return;
-
- const handle = setTimeout(async () => {
- setRepricing(true);
- setPriceWarning(null);
- try {
- const next = await recomputePricing(vol, wgt);
- if (next) {
- setPricing(next);
- } else {
- setPriceWarning(
- 'Tarif introuvable pour cette route — le prix affiché reste celui d’origine.'
- );
- }
- } catch {
- setPriceWarning('Impossible de recalculer le prix pour le moment.');
- } finally {
- setRepricing(false);
- }
- }, 600);
-
- return () => clearTimeout(handle);
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [volumeCBM, weightKG, booking]);
-
- const persistDetails = async (): Promise => {
- setError(null);
- const vol = parseFloat(volumeCBM);
- const wgt = parseFloat(weightKG);
- const plt = parseInt(palletCount, 10);
-
- if (!Number.isFinite(vol) || vol <= 0) {
- setError('Le volume doit être un nombre positif.');
- return false;
- }
- if (!Number.isFinite(wgt) || wgt <= 0) {
- setError('Le poids doit être un nombre positif.');
- return false;
- }
- if (!Number.isFinite(plt) || plt < 0) {
- setError('Le nombre de palettes est invalide.');
- return false;
- }
-
- const updated = (await updateCsvBookingDetails(bookingId, {
- volumeCBM: vol,
- weightKG: wgt,
- palletCount: plt,
- notes: notes.trim() || undefined,
- ...(pricing
- ? {
- priceUSD: pricing.priceUSD,
- priceEUR: pricing.priceEUR,
- primaryCurrency: pricing.primaryCurrency,
- freightTotal: pricing.freightTotal,
- freightCurrency: pricing.freightCurrency,
- fobTotal: pricing.fobTotal,
- fobCurrency: pricing.fobCurrency,
- }
- : {}),
- })) as any as BookingData;
- applyBooking(updated);
- return true;
- };
-
- const handleSave = async () => {
- setSaving(true);
- setSuccess(null);
- try {
- const ok = await persistDetails();
- if (ok) setSuccess('Modifications enregistrées.');
- } catch (err) {
- setError(err instanceof Error ? err.message : "Erreur lors de l'enregistrement");
- } finally {
- setSaving(false);
- }
- };
-
- const handleContinueToPayment = async () => {
- setSaving(true);
- setSuccess(null);
- try {
- const ok = await persistDetails();
- if (ok) router.push(`/dashboard/booking/${bookingId}/pay`);
- } catch (err) {
- setError(err instanceof Error ? err.message : "Erreur lors de l'enregistrement");
- } finally {
- setSaving(false);
- }
- };
-
- const handleAddDocuments = async (files: FileList | null) => {
- if (!files || files.length === 0) return;
- setUploadingDocs(true);
- setError(null);
- try {
- const formData = new FormData();
- Array.from(files).forEach(file => formData.append('documents', file));
- await upload(`/api/v1/csv-bookings/${bookingId}/documents`, formData);
- await loadBooking();
- } catch (err) {
- setError(err instanceof Error ? err.message : "Erreur lors de l'ajout des documents");
- } finally {
- setUploadingDocs(false);
- if (fileInputRef.current) fileInputRef.current.value = '';
- }
- };
-
- const handleDeleteDocument = async (docId?: string) => {
- if (!docId) return;
- if (!confirm('Supprimer ce document ?')) return;
- setDeletingDocId(docId);
- setError(null);
- try {
- await del(`/api/v1/csv-bookings/${bookingId}/documents/${docId}`);
- await loadBooking();
- } catch (err) {
- setError(err instanceof Error ? err.message : 'Erreur lors de la suppression du document');
- } finally {
- setDeletingDocId(null);
- }
- };
-
- if (loading) {
- return (
-
- );
- }
-
- if (!booking) {
- return (
-
-
-
-
{error || 'Réservation introuvable'}
-
-
-
- );
- }
-
- const transportTotalUSD = (pricing?.priceUSD ?? 0) + (pricing?.priceEUR ?? 0);
-
- return (
-
-
-
-
-
Modifier la réservation
-
- Corrigez les informations de votre réservation avant de procéder au paiement. Le prix est
- recalculé automatiquement.
-
-
- {error && (
-
- )}
- {success && (
-
- )}
-
- {/* Read-only rate summary */}
-
-
- Réservation
-
-
-
-
Transporteur
-
{booking.carrierName}
-
-
-
Trajet
-
- {booking.origin} → {booking.destination}
-
-
-
-
Type
-
{booking.containerType}
-
- {booking.bookingNumber && (
-
-
Numéro
-
{booking.bookingNumber}
-
- )}
-
-
- Le transporteur et le trajet proviennent du tarif sélectionné et ne sont pas modifiables
- ici.
-
-
-
- {/* Editable cargo details */}
-
-
-
- Caractéristiques de la marchandise
-
-
-
-
-
-
-
- {/* Price summary (recomputed) */}
-
-
-
- Prix estimé
-
- {repricing && (
-
-
- Recalcul...
-
- )}
-
- {priceWarning && (
-
- {priceWarning}
-
- )}
- {pricing && (
-
-
-
Fret
-
- {formatPrice(pricing.freightTotal, pricing.freightCurrency)}
-
-
-
-
Frais FOB
-
- {formatPrice(pricing.fobTotal, pricing.fobCurrency)}
-
-
-
-
Total transport (USD)
-
{formatPrice(transportTotalUSD, 'USD')}
-
-
- )}
-
- Fret et frais FOB sont réglés directement au transporteur. Le forfait Xpeditis reste
- inchangé.
-
-
-
- {/* Documents */}
-
-
-
-
- Documents
-
-
- handleAddDocuments(e.target.files)}
- />
-
- {booking.documents.length === 0 ? (
-
Aucun document.
- ) : (
-
- {booking.documents.map((doc, idx) => (
- -
-
-
- {doc.fileName}
-
- {doc.id && (
-
- )}
-
- ))}
-
- )}
-
-
- {/* Actions */}
-
-
-
-
-
-
- );
-}
diff --git a/apps/frontend/app/[locale]/dashboard/booking/[id]/pay/page.tsx b/apps/frontend/app/[locale]/dashboard/booking/[id]/pay/page.tsx
index 4beacc7..01ebbea 100644
--- a/apps/frontend/app/[locale]/dashboard/booking/[id]/pay/page.tsx
+++ b/apps/frontend/app/[locale]/dashboard/booking/[id]/pay/page.tsx
@@ -227,7 +227,18 @@ export default function PayCommissionPage() {
Finalisez votre booking en réglant le forfait par booking