diff --git a/apps/frontend/app/[locale]/dashboard/bookings/page.tsx b/apps/frontend/app/[locale]/dashboard/bookings/page.tsx
index 025f56a..c3ae296 100644
--- a/apps/frontend/app/[locale]/dashboard/bookings/page.tsx
+++ b/apps/frontend/app/[locale]/dashboard/bookings/page.tsx
@@ -4,7 +4,7 @@ import { useState, useEffect } from 'react';
import { useQuery } from '@tanstack/react-query';
import { listCsvBookings } from '@/lib/api';
import { Link } from '@/i18n/navigation';
-import { Plus, Clock, Eye, X } from 'lucide-react';
+import { Plus, Clock, Eye, X, CreditCard } from 'lucide-react';
import ExportButton from '@/components/ExportButton';
import { useSearchParams } from 'next/navigation';
import { PageHeader } from '@/components/ui/PageHeader';
@@ -350,13 +350,24 @@ export default function BookingsListPage() {
})
: t('mobile.booking', { number: booking.bookingNumber || '-' })}
-
+
+ {booking.status === 'PENDING_PAYMENT' && (
+
+
+ {t('actions.pay')}
+
+ )}
+
+
))}
@@ -465,13 +476,24 @@ export default function BookingsListPage() {
{booking.bookingNumber || '-'}
-
+
+ {booking.status === 'PENDING_PAYMENT' && (
+
+
+ {t('actions.pay')}
+
+ )}
+
+
|
))}
@@ -719,15 +741,16 @@ export default function BookingsListPage() {
{selectedBooking.status === 'PENDING_PAYMENT' && (
-
+
{t('detail.pendingPaymentNotice')}
setSelectedBooking(null)}
+ className="mt-3 inline-flex items-center gap-2 px-4 py-2 bg-orange-500 text-white text-sm font-medium rounded-lg hover:bg-orange-600 transition-colors"
>
-
- {t('detail.newBooking')}
+
+ {t('detail.payNow')}
)}
diff --git a/apps/frontend/app/[locale]/dashboard/documents/page.tsx b/apps/frontend/app/[locale]/dashboard/documents/page.tsx
index bff453a..cf17ae5 100644
--- a/apps/frontend/app/[locale]/dashboard/documents/page.tsx
+++ b/apps/frontend/app/[locale]/dashboard/documents/page.tsx
@@ -46,12 +46,14 @@ export default function UserDocumentsPage() {
const [selectedBookingId, setSelectedBookingId] = useState(null);
const [uploadingFiles, setUploadingFiles] = useState(false);
const [isDraggingAdd, setIsDraggingAdd] = useState(false);
+ const [addFiles, setAddFiles] = useState([]);
const fileInputRef = useRef(null);
const [showReplaceModal, setShowReplaceModal] = useState(false);
const [documentToReplace, setDocumentToReplace] = useState(null);
const [replacingFile, setReplacingFile] = useState(false);
const [isDraggingReplace, setIsDraggingReplace] = useState(false);
+ const [replaceFile, setReplaceFile] = useState(null);
const replaceFileInputRef = useRef(null);
const [openDropdownId, setOpenDropdownId] = useState(null);
@@ -189,6 +191,8 @@ export default function UserDocumentsPage() {
const getStatusColor = (status: string) => {
const colors: Record = {
+ PENDING_PAYMENT: 'bg-orange-100 text-orange-800',
+ PENDING_BANK_TRANSFER: 'bg-blue-100 text-blue-800',
PENDING: 'bg-yellow-100 text-yellow-800',
ACCEPTED: 'bg-green-100 text-green-800',
REJECTED: 'bg-red-100 text-red-800',
@@ -198,8 +202,15 @@ export default function UserDocumentsPage() {
};
const getStatusLabel = (status: string) => {
- const key = status as 'PENDING' | 'ACCEPTED' | 'REJECTED' | 'CANCELLED';
- return t(`statuses.${key}` as any) || status;
+ const labels: Record = {
+ PENDING_PAYMENT: t('statuses.PENDING_PAYMENT' as any),
+ PENDING_BANK_TRANSFER: t('statuses.PENDING_BANK_TRANSFER' as any),
+ PENDING: t('statuses.PENDING' as any),
+ ACCEPTED: t('statuses.ACCEPTED' as any),
+ REJECTED: t('statuses.REJECTED' as any),
+ CANCELLED: t('statuses.CANCELLED' as any),
+ };
+ return labels[status] || status;
};
const handleDownload = async (url: string, fileName: string) => {
@@ -251,11 +262,12 @@ export default function UserDocumentsPage() {
const handleCloseModal = () => {
setShowAddModal(false);
setSelectedBookingId(null);
+ setAddFiles([]);
if (fileInputRef.current) fileInputRef.current.value = '';
};
const handleFileUpload = async () => {
- if (!selectedBookingId || !fileInputRef.current?.files?.length) {
+ if (!selectedBookingId || addFiles.length === 0) {
alert(t('addDocument.noBookingError'));
return;
}
@@ -263,10 +275,7 @@ export default function UserDocumentsPage() {
setUploadingFiles(true);
try {
const formData = new FormData();
- const files = fileInputRef.current.files;
- for (let i = 0; i < files.length; i++) {
- formData.append('documents', files[i]);
- }
+ addFiles.forEach(file => formData.append('documents', file));
const token = localStorage.getItem('access_token');
const response = await fetch(
@@ -274,7 +283,10 @@ export default function UserDocumentsPage() {
{ method: 'POST', headers: { Authorization: `Bearer ${token}` }, body: formData }
);
- if (!response.ok) throw new Error(t('addDocument.errorMessage'));
+ if (!response.ok) {
+ const errorData = await response.json().catch(() => ({}));
+ throw new Error(errorData.message || t('addDocument.errorMessage'));
+ }
alert(t('addDocument.successMessage'));
handleCloseModal();
@@ -308,11 +320,12 @@ export default function UserDocumentsPage() {
const handleCloseReplaceModal = () => {
setShowReplaceModal(false);
setDocumentToReplace(null);
+ setReplaceFile(null);
if (replaceFileInputRef.current) replaceFileInputRef.current.value = '';
};
const handleReplaceDocument = async () => {
- if (!documentToReplace || !replaceFileInputRef.current?.files?.length) {
+ if (!documentToReplace || !replaceFile) {
alert(t('replaceDocument.noFileError'));
return;
}
@@ -320,7 +333,7 @@ export default function UserDocumentsPage() {
setReplacingFile(true);
try {
const formData = new FormData();
- formData.append('document', replaceFileInputRef.current.files[0]);
+ formData.append('document', replaceFile);
const token = localStorage.getItem('access_token');
const response = await fetch(
@@ -768,11 +781,7 @@ export default function UserDocumentsPage() {
{bookingsAvailableForDocuments.map(booking => (
))}
@@ -795,11 +804,8 @@ export default function UserDocumentsPage() {
onDrop={e => {
e.preventDefault();
setIsDraggingAdd(false);
- if (fileInputRef.current && e.dataTransfer.files.length > 0) {
- const dt = new DataTransfer();
- Array.from(e.dataTransfer.files).forEach(f => dt.items.add(f));
- fileInputRef.current.files = dt.files;
- }
+ const dropped = Array.from(e.dataTransfer.files);
+ if (dropped.length > 0) setAddFiles(dropped);
}}
onClick={() => fileInputRef.current?.click()}
>
@@ -816,11 +822,19 @@ export default function UserDocumentsPage() {
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
/>
-
- {isDraggingAdd
- ? t('addDocument.dropHere')
- : t('addDocument.dragOrClick')}
-
+ {addFiles.length > 0 ? (
+
+ {addFiles.length === 1
+ ? addFiles[0].name
+ : `${addFiles.length} fichiers sélectionnés`}
+
+ ) : (
+
+ {isDraggingAdd
+ ? t('addDocument.dropHere')
+ : t('addDocument.dragOrClick')}
+
+ )}
{t('addDocument.acceptedFormats')}
{
- // Force re-render to show selected file names
- setIsDraggingAdd(false);
+ onChange={e => {
+ const selected = Array.from(e.target.files || []);
+ if (selected.length > 0) setAddFiles(selected);
}}
/>
@@ -843,7 +857,7 @@ export default function UserDocumentsPage() {