|
|
@@ -45,6 +45,7 @@ export default function InvoiceScanner() {
|
|
|
const [editInvoice, setEditInvoice] = useState(null)
|
|
|
const [editForm, setEditForm] = useState({})
|
|
|
const [previewId, setPreviewId] = useState(null)
|
|
|
+ const [previewUrl, setPreviewUrl] = useState(null)
|
|
|
|
|
|
const loadProperties = () => api.get('/properties').then(r => setProperties(r.data))
|
|
|
const loadStats = () => api.get('/scan/stats').then(r => setStats(r.data))
|
|
|
@@ -162,6 +163,23 @@ export default function InvoiceScanner() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ const openPreview = async (id) => {
|
|
|
+ try {
|
|
|
+ const res = await api.get(`/scan/invoices/${id}/preview`, { responseType: 'blob' })
|
|
|
+ const url = URL.createObjectURL(res.data)
|
|
|
+ setPreviewUrl(url)
|
|
|
+ setPreviewId(id)
|
|
|
+ } catch {
|
|
|
+ toast.error('Impossible de charger l\'aperçu')
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const closePreview = () => {
|
|
|
+ if (previewUrl) URL.revokeObjectURL(previewUrl)
|
|
|
+ setPreviewUrl(null)
|
|
|
+ setPreviewId(null)
|
|
|
+ }
|
|
|
+
|
|
|
const years = [...new Set(invoices.map(i => i.year).filter(Boolean))].sort((a, b) => b - a)
|
|
|
const allYears = years.length > 0 ? years : [new Date().getFullYear()]
|
|
|
|
|
|
@@ -280,7 +298,7 @@ export default function InvoiceScanner() {
|
|
|
<p className="text-xs text-gray-400 mt-1 truncate" title={inv.file_path}>{inv.file_path.split('/').slice(-2).join('/')}</p>
|
|
|
</div>
|
|
|
<div className="flex items-center gap-2 shrink-0">
|
|
|
- <button onClick={() => setPreviewId(inv.id)} className="text-sm text-gray-500 hover:text-blue-600" title="Prévisualiser">👁️</button>
|
|
|
+ <button onClick={() => openPreview(inv.id)} className="text-sm text-gray-500 hover:text-blue-600" title="Prévisualiser">👁️</button>
|
|
|
{inv.status === 'pending' && (
|
|
|
<>
|
|
|
<button onClick={() => openEdit(inv)} className="text-sm text-blue-600 hover:text-blue-800" title="Modifier et approuver">✏️</button>
|
|
|
@@ -299,10 +317,10 @@ export default function InvoiceScanner() {
|
|
|
)}
|
|
|
|
|
|
{/* Preview modal */}
|
|
|
- {previewId && (
|
|
|
- <Modal title="Prévisualisation" onClose={() => setPreviewId(null)}>
|
|
|
+ {previewId && previewUrl && (
|
|
|
+ <Modal title="Prévisualisation" onClose={closePreview}>
|
|
|
<iframe
|
|
|
- src={`/api/scan/invoices/${previewId}/preview`}
|
|
|
+ src={previewUrl}
|
|
|
className="w-full h-[70vh] border rounded-lg"
|
|
|
title="Aperçu facture"
|
|
|
/>
|
|
|
@@ -315,7 +333,7 @@ export default function InvoiceScanner() {
|
|
|
<div className="space-y-4">
|
|
|
<div className="bg-gray-50 rounded-lg p-3 text-xs text-gray-500">
|
|
|
<p className="truncate">📄 {editInvoice.file_path.split('/').slice(-2).join('/')}</p>
|
|
|
- <button onClick={() => { setPreviewId(editInvoice.id) }} className="text-blue-600 hover:underline mt-1">Prévisualiser le fichier</button>
|
|
|
+ <button onClick={() => { openPreview(editInvoice.id) }} className="text-blue-600 hover:underline mt-1">Prévisualiser le fichier</button>
|
|
|
</div>
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
|
<div className="col-span-2">
|