fix: resolve payment access, status labels, and document upload reliability
- Add PENDING_PAYMENT and PENDING_BANK_TRANSFER to document statuses translations (was displaying raw i18n key instead of human label) - Fix getStatusLabel/getStatusColor to handle all booking statuses - Fix select dropdown in Add Document modal to use getStatusLabel() instead of hardcoded PENDING/ACCEPTED binary - Add direct "Payer" button (orange) on PENDING_PAYMENT rows in both desktop table and mobile cards, linking to /dashboard/booking/[id]/pay - Update detail modal to show payment CTA for PENDING_PAYMENT bookings - Refactor file handling in document modals to use React state (addFiles, replaceFile) instead of reading from DOM ref — fixes upload reliability and shows selected filename feedback in the dropzone Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9950d96fd6
commit
b344650608
@ -4,7 +4,7 @@ import { useState, useEffect } from 'react';
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { listCsvBookings } from '@/lib/api';
|
import { listCsvBookings } from '@/lib/api';
|
||||||
import { Link } from '@/i18n/navigation';
|
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 ExportButton from '@/components/ExportButton';
|
||||||
import { useSearchParams } from 'next/navigation';
|
import { useSearchParams } from 'next/navigation';
|
||||||
import { PageHeader } from '@/components/ui/PageHeader';
|
import { PageHeader } from '@/components/ui/PageHeader';
|
||||||
@ -350,6 +350,16 @@ export default function BookingsListPage() {
|
|||||||
})
|
})
|
||||||
: t('mobile.booking', { number: booking.bookingNumber || '-' })}
|
: t('mobile.booking', { number: booking.bookingNumber || '-' })}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{booking.status === 'PENDING_PAYMENT' && (
|
||||||
|
<Link
|
||||||
|
href={`/dashboard/booking/${booking.id}/pay`}
|
||||||
|
className="inline-flex items-center gap-1 px-3 py-1.5 text-xs font-medium text-white bg-orange-500 hover:bg-orange-600 rounded-lg transition-colors"
|
||||||
|
>
|
||||||
|
<CreditCard className="h-3.5 w-3.5" />
|
||||||
|
{t('actions.pay')}
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
<button
|
<button
|
||||||
onClick={() => setSelectedBooking(booking)}
|
onClick={() => setSelectedBooking(booking)}
|
||||||
className="inline-flex items-center gap-1 px-3 py-1.5 text-xs font-medium text-blue-700 bg-blue-50 hover:bg-blue-100 rounded-lg transition-colors"
|
className="inline-flex items-center gap-1 px-3 py-1.5 text-xs font-medium text-blue-700 bg-blue-50 hover:bg-blue-100 rounded-lg transition-colors"
|
||||||
@ -359,6 +369,7 @@ export default function BookingsListPage() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -465,6 +476,16 @@ export default function BookingsListPage() {
|
|||||||
{booking.bookingNumber || '-'}
|
{booking.bookingNumber || '-'}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-center">
|
<td className="px-6 py-4 whitespace-nowrap text-center">
|
||||||
|
<div className="flex items-center justify-center gap-2">
|
||||||
|
{booking.status === 'PENDING_PAYMENT' && (
|
||||||
|
<Link
|
||||||
|
href={`/dashboard/booking/${booking.id}/pay`}
|
||||||
|
className="inline-flex items-center gap-1 px-3 py-1.5 text-xs font-medium text-white bg-orange-500 hover:bg-orange-600 rounded-lg transition-colors"
|
||||||
|
>
|
||||||
|
<CreditCard className="h-3.5 w-3.5" />
|
||||||
|
{t('actions.pay')}
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
<button
|
<button
|
||||||
onClick={() => setSelectedBooking(booking)}
|
onClick={() => setSelectedBooking(booking)}
|
||||||
className="inline-flex items-center gap-1 px-3 py-1.5 text-xs font-medium text-blue-700 bg-blue-50 hover:bg-blue-100 rounded-lg transition-colors"
|
className="inline-flex items-center gap-1 px-3 py-1.5 text-xs font-medium text-blue-700 bg-blue-50 hover:bg-blue-100 rounded-lg transition-colors"
|
||||||
@ -472,6 +493,7 @@ export default function BookingsListPage() {
|
|||||||
<Eye className="h-3.5 w-3.5" />
|
<Eye className="h-3.5 w-3.5" />
|
||||||
{t('actions.view')}
|
{t('actions.view')}
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
@ -719,15 +741,16 @@ export default function BookingsListPage() {
|
|||||||
</div>
|
</div>
|
||||||
{selectedBooking.status === 'PENDING_PAYMENT' && (
|
{selectedBooking.status === 'PENDING_PAYMENT' && (
|
||||||
<div className="mt-4 pt-4 border-t border-gray-100">
|
<div className="mt-4 pt-4 border-t border-gray-100">
|
||||||
<p className="text-sm text-amber-700 bg-amber-50 rounded-lg px-3 py-2">
|
<p className="text-sm text-orange-700 bg-orange-50 rounded-lg px-3 py-2">
|
||||||
{t('detail.pendingPaymentNotice')}
|
{t('detail.pendingPaymentNotice')}
|
||||||
</p>
|
</p>
|
||||||
<Link
|
<Link
|
||||||
href="/dashboard/search-advanced"
|
href={`/dashboard/booking/${selectedBooking.id}/pay`}
|
||||||
className="mt-3 inline-flex items-center gap-2 px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-lg hover:bg-blue-700 transition-colors"
|
onClick={() => 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"
|
||||||
>
|
>
|
||||||
<Plus className="h-4 w-4" />
|
<CreditCard className="h-4 w-4" />
|
||||||
{t('detail.newBooking')}
|
{t('detail.payNow')}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -46,12 +46,14 @@ export default function UserDocumentsPage() {
|
|||||||
const [selectedBookingId, setSelectedBookingId] = useState<string | null>(null);
|
const [selectedBookingId, setSelectedBookingId] = useState<string | null>(null);
|
||||||
const [uploadingFiles, setUploadingFiles] = useState(false);
|
const [uploadingFiles, setUploadingFiles] = useState(false);
|
||||||
const [isDraggingAdd, setIsDraggingAdd] = useState(false);
|
const [isDraggingAdd, setIsDraggingAdd] = useState(false);
|
||||||
|
const [addFiles, setAddFiles] = useState<File[]>([]);
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const [showReplaceModal, setShowReplaceModal] = useState(false);
|
const [showReplaceModal, setShowReplaceModal] = useState(false);
|
||||||
const [documentToReplace, setDocumentToReplace] = useState<DocumentWithBooking | null>(null);
|
const [documentToReplace, setDocumentToReplace] = useState<DocumentWithBooking | null>(null);
|
||||||
const [replacingFile, setReplacingFile] = useState(false);
|
const [replacingFile, setReplacingFile] = useState(false);
|
||||||
const [isDraggingReplace, setIsDraggingReplace] = useState(false);
|
const [isDraggingReplace, setIsDraggingReplace] = useState(false);
|
||||||
|
const [replaceFile, setReplaceFile] = useState<File | null>(null);
|
||||||
const replaceFileInputRef = useRef<HTMLInputElement>(null);
|
const replaceFileInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const [openDropdownId, setOpenDropdownId] = useState<string | null>(null);
|
const [openDropdownId, setOpenDropdownId] = useState<string | null>(null);
|
||||||
@ -189,6 +191,8 @@ export default function UserDocumentsPage() {
|
|||||||
|
|
||||||
const getStatusColor = (status: string) => {
|
const getStatusColor = (status: string) => {
|
||||||
const colors: Record<string, string> = {
|
const colors: Record<string, string> = {
|
||||||
|
PENDING_PAYMENT: 'bg-orange-100 text-orange-800',
|
||||||
|
PENDING_BANK_TRANSFER: 'bg-blue-100 text-blue-800',
|
||||||
PENDING: 'bg-yellow-100 text-yellow-800',
|
PENDING: 'bg-yellow-100 text-yellow-800',
|
||||||
ACCEPTED: 'bg-green-100 text-green-800',
|
ACCEPTED: 'bg-green-100 text-green-800',
|
||||||
REJECTED: 'bg-red-100 text-red-800',
|
REJECTED: 'bg-red-100 text-red-800',
|
||||||
@ -198,8 +202,15 @@ export default function UserDocumentsPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getStatusLabel = (status: string) => {
|
const getStatusLabel = (status: string) => {
|
||||||
const key = status as 'PENDING' | 'ACCEPTED' | 'REJECTED' | 'CANCELLED';
|
const labels: Record<string, string> = {
|
||||||
return t(`statuses.${key}` as any) || status;
|
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) => {
|
const handleDownload = async (url: string, fileName: string) => {
|
||||||
@ -251,11 +262,12 @@ export default function UserDocumentsPage() {
|
|||||||
const handleCloseModal = () => {
|
const handleCloseModal = () => {
|
||||||
setShowAddModal(false);
|
setShowAddModal(false);
|
||||||
setSelectedBookingId(null);
|
setSelectedBookingId(null);
|
||||||
|
setAddFiles([]);
|
||||||
if (fileInputRef.current) fileInputRef.current.value = '';
|
if (fileInputRef.current) fileInputRef.current.value = '';
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFileUpload = async () => {
|
const handleFileUpload = async () => {
|
||||||
if (!selectedBookingId || !fileInputRef.current?.files?.length) {
|
if (!selectedBookingId || addFiles.length === 0) {
|
||||||
alert(t('addDocument.noBookingError'));
|
alert(t('addDocument.noBookingError'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -263,10 +275,7 @@ export default function UserDocumentsPage() {
|
|||||||
setUploadingFiles(true);
|
setUploadingFiles(true);
|
||||||
try {
|
try {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
const files = fileInputRef.current.files;
|
addFiles.forEach(file => formData.append('documents', file));
|
||||||
for (let i = 0; i < files.length; i++) {
|
|
||||||
formData.append('documents', files[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const token = localStorage.getItem('access_token');
|
const token = localStorage.getItem('access_token');
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
@ -274,7 +283,10 @@ export default function UserDocumentsPage() {
|
|||||||
{ method: 'POST', headers: { Authorization: `Bearer ${token}` }, body: formData }
|
{ 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'));
|
alert(t('addDocument.successMessage'));
|
||||||
handleCloseModal();
|
handleCloseModal();
|
||||||
@ -308,11 +320,12 @@ export default function UserDocumentsPage() {
|
|||||||
const handleCloseReplaceModal = () => {
|
const handleCloseReplaceModal = () => {
|
||||||
setShowReplaceModal(false);
|
setShowReplaceModal(false);
|
||||||
setDocumentToReplace(null);
|
setDocumentToReplace(null);
|
||||||
|
setReplaceFile(null);
|
||||||
if (replaceFileInputRef.current) replaceFileInputRef.current.value = '';
|
if (replaceFileInputRef.current) replaceFileInputRef.current.value = '';
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReplaceDocument = async () => {
|
const handleReplaceDocument = async () => {
|
||||||
if (!documentToReplace || !replaceFileInputRef.current?.files?.length) {
|
if (!documentToReplace || !replaceFile) {
|
||||||
alert(t('replaceDocument.noFileError'));
|
alert(t('replaceDocument.noFileError'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -320,7 +333,7 @@ export default function UserDocumentsPage() {
|
|||||||
setReplacingFile(true);
|
setReplacingFile(true);
|
||||||
try {
|
try {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('document', replaceFileInputRef.current.files[0]);
|
formData.append('document', replaceFile);
|
||||||
|
|
||||||
const token = localStorage.getItem('access_token');
|
const token = localStorage.getItem('access_token');
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
@ -768,11 +781,7 @@ export default function UserDocumentsPage() {
|
|||||||
<option value="">{t('addDocument.selectBookingPlaceholder')}</option>
|
<option value="">{t('addDocument.selectBookingPlaceholder')}</option>
|
||||||
{bookingsAvailableForDocuments.map(booking => (
|
{bookingsAvailableForDocuments.map(booking => (
|
||||||
<option key={booking.id} value={booking.id}>
|
<option key={booking.id} value={booking.id}>
|
||||||
{getQuoteNumber(booking)} - {booking.origin} → {booking.destination} (
|
{getQuoteNumber(booking)} - {booking.origin} → {booking.destination} ({getStatusLabel(booking.status)})
|
||||||
{booking.status === 'PENDING'
|
|
||||||
? t('statuses.PENDING')
|
|
||||||
: t('statuses.ACCEPTED')}
|
|
||||||
)
|
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
@ -795,11 +804,8 @@ export default function UserDocumentsPage() {
|
|||||||
onDrop={e => {
|
onDrop={e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setIsDraggingAdd(false);
|
setIsDraggingAdd(false);
|
||||||
if (fileInputRef.current && e.dataTransfer.files.length > 0) {
|
const dropped = Array.from(e.dataTransfer.files);
|
||||||
const dt = new DataTransfer();
|
if (dropped.length > 0) setAddFiles(dropped);
|
||||||
Array.from(e.dataTransfer.files).forEach(f => dt.items.add(f));
|
|
||||||
fileInputRef.current.files = dt.files;
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
onClick={() => fileInputRef.current?.click()}
|
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"
|
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"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
{addFiles.length > 0 ? (
|
||||||
|
<p className="text-sm font-medium text-blue-700">
|
||||||
|
{addFiles.length === 1
|
||||||
|
? addFiles[0].name
|
||||||
|
: `${addFiles.length} fichiers sélectionnés`}
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
<p className="text-sm text-gray-600">
|
<p className="text-sm text-gray-600">
|
||||||
{isDraggingAdd
|
{isDraggingAdd
|
||||||
? t('addDocument.dropHere')
|
? t('addDocument.dropHere')
|
||||||
: t('addDocument.dragOrClick')}
|
: t('addDocument.dragOrClick')}
|
||||||
</p>
|
</p>
|
||||||
|
)}
|
||||||
<p className="text-xs text-gray-400 mt-1">{t('addDocument.acceptedFormats')}</p>
|
<p className="text-xs text-gray-400 mt-1">{t('addDocument.acceptedFormats')}</p>
|
||||||
<input
|
<input
|
||||||
ref={fileInputRef}
|
ref={fileInputRef}
|
||||||
@ -828,9 +842,9 @@ export default function UserDocumentsPage() {
|
|||||||
multiple
|
multiple
|
||||||
accept=".pdf,.doc,.docx,.xls,.xlsx,.jpg,.jpeg,.png,.gif"
|
accept=".pdf,.doc,.docx,.xls,.xlsx,.jpg,.jpeg,.png,.gif"
|
||||||
className="hidden"
|
className="hidden"
|
||||||
onChange={() => {
|
onChange={e => {
|
||||||
// Force re-render to show selected file names
|
const selected = Array.from(e.target.files || []);
|
||||||
setIsDraggingAdd(false);
|
if (selected.length > 0) setAddFiles(selected);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -843,7 +857,7 @@ export default function UserDocumentsPage() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleFileUpload}
|
onClick={handleFileUpload}
|
||||||
disabled={uploadingFiles || !selectedBookingId}
|
disabled={uploadingFiles || !selectedBookingId || addFiles.length === 0}
|
||||||
className="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm disabled:opacity-50 disabled:cursor-not-allowed"
|
className="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
{uploadingFiles ? (
|
{uploadingFiles ? (
|
||||||
@ -947,11 +961,8 @@ export default function UserDocumentsPage() {
|
|||||||
onDrop={e => {
|
onDrop={e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setIsDraggingReplace(false);
|
setIsDraggingReplace(false);
|
||||||
if (replaceFileInputRef.current && e.dataTransfer.files.length > 0) {
|
const dropped = e.dataTransfer.files[0];
|
||||||
const dt = new DataTransfer();
|
if (dropped) setReplaceFile(dropped);
|
||||||
dt.items.add(e.dataTransfer.files[0]);
|
|
||||||
replaceFileInputRef.current.files = dt.files;
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
onClick={() => replaceFileInputRef.current?.click()}
|
onClick={() => replaceFileInputRef.current?.click()}
|
||||||
>
|
>
|
||||||
@ -968,18 +979,25 @@ 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"
|
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"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
{replaceFile ? (
|
||||||
|
<p className="text-sm font-medium text-blue-700">{replaceFile.name}</p>
|
||||||
|
) : (
|
||||||
<p className="text-sm text-gray-600">
|
<p className="text-sm text-gray-600">
|
||||||
{isDraggingReplace
|
{isDraggingReplace
|
||||||
? t('replaceDocument.dropHere')
|
? t('replaceDocument.dropHere')
|
||||||
: t('replaceDocument.dragOrClick')}
|
: t('replaceDocument.dragOrClick')}
|
||||||
</p>
|
</p>
|
||||||
|
)}
|
||||||
<p className="text-xs text-gray-400 mt-1">{t('replaceDocument.acceptedFormats')}</p>
|
<p className="text-xs text-gray-400 mt-1">{t('replaceDocument.acceptedFormats')}</p>
|
||||||
<input
|
<input
|
||||||
ref={replaceFileInputRef}
|
ref={replaceFileInputRef}
|
||||||
type="file"
|
type="file"
|
||||||
accept=".pdf,.doc,.docx,.xls,.xlsx,.jpg,.jpeg,.png,.gif"
|
accept=".pdf,.doc,.docx,.xls,.xlsx,.jpg,.jpeg,.png,.gif"
|
||||||
className="hidden"
|
className="hidden"
|
||||||
onChange={() => setIsDraggingReplace(false)}
|
onChange={e => {
|
||||||
|
const file = e.target.files?.[0];
|
||||||
|
if (file) setReplaceFile(file);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -430,7 +430,8 @@
|
|||||||
"actions": "Actions"
|
"actions": "Actions"
|
||||||
},
|
},
|
||||||
"actions": {
|
"actions": {
|
||||||
"view": "View"
|
"view": "View",
|
||||||
|
"pay": "Pay"
|
||||||
},
|
},
|
||||||
"detail": {
|
"detail": {
|
||||||
"title": "Booking",
|
"title": "Booking",
|
||||||
@ -440,7 +441,8 @@
|
|||||||
"weight": "Weight / Volume",
|
"weight": "Weight / Volume",
|
||||||
"pickupDate": "Requested pickup date",
|
"pickupDate": "Requested pickup date",
|
||||||
"price": "Price",
|
"price": "Price",
|
||||||
"pendingPaymentNotice": "This booking is awaiting payment. Contact an administrator or create a new booking.",
|
"pendingPaymentNotice": "This booking is awaiting commission payment. Click below to complete the payment.",
|
||||||
|
"payNow": "Complete payment",
|
||||||
"newBooking": "New booking",
|
"newBooking": "New booking",
|
||||||
"close": "Close"
|
"close": "Close"
|
||||||
},
|
},
|
||||||
@ -1453,6 +1455,8 @@
|
|||||||
"actions": "Actions"
|
"actions": "Actions"
|
||||||
},
|
},
|
||||||
"statuses": {
|
"statuses": {
|
||||||
|
"PENDING_PAYMENT": "Payment pending",
|
||||||
|
"PENDING_BANK_TRANSFER": "Bank transfer pending",
|
||||||
"PENDING": "Pending",
|
"PENDING": "Pending",
|
||||||
"ACCEPTED": "Accepted",
|
"ACCEPTED": "Accepted",
|
||||||
"REJECTED": "Rejected",
|
"REJECTED": "Rejected",
|
||||||
|
|||||||
@ -430,7 +430,8 @@
|
|||||||
"actions": "Actions"
|
"actions": "Actions"
|
||||||
},
|
},
|
||||||
"actions": {
|
"actions": {
|
||||||
"view": "Voir"
|
"view": "Voir",
|
||||||
|
"pay": "Payer"
|
||||||
},
|
},
|
||||||
"detail": {
|
"detail": {
|
||||||
"title": "Réservation",
|
"title": "Réservation",
|
||||||
@ -440,7 +441,8 @@
|
|||||||
"weight": "Poids / Volume",
|
"weight": "Poids / Volume",
|
||||||
"pickupDate": "Date d'enlèvement souhaitée",
|
"pickupDate": "Date d'enlèvement souhaitée",
|
||||||
"price": "Prix",
|
"price": "Prix",
|
||||||
"pendingPaymentNotice": "Cette réservation est en attente de paiement. Contactez l'administrateur ou créez une nouvelle réservation.",
|
"pendingPaymentNotice": "Cette réservation est en attente du paiement de la commission. Cliquez ci-dessous pour finaliser le paiement.",
|
||||||
|
"payNow": "Finaliser le paiement",
|
||||||
"newBooking": "Nouvelle réservation",
|
"newBooking": "Nouvelle réservation",
|
||||||
"close": "Fermer"
|
"close": "Fermer"
|
||||||
},
|
},
|
||||||
@ -1453,6 +1455,8 @@
|
|||||||
"actions": "Actions"
|
"actions": "Actions"
|
||||||
},
|
},
|
||||||
"statuses": {
|
"statuses": {
|
||||||
|
"PENDING_PAYMENT": "Paiement en attente",
|
||||||
|
"PENDING_BANK_TRANSFER": "Virement en attente",
|
||||||
"PENDING": "En attente",
|
"PENDING": "En attente",
|
||||||
"ACCEPTED": "Accepté",
|
"ACCEPTED": "Accepté",
|
||||||
"REJECTED": "Refusé",
|
"REJECTED": "Refusé",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user