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 { jsPDF } from "jspdf";
|
||||
import { useState } from "react";
|
||||
import { type PageObject } from "../page"; // Importer le type depuis la page principale
|
||||
|
||||
interface HtmlModalProps {
|
||||
content: string[] | null;
|
||||
content: PageObject[] | null; // Utilise le type PageObject importé
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
// Moteur de rendu HTML vers PDF, robuste et récursif
|
||||
const renderHtmlOnPdfPage = (pdf: jsPDF, htmlContent: string) => {
|
||||
const pageHeight = pdf.internal.pageSize.getHeight();
|
||||
const pageWidth = pdf.internal.pageSize.getWidth();
|
||||
@@ -105,10 +107,13 @@ export default function MarkdownModal({ content, onClose }: HtmlModalProps) {
|
||||
try {
|
||||
const pdf = new jsPDF({ orientation: "p", unit: "mm", format: "a4" });
|
||||
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();
|
||||
renderHtmlOnPdfPage(pdf, pageHtml);
|
||||
renderHtmlOnPdfPage(pdf, page.htmlContent);
|
||||
});
|
||||
|
||||
pdf.save("document_anonymise.pdf");
|
||||
} catch (error) {
|
||||
console.error("Erreur lors de la génération du PDF:", error);
|
||||
@@ -117,7 +122,10 @@ 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
|
||||
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);">'
|
||||
);
|
||||
|
||||
|
||||
82
app/page.tsx
82
app/page.tsx
@@ -33,7 +33,13 @@ import {
|
||||
} from "lucide-react";
|
||||
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 {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -44,8 +50,7 @@ interface ProcessedFile {
|
||||
piiCount?: number;
|
||||
errorMessage?: string;
|
||||
processedBlob?: Blob;
|
||||
// textContent est maintenant un tableau de strings, une par page.
|
||||
textContent?: string[];
|
||||
textContent?: PageObject[];
|
||||
}
|
||||
|
||||
interface AnonymizationOptions {
|
||||
@@ -76,14 +81,14 @@ export default function Home() {
|
||||
useState<AnonymizationOptions>(
|
||||
piiOptions.reduce((acc, option) => ({ ...acc, [option.id]: true }), {})
|
||||
);
|
||||
// L'état de la modale attend maintenant un tableau de strings ou null
|
||||
const [modalContent, setModalContent] = useState<string[] | null>(null);
|
||||
// CORRECTION : L'état de la modale attend maintenant la nouvelle structure de données
|
||||
const [modalContent, setModalContent] = useState<PageObject[] | null>(null);
|
||||
|
||||
const handleOptionChange = (id: string) =>
|
||||
setAnonymizationOptions((prev) => ({ ...prev, [id]: !prev[id] }));
|
||||
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.files?.length) {
|
||||
setFile(event.target.files[0]);
|
||||
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (e.target.files?.length) {
|
||||
setFile(e.target.files[0]);
|
||||
setError(null);
|
||||
}
|
||||
};
|
||||
@@ -99,8 +104,7 @@ export default function Home() {
|
||||
e.preventDefault();
|
||||
setIsDragOver(true);
|
||||
}, []);
|
||||
const handleDragLeave = useCallback((e: React.DragEvent) => {
|
||||
e.preventDefault();
|
||||
const handleDragLeave = useCallback(() => {
|
||||
setIsDragOver(false);
|
||||
}, []);
|
||||
const formatFileSize = (bytes: number): string => {
|
||||
@@ -118,7 +122,7 @@ export default function Home() {
|
||||
const removeFromHistory = (id: string) =>
|
||||
setHistory((prev) => prev.filter((item) => item.id !== id));
|
||||
|
||||
const handleDownload = (id: string) => {
|
||||
const handleDownloadTxt = (id: string) => {
|
||||
const fileToDownload = history.find((item) => item.id === id);
|
||||
if (!fileToDownload?.processedBlob) return;
|
||||
const url = URL.createObjectURL(fileToDownload.processedBlob);
|
||||
@@ -132,7 +136,6 @@ export default function Home() {
|
||||
a.remove();
|
||||
};
|
||||
|
||||
// Met à jour le contenu de la modale avec le tableau de pages
|
||||
const handlePreview = (id: string) => {
|
||||
const fileToPreview = history.find((item) => item.id === id);
|
||||
if (fileToPreview?.textContent) {
|
||||
@@ -162,7 +165,6 @@ export default function Home() {
|
||||
}
|
||||
};
|
||||
|
||||
// === Fonction principale mise à jour pour gérer le format JSON ===
|
||||
const processFile = async () => {
|
||||
if (!file) return;
|
||||
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.`);
|
||||
}
|
||||
|
||||
// On parse la réponse JSON au lieu de lire un blob
|
||||
const result = await response.json();
|
||||
|
||||
// Validation de la structure de la réponse
|
||||
if (
|
||||
!result.anonymizedDocument ||
|
||||
!Array.isArray(result.anonymizedDocument.pages)
|
||||
) {
|
||||
const docData = result.anonymizedDocument;
|
||||
if (!docData || !Array.isArray(docData.pages)) {
|
||||
throw new Error(
|
||||
"Format de réponse invalide du service d'anonymisation."
|
||||
);
|
||||
}
|
||||
|
||||
const textContent: string[] = result.anonymizedDocument.pages;
|
||||
const piiCount: number = result.anonymizedDocument.piiCount || 0;
|
||||
const textContent: PageObject[] = docData.pages;
|
||||
const piiCount: number = docData.piiCount || 0;
|
||||
|
||||
// On crée un blob pour le téléchargement .txt en joignant les pages
|
||||
const fullText = textContent.join("\n\n--- Page Suivante ---\n\n");
|
||||
const fullText = textContent
|
||||
.map(
|
||||
(page) =>
|
||||
`--- Page ${page.pageNumber} ---\n${page.htmlContent.replace(
|
||||
/<[^>]*>/g,
|
||||
"\n"
|
||||
)}`
|
||||
)
|
||||
.join("\n\n");
|
||||
const processedBlob = new Blob([fullText], {
|
||||
type: "text/plain;charset=utf-8",
|
||||
});
|
||||
setProgress(90);
|
||||
|
||||
// On met à jour l'historique avec le tableau de pages
|
||||
setHistory((prev) =>
|
||||
prev.map((item) =>
|
||||
item.id === fileId
|
||||
@@ -263,7 +266,6 @@ export default function Home() {
|
||||
}
|
||||
};
|
||||
|
||||
// === Rendu du composant (JSX - inchangé) ===
|
||||
return (
|
||||
<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]">
|
||||
@@ -291,19 +293,7 @@ export default function Home() {
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-1 overflow-y-auto p-3 space-y-3">
|
||||
{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.length > 0 ? (
|
||||
history.map((item) => {
|
||||
const status = getStatusInfo(item);
|
||||
return (
|
||||
@@ -360,7 +350,7 @@ export default function Home() {
|
||||
)}
|
||||
{item.status === "completed" && (
|
||||
<Button
|
||||
onClick={() => handleDownload(item.id)}
|
||||
onClick={() => handleDownloadTxt(item.id)}
|
||||
variant="ghost"
|
||||
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"
|
||||
@@ -381,6 +371,18 @@ export default function Home() {
|
||||
</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>
|
||||
</aside>
|
||||
|
||||
Reference in New Issue
Block a user