Checkpoint: Ajout du bouton de téléchargement direct du PDF annoté BAP : route Express /api/download-bap/:filename avec Content-Disposition attachment, bouton Télécharger dans InvoicesBAP (apparaît après validation), boutons Voir + Télécharger dans l'Historique BAP. Correction du layout PDF (2 colonnes) également incluse.
This commit is contained in:
@@ -33,6 +33,18 @@ import {
|
||||
} from "@/components/ui/table";
|
||||
import { trpc } from "@/lib/trpc";
|
||||
import { Search, FileText, Download, FileSpreadsheet, Trash2, Edit, Trash, CheckCircle, CheckCircle2, ShieldCheck } from "lucide-react";
|
||||
|
||||
// Helper : télécharge un PDF annoté BAP depuis son URL de stockage
|
||||
function downloadBapPdf(pdfUrl: string, supplierName?: string) {
|
||||
const filename = pdfUrl.split('/').pop() || 'BAP.pdf';
|
||||
const downloadUrl = `/api/download-bap/${filename}`;
|
||||
const a = document.createElement('a');
|
||||
a.href = downloadUrl;
|
||||
a.download = supplierName ? `BAP_${supplierName.replace(/[^a-zA-Z0-9]/g, '_')}.pdf` : filename;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
}
|
||||
import * as XLSX from 'xlsx';
|
||||
import { toast } from "sonner";
|
||||
import { useLocation } from "wouter";
|
||||
@@ -65,6 +77,8 @@ export default function InvoicesBAP() {
|
||||
const [addDialogType, setAddDialogType] = useState<"service" | "ventilation" | null>(null);
|
||||
const [newItemName, setNewItemName] = useState("");
|
||||
const [pendingInvoiceId, setPendingInvoiceId] = useState<number | null>(null);
|
||||
// Map invoiceId -> pdfUrl après validation BAP (pour bouton téléchargement)
|
||||
const [bapPdfUrls, setBapPdfUrls] = useState<Record<number, string>>({});
|
||||
const [textDialogOpen, setTextDialogOpen] = useState(false);
|
||||
const [selectedInvoiceText, setSelectedInvoiceText] = useState<string | null>(null);
|
||||
const { data: allInvoices, isLoading } = trpc.invoices.list.useQuery();
|
||||
@@ -151,6 +165,10 @@ export default function InvoicesBAP() {
|
||||
|
||||
const validateBAPMutation = trpc.invoices.validateBAP.useMutation({
|
||||
onSuccess: (data) => {
|
||||
// Stocker le pdfUrl pour le bouton téléchargement
|
||||
if (data.invoiceId && data.pdfUrl) {
|
||||
setBapPdfUrls(prev => ({ ...prev, [data.invoiceId as number]: data.pdfUrl as string }));
|
||||
}
|
||||
if (data.exportMode === 'browser' && data.pdfUrl) {
|
||||
toast.success("Facture validée BAP ! Ouverture du PDF annoté...", { duration: 3000 });
|
||||
window.open(data.pdfUrl, '_blank');
|
||||
@@ -679,12 +697,25 @@ export default function InvoicesBAP() {
|
||||
<Trash className="h-4 w-4" />
|
||||
</Button>
|
||||
{invoice.bapValidated === 1 ? (
|
||||
<div
|
||||
className="h-8 px-2 flex items-center justify-center rounded border border-green-300 bg-green-50 text-green-700 text-xs font-semibold gap-1"
|
||||
title={`Validé BAP le ${invoice.bapValidatedAt ? new Date(invoice.bapValidatedAt).toLocaleDateString('fr-FR') : ''}`}
|
||||
>
|
||||
<CheckCircle2 className="h-4 w-4" />
|
||||
<span>Validé</span>
|
||||
<div className="flex gap-1 items-center">
|
||||
<div
|
||||
className="h-8 px-2 flex items-center justify-center rounded border border-green-300 bg-green-50 text-green-700 text-xs font-semibold gap-1"
|
||||
title={`Validé BAP le ${invoice.bapValidatedAt ? new Date(invoice.bapValidatedAt).toLocaleDateString('fr-FR') : ''}`}
|
||||
>
|
||||
<CheckCircle2 className="h-4 w-4" />
|
||||
<span>Validé</span>
|
||||
</div>
|
||||
{bapPdfUrls[invoice.id] && (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="h-8 px-2 text-blue-600 border-blue-300 hover:bg-blue-50"
|
||||
title="Télécharger le PDF annoté BAP"
|
||||
onClick={() => downloadBapPdf(bapPdfUrls[invoice.id], invoice.supplierName || undefined)}
|
||||
>
|
||||
<Download className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<Button
|
||||
|
||||
Reference in New Issue
Block a user