From aa19bb82a0bcdcc4b1f64fc8a3e0fcecd3493648 Mon Sep 17 00:00:00 2001 From: nBiqoz Date: Sat, 26 Jul 2025 00:12:57 +0200 Subject: [PATCH] version pre --- app/page.tsx | 917 ++++++++++++++++++++++++--------------------------- 1 file changed, 433 insertions(+), 484 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index 7086d17..7fb69d5 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,536 +1,485 @@ "use client"; -import { useState, useCallback } from "react"; -import Image from "next/image"; -import { Button } from "@/components/ui/button"; -import { Card, CardContent } from "@/components/ui/card"; -import { Input } from "@/components/ui/input"; -import { Badge } from "@/components/ui/badge"; -import { - Upload, - FileText, - ShieldCheck, - Download, - Trash2, - AlertCircle, - X, - Zap, - Lock, - Shield, - Clock, - Eye, - TestTube, -} from "lucide-react"; -import PresidioModal from "./components/PresidioModal"; +import { useState } from "react"; +import { Upload, Download, Copy, AlertTriangle } from "lucide-react"; export type PageObject = { pageNumber: number; htmlContent: string; }; -interface ProcessedFile { - id: string; - name: string; - status: "processing" | "completed" | "error"; - timestamp: Date; - originalSize?: string; - processedSize?: string; - piiCount?: number; - errorMessage?: string; - processedBlob?: Blob; - anonymizedText?: string; -} - export default function Home() { - const [file, setFile] = useState(null); + const [sourceText, setSourceText] = useState(""); + const [outputText, setOutputText] = useState(""); + const [activeTab, setActiveTab] = useState<"text" | "url">("text"); + const [urlInput, setUrlInput] = useState(""); const [isProcessing, setIsProcessing] = useState(false); - const [progress, setProgress] = useState(0); - const [isDragOver, setIsDragOver] = useState(false); - const [history, setHistory] = useState([]); const [error, setError] = useState(null); - const [showPresidioModal, setShowPresidioModal] = useState(false); - const [anonymizedResult, setAnonymizedResult] = useState<{ - text: string; - piiCount: number; - } | null>(null); + const [uploadedFile, setUploadedFile] = useState(null); // Nouveau state pour le fichier + const [fileContent, setFileContent] = useState(""); // Nouveau state pour le contenu - const handleFileChange = (e: React.ChangeEvent) => { + const handleFileChange = async (e: React.ChangeEvent) => { if (e.target.files?.length) { - setFile(e.target.files[0]); + const selectedFile = e.target.files[0]; + setUploadedFile(selectedFile); // Stocker le fichier + setSourceText(""); // EFFACER le texte existant quand on upload un fichier setError(null); + + try { + // Traitement des fichiers texte - juste lire le contenu + if ( + selectedFile.type === "text/plain" || + selectedFile.name.endsWith(".txt") + ) { + const reader = new FileReader(); + reader.onload = (e) => { + const content = e.target?.result as string; + setFileContent(content); // Stocker le contenu sans l'afficher + }; + reader.readAsText(selectedFile); + } + // Pour les PDF - juste stocker le fichier, pas de traitement automatique + else if ( + selectedFile.type === "application/pdf" || + selectedFile.name.endsWith(".pdf") + ) { + setFileContent(""); // Pas de contenu texte pour les PDF + } + // Traitement des fichiers Word (optionnel) + else if ( + selectedFile.name.endsWith(".docx") || + selectedFile.name.endsWith(".doc") + ) { + setError( + "Les fichiers Word ne sont pas encore supportés. Veuillez convertir en PDF ou texte." + ); + setUploadedFile(null); + return; + } else { + setError( + "Format de fichier non supporté. Veuillez utiliser un fichier PDF ou texte." + ); + setUploadedFile(null); + return; + } + } catch (error) { + setError( + error instanceof Error + ? error.message + : "Erreur lors du traitement du fichier" + ); + setUploadedFile(null); + } } }; - const handleDrop = useCallback((e: React.DragEvent) => { - e.preventDefault(); - setIsDragOver(false); - if (e.dataTransfer.files?.length) { - setFile(e.dataTransfer.files[0]); - setError(null); - } - }, []); + const loadSampleText = () => { + const sampleText = `Bonjour, - const handleDragOver = useCallback((e: React.DragEvent) => { - e.preventDefault(); - setIsDragOver(true); - }, []); +Je suis Jean Dupont, né le 15 mars 1985. Mon numéro de téléphone est le 06 12 34 56 78 et mon email est jean.dupont@email.com. - const handleDragLeave = useCallback(() => { - setIsDragOver(false); - }, []); +Mon adresse est 123 rue de la Paix, 75001 Paris. - const formatFileSize = (bytes: number): string => { - if (bytes === 0) return "0 Bytes"; - const k = 1024; - const sizes = ["Bytes", "KB", "MB", "GB"]; - const i = Math.floor(Math.log(bytes) / Math.log(k)); - return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; +Mon numéro de sécurité sociale est 1 85 03 75 123 456 78. + +Cordialement, +Jean Dupont`; + setSourceText(sampleText); }; - const clearFile = () => { - setFile(null); + const anonymizeData = async () => { + // Si un fichier est uploadé, traiter le fichier + if (uploadedFile) { + if ( + uploadedFile.type === "application/pdf" || + uploadedFile.name.endsWith(".pdf") + ) { + setIsProcessing(true); + setError(null); + setOutputText(""); + + try { + const formData = new FormData(); + formData.append("file", uploadedFile); + + const response = await fetch("/api/process-document", { + method: "POST", + body: formData, + }); + + if (!response.ok) { + throw new Error("Erreur lors du traitement du PDF"); + } + + const result = await response.json(); + if (result.anonymizedText) { + setOutputText(result.anonymizedText); + } else { + throw new Error(result.error || "Erreur lors de l'anonymisation"); + } + } catch (error) { + setError( + error instanceof Error + ? error.message + : "Erreur lors du traitement du fichier" + ); + } finally { + setIsProcessing(false); + } + return; + } else { + // Pour les fichiers texte, utiliser le contenu lu + if (!fileContent.trim()) { + setError("Le fichier ne contient pas de texte"); + return; + } + // Utiliser fileContent au lieu de sourceText pour l'anonymisation + const textToAnonymize = fileContent; + + setIsProcessing(true); + setError(null); + setOutputText(""); + + try { + await new Promise((resolve) => setTimeout(resolve, 1500)); + + const anonymized = textToAnonymize + .replace(/\b[A-Z][a-z]+ [A-Z][a-z]+\b/g, "[Nom1]") + .replace(/\b0[1-9](?:[\s.-]?\d{2}){4}\b/g, "[Téléphone1]") + .replace( + /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g, + "[Email1]" + ) + .replace( + /\b\d{1,3}\s+[a-zA-Z\s]+,\s*\d{5}\s+[a-zA-Z\s]+\b/g, + "[Adresse1]" + ) + .replace( + /\b\d\s\d{2}\s\d{2}\s\d{2}\s\d{3}\s\d{3}\s\d{2}\b/g, + "[NuméroSS1]" + ); + + setOutputText(anonymized); + } catch { + setError("Erreur lors de l'anonymisation"); + } finally { + setIsProcessing(false); + } + return; + } + } + + // Si pas de fichier, traiter le texte saisi manuellement + if (!sourceText.trim()) { + setError( + "Veuillez saisir du texte à anonymiser ou télécharger un fichier" + ); + return; + } + + setIsProcessing(true); setError(null); + setOutputText(""); + + try { + await new Promise((resolve) => setTimeout(resolve, 1500)); + + const anonymized = sourceText + .replace(/\b[A-Z][a-z]+ [A-Z][a-z]+\b/g, "[Nom1]") + .replace(/\b0[1-9](?:[\s.-]?\d{2}){4}\b/g, "[Téléphone1]") + .replace( + /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g, + "[Email1]" + ) + .replace( + /\b\d{1,3}\s+[a-zA-Z\s]+,\s*\d{5}\s+[a-zA-Z\s]+\b/g, + "[Adresse1]" + ) + .replace( + /\b\d\s\d{2}\s\d{2}\s\d{2}\s\d{3}\s\d{3}\s\d{2}\b/g, + "[NuméroSS1]" + ); + + setOutputText(anonymized); + } catch { + setError("Erreur lors de l'anonymisation"); + } finally { + setIsProcessing(false); + } }; - const clearHistory = () => setHistory([]); + const copyToClipboard = () => { + navigator.clipboard.writeText(outputText); + }; - const removeFromHistory = (id: string) => - setHistory((prev) => prev.filter((item) => item.id !== id)); - - const handleDownloadTxt = (id: string) => { - const fileToDownload = history.find((item) => item.id === id); - if (!fileToDownload?.processedBlob) return; - const url = URL.createObjectURL(fileToDownload.processedBlob); + const downloadText = () => { + const blob = new Blob([outputText], { type: "text/plain" }); + const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; - a.download = `anonymized_${fileToDownload.name - .split(".") - .slice(0, -1) - .join(".")}.txt`; + a.download = "texte-anonymise.txt"; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }; - const handlePreview = (id: string) => { - const fileToPreview = history.find((item) => item.id === id); - if (fileToPreview?.anonymizedText && fileToPreview.piiCount !== undefined) { - setAnonymizedResult({ - text: fileToPreview.anonymizedText, - piiCount: fileToPreview.piiCount, - }); - setShowPresidioModal(true); - } - }; - - const getStatusInfo = (item: ProcessedFile) => { - switch (item.status) { - case "completed": - return { - icon: , - color: "bg-[#061717]", - }; - case "error": - return { - icon: , - color: "bg-[#F7AB6E]", - }; - default: - return { - icon: ( -
- ), - color: "bg-[#061717]", - }; - } - }; - - const loadTestDocument = () => { - try { - // Créer un document de test avec des données PII fictives - const testContent = `Rapport pour la société belge "Solution Globale SPRL" (BCE : BE 0987.654.321). -Contact principal : M. Luc Dubois, né le 15/03/1975. -Son numéro de registre national est le 75.03.15-123.45. -Adresse : Avenue des Arts 56, 1000 Bruxelles. -Téléphone : +32 470 12 34 56. Email : luc.dubois@solutionglobale.be. -Le paiement de la facture a été effectué par carte VISA 4979 1234 5678 9012. -Le remboursement sera versé sur le compte IBAN BE12 3456 7890 1234, code SWIFT : GEBABEBB.`; - - const testBlob = new Blob([testContent], { type: "text/plain" }); - const testFile = new File([testBlob], "document-test.txt", { - type: "text/plain", - }); - - setFile(testFile); - setError(null); - } catch { - setError("Erreur lors du chargement du document de test"); - } - }; - - const processFile = async () => { - if (!file) return; - - setIsProcessing(true); - setProgress(0); - setError(null); - const fileId = `${Date.now()}-${file.name}`; - - setHistory((prev) => [ - { - id: fileId, - name: file.name, - status: "processing", - timestamp: new Date(), - originalSize: formatFileSize(file.size), - }, - ...prev, - ]); - - setFile(null); - - try { - const formData = new FormData(); - formData.append("file", file); - setProgress(25); - - const response = await fetch("/api/process-document", { - method: "POST", - body: formData, - }); - setProgress(75); - - const result = await response.json(); - - if (!response.ok) { - throw new Error( - result.error || "Une erreur est survenue lors du traitement." - ); - } - - const { anonymizedText, piiCount } = result; - - const processedBlob = new Blob([anonymizedText], { - type: "text/plain;charset=utf-8", - }); - - setHistory((prev) => - prev.map((item) => - item.id === fileId - ? { - ...item, - status: "completed", - processedSize: formatFileSize(processedBlob.size), - piiCount, - processedBlob, - anonymizedText, - } - : item - ) - ); - setProgress(100); - } catch (err) { - const errorMessage = - err instanceof Error - ? err.message - : "Une erreur inconnue est survenue."; - setError(errorMessage); - setHistory((prev) => - prev.map((item) => - item.id === fileId ? { ...item, status: "error", errorMessage } : item - ) - ); - } finally { - setIsProcessing(false); - setTimeout(() => setProgress(0), 1000); - } - }; - return ( -
- - -
-
-
-
- -
-

- LeCercle.IA -

-

- Anonymisation • RGPD • Sécurisé -

-
- - -
+ {/* Main Content */} +
+
+ {/* Left Panel - Source */} +
+ {/* Tabs */} +
+
+ Texte Source + + +
- {/* Bouton pour charger un document de test */} -
- -
+ {/* Content Area - même taille que droite */} +
+ {/* Zone de contenu - prend tout l'espace disponible */} +
+ {activeTab === "text" ? ( +
+ {/* Zone de texte - prend TOUT l'espace */} +
+ {/* Placeholder conditionnel : affiché seulement si pas de texte ET pas de fichier uploadé */} + {sourceText === "" && !uploadedFile && ( +
+ Tapez votre texte ici ou{" "} + + essayez un texte d'exemple + +
+ )} - {isProcessing && ( -
-
- - Traitement... - - - {Math.round(progress)}% - + {/* Affichage du fichier uploadé dans la zone de texte */} + {uploadedFile && ( +
+
+ + + {uploadedFile.name} + + + ({(uploadedFile.size / 1024).toFixed(1)} KB) + + +
+
+ )} + +