full pages
This commit is contained in:
@@ -1,14 +1,16 @@
|
|||||||
// components/MarkdownModal.tsx
|
// app/components/MarkdownModal.tsx
|
||||||
|
|
||||||
import { X, Download } from "lucide-react";
|
import { X, Download } from "lucide-react";
|
||||||
import { jsPDF } from "jspdf";
|
import { jsPDF } from "jspdf";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { type PageObject } from "../page"; // Importer le type depuis la page principale
|
||||||
|
|
||||||
interface HtmlModalProps {
|
interface HtmlModalProps {
|
||||||
content: string[] | null;
|
content: PageObject[] | null; // Utilise le type PageObject importé
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Moteur de rendu HTML vers PDF, robuste et récursif
|
||||||
const renderHtmlOnPdfPage = (pdf: jsPDF, htmlContent: string) => {
|
const renderHtmlOnPdfPage = (pdf: jsPDF, htmlContent: string) => {
|
||||||
const pageHeight = pdf.internal.pageSize.getHeight();
|
const pageHeight = pdf.internal.pageSize.getHeight();
|
||||||
const pageWidth = pdf.internal.pageSize.getWidth();
|
const pageWidth = pdf.internal.pageSize.getWidth();
|
||||||
@@ -105,10 +107,13 @@ export default function MarkdownModal({ content, onClose }: HtmlModalProps) {
|
|||||||
try {
|
try {
|
||||||
const pdf = new jsPDF({ orientation: "p", unit: "mm", format: "a4" });
|
const pdf = new jsPDF({ orientation: "p", unit: "mm", format: "a4" });
|
||||||
pdf.setFont("Helvetica", "normal");
|
pdf.setFont("Helvetica", "normal");
|
||||||
content.forEach((pageHtml, index) => {
|
|
||||||
|
// Mis à jour pour boucler sur les objets et passer le contenu HTML
|
||||||
|
content.forEach((page, index) => {
|
||||||
if (index > 0) pdf.addPage();
|
if (index > 0) pdf.addPage();
|
||||||
renderHtmlOnPdfPage(pdf, pageHtml);
|
renderHtmlOnPdfPage(pdf, page.htmlContent);
|
||||||
});
|
});
|
||||||
|
|
||||||
pdf.save("document_anonymise.pdf");
|
pdf.save("document_anonymise.pdf");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Erreur lors de la génération du PDF:", error);
|
console.error("Erreur lors de la génération du PDF:", error);
|
||||||
@@ -117,9 +122,12 @@ export default function MarkdownModal({ content, onClose }: HtmlModalProps) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const previewHtml = content.join(
|
// Mis à jour pour extraire le contenu HTML de chaque page avant de l'afficher
|
||||||
'<hr style="margin: 2.5rem 0; border-style: dashed; border-color: rgba(255,255,255,0.2);">'
|
const previewHtml = content
|
||||||
);
|
.map((page) => page.htmlContent)
|
||||||
|
.join(
|
||||||
|
'<hr style="margin: 2.5rem 0; border-style: dashed; border-color: rgba(255,255,255,0.2);">'
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|||||||
82
app/page.tsx
82
app/page.tsx
@@ -33,7 +33,13 @@ import {
|
|||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import MarkdownModal from "./components/MarkdownModal";
|
import MarkdownModal from "./components/MarkdownModal";
|
||||||
|
|
||||||
// === Interfaces et Données ===
|
// Définition de la structure d'un objet page, exporté pour être réutilisé
|
||||||
|
export type PageObject = {
|
||||||
|
pageNumber: number;
|
||||||
|
htmlContent: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Interface mise à jour pour utiliser la nouvelle structure PageObject
|
||||||
interface ProcessedFile {
|
interface ProcessedFile {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -44,8 +50,7 @@ interface ProcessedFile {
|
|||||||
piiCount?: number;
|
piiCount?: number;
|
||||||
errorMessage?: string;
|
errorMessage?: string;
|
||||||
processedBlob?: Blob;
|
processedBlob?: Blob;
|
||||||
// textContent est maintenant un tableau de strings, une par page.
|
textContent?: PageObject[];
|
||||||
textContent?: string[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AnonymizationOptions {
|
interface AnonymizationOptions {
|
||||||
@@ -76,14 +81,14 @@ export default function Home() {
|
|||||||
useState<AnonymizationOptions>(
|
useState<AnonymizationOptions>(
|
||||||
piiOptions.reduce((acc, option) => ({ ...acc, [option.id]: true }), {})
|
piiOptions.reduce((acc, option) => ({ ...acc, [option.id]: true }), {})
|
||||||
);
|
);
|
||||||
// L'état de la modale attend maintenant un tableau de strings ou null
|
// CORRECTION : L'état de la modale attend maintenant la nouvelle structure de données
|
||||||
const [modalContent, setModalContent] = useState<string[] | null>(null);
|
const [modalContent, setModalContent] = useState<PageObject[] | null>(null);
|
||||||
|
|
||||||
const handleOptionChange = (id: string) =>
|
const handleOptionChange = (id: string) =>
|
||||||
setAnonymizationOptions((prev) => ({ ...prev, [id]: !prev[id] }));
|
setAnonymizationOptions((prev) => ({ ...prev, [id]: !prev[id] }));
|
||||||
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
if (event.target.files?.length) {
|
if (e.target.files?.length) {
|
||||||
setFile(event.target.files[0]);
|
setFile(e.target.files[0]);
|
||||||
setError(null);
|
setError(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -99,8 +104,7 @@ export default function Home() {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setIsDragOver(true);
|
setIsDragOver(true);
|
||||||
}, []);
|
}, []);
|
||||||
const handleDragLeave = useCallback((e: React.DragEvent) => {
|
const handleDragLeave = useCallback(() => {
|
||||||
e.preventDefault();
|
|
||||||
setIsDragOver(false);
|
setIsDragOver(false);
|
||||||
}, []);
|
}, []);
|
||||||
const formatFileSize = (bytes: number): string => {
|
const formatFileSize = (bytes: number): string => {
|
||||||
@@ -118,7 +122,7 @@ export default function Home() {
|
|||||||
const removeFromHistory = (id: string) =>
|
const removeFromHistory = (id: string) =>
|
||||||
setHistory((prev) => prev.filter((item) => item.id !== id));
|
setHistory((prev) => prev.filter((item) => item.id !== id));
|
||||||
|
|
||||||
const handleDownload = (id: string) => {
|
const handleDownloadTxt = (id: string) => {
|
||||||
const fileToDownload = history.find((item) => item.id === id);
|
const fileToDownload = history.find((item) => item.id === id);
|
||||||
if (!fileToDownload?.processedBlob) return;
|
if (!fileToDownload?.processedBlob) return;
|
||||||
const url = URL.createObjectURL(fileToDownload.processedBlob);
|
const url = URL.createObjectURL(fileToDownload.processedBlob);
|
||||||
@@ -132,7 +136,6 @@ export default function Home() {
|
|||||||
a.remove();
|
a.remove();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Met à jour le contenu de la modale avec le tableau de pages
|
|
||||||
const handlePreview = (id: string) => {
|
const handlePreview = (id: string) => {
|
||||||
const fileToPreview = history.find((item) => item.id === id);
|
const fileToPreview = history.find((item) => item.id === id);
|
||||||
if (fileToPreview?.textContent) {
|
if (fileToPreview?.textContent) {
|
||||||
@@ -162,7 +165,6 @@ export default function Home() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// === Fonction principale mise à jour pour gérer le format JSON ===
|
|
||||||
const processFile = async () => {
|
const processFile = async () => {
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
const n8nWebhookUrl = process.env.NEXT_PUBLIC_N8N_WEBHOOK_URL;
|
const n8nWebhookUrl = process.env.NEXT_PUBLIC_N8N_WEBHOOK_URL;
|
||||||
@@ -207,30 +209,31 @@ export default function Home() {
|
|||||||
throw new Error(errorResult.error || `Échec du traitement.`);
|
throw new Error(errorResult.error || `Échec du traitement.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// On parse la réponse JSON au lieu de lire un blob
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
const docData = result.anonymizedDocument;
|
||||||
// Validation de la structure de la réponse
|
if (!docData || !Array.isArray(docData.pages)) {
|
||||||
if (
|
|
||||||
!result.anonymizedDocument ||
|
|
||||||
!Array.isArray(result.anonymizedDocument.pages)
|
|
||||||
) {
|
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Format de réponse invalide du service d'anonymisation."
|
"Format de réponse invalide du service d'anonymisation."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const textContent: string[] = result.anonymizedDocument.pages;
|
const textContent: PageObject[] = docData.pages;
|
||||||
const piiCount: number = result.anonymizedDocument.piiCount || 0;
|
const piiCount: number = docData.piiCount || 0;
|
||||||
|
|
||||||
// On crée un blob pour le téléchargement .txt en joignant les pages
|
const fullText = textContent
|
||||||
const fullText = textContent.join("\n\n--- Page Suivante ---\n\n");
|
.map(
|
||||||
|
(page) =>
|
||||||
|
`--- Page ${page.pageNumber} ---\n${page.htmlContent.replace(
|
||||||
|
/<[^>]*>/g,
|
||||||
|
"\n"
|
||||||
|
)}`
|
||||||
|
)
|
||||||
|
.join("\n\n");
|
||||||
const processedBlob = new Blob([fullText], {
|
const processedBlob = new Blob([fullText], {
|
||||||
type: "text/plain;charset=utf-8",
|
type: "text/plain;charset=utf-8",
|
||||||
});
|
});
|
||||||
setProgress(90);
|
setProgress(90);
|
||||||
|
|
||||||
// On met à jour l'historique avec le tableau de pages
|
|
||||||
setHistory((prev) =>
|
setHistory((prev) =>
|
||||||
prev.map((item) =>
|
prev.map((item) =>
|
||||||
item.id === fileId
|
item.id === fileId
|
||||||
@@ -263,7 +266,6 @@ export default function Home() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// === Rendu du composant (JSX - inchangé) ===
|
|
||||||
return (
|
return (
|
||||||
<div className="h-screen w-screen bg-[#061717] flex flex-col md:flex-row overflow-hidden">
|
<div className="h-screen w-screen bg-[#061717] flex flex-col md:flex-row overflow-hidden">
|
||||||
<aside className="w-full md:w-80 md:flex-shrink-0 bg-[#061717] border-b-4 md:border-b-0 md:border-r-4 border-white flex flex-col shadow-[4px_0_0_0_black]">
|
<aside className="w-full md:w-80 md:flex-shrink-0 bg-[#061717] border-b-4 md:border-b-0 md:border-r-4 border-white flex flex-col shadow-[4px_0_0_0_black]">
|
||||||
@@ -291,19 +293,7 @@ export default function Home() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 overflow-y-auto p-3 space-y-3">
|
<div className="flex-1 overflow-y-auto p-3 space-y-3">
|
||||||
{history.length === 0 ? (
|
{history.length > 0 ? (
|
||||||
<div className="text-center py-10 px-4 flex flex-col justify-center h-full">
|
|
||||||
<div className="w-14 h-14 bg-[#F7AB6E] border-4 border-white shadow-[6px_6px_0_0_black] mx-auto mb-4 flex items-center justify-center">
|
|
||||||
<FileText className="h-7 w-7 text-white" />
|
|
||||||
</div>
|
|
||||||
<p className="text-white font-black text-base uppercase">
|
|
||||||
Aucun Document
|
|
||||||
</p>
|
|
||||||
<p className="text-white/70 font-bold mt-1 text-xs">
|
|
||||||
Vos fichiers apparaîtront ici.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
history.map((item) => {
|
history.map((item) => {
|
||||||
const status = getStatusInfo(item);
|
const status = getStatusInfo(item);
|
||||||
return (
|
return (
|
||||||
@@ -360,7 +350,7 @@ export default function Home() {
|
|||||||
)}
|
)}
|
||||||
{item.status === "completed" && (
|
{item.status === "completed" && (
|
||||||
<Button
|
<Button
|
||||||
onClick={() => handleDownload(item.id)}
|
onClick={() => handleDownloadTxt(item.id)}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
className="h-7 w-7 bg-[#061717] text-white border-2 border-white shadow-[2px_2px_0_0_black] hover:shadow-[1px_1px_0_0_black] active:shadow-none active:translate-x-0.5 active:translate-y-0.5"
|
className="h-7 w-7 bg-[#061717] text-white border-2 border-white shadow-[2px_2px_0_0_black] hover:shadow-[1px_1px_0_0_black] active:shadow-none active:translate-x-0.5 active:translate-y-0.5"
|
||||||
@@ -381,6 +371,18 @@ export default function Home() {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
) : (
|
||||||
|
<div className="text-center py-10 px-4 flex flex-col justify-center h-full">
|
||||||
|
<div className="w-14 h-14 bg-[#F7AB6E] border-4 border-white shadow-[6px_6px_0_0_black] mx-auto mb-4 flex items-center justify-center">
|
||||||
|
<FileText className="h-7 w-7 text-white" />
|
||||||
|
</div>
|
||||||
|
<p className="text-white font-black text-base uppercase">
|
||||||
|
Aucun Document
|
||||||
|
</p>
|
||||||
|
<p className="text-white/70 font-bold mt-1 text-xs">
|
||||||
|
Vos fichiers apparaîtront ici.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|||||||
Reference in New Issue
Block a user