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() {