"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, User, AtSign, MapPin, Cake, Home as HomeIcon, Venus, Phone, Building, Fingerprint, CreditCard, Check, Eye, } from "lucide-react"; import PresidioModal from "./components/PresidioModal"; 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; } interface AnonymizationOptions { [key: string]: boolean; } const piiOptions = [ { id: "name", label: "Nom & Prénom", icon: User }, { id: "email", label: "Email", icon: AtSign }, { id: "location", label: "Lieu", icon: MapPin }, { id: "birthdate", label: "Date de naissance", icon: Cake }, { id: "address", label: "Adresse", icon: HomeIcon }, { id: "gender", label: "Genre / Sexe", icon: Venus }, { id: "phone", label: "Téléphone", icon: Phone }, { id: "organization", label: "Entreprise", icon: Building }, { id: "idNumber", label: "N° Identification", icon: Fingerprint }, { id: "financial", label: "Données financières", icon: CreditCard }, ]; export default function Home() { const [file, setFile] = useState(null); 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 [anonymizationOptions, setAnonymizationOptions] = useState( piiOptions.reduce((acc, option) => ({ ...acc, [option.id]: true }), {}) ); const [showPresidioModal, setShowPresidioModal] = useState(false); const [anonymizedResult, setAnonymizedResult] = useState<{ text: string; piiCount: number; } | null>(null); const handleOptionChange = (id: string) => setAnonymizationOptions((prev) => ({ ...prev, [id]: !prev[id] })); const handleFileChange = (e: React.ChangeEvent) => { if (e.target.files?.length) { setFile(e.target.files[0]); setError(null); } }; const handleDrop = useCallback((e: React.DragEvent) => { e.preventDefault(); setIsDragOver(false); if (e.dataTransfer.files?.length) { setFile(e.dataTransfer.files[0]); setError(null); } }, []); const handleDragOver = useCallback((e: React.DragEvent) => { e.preventDefault(); setIsDragOver(true); }, []); const handleDragLeave = useCallback(() => { setIsDragOver(false); }, []); 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]; }; const clearFile = () => { setFile(null); setError(null); }; const clearHistory = () => setHistory([]); 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 a = document.createElement("a"); a.href = url; a.download = `anonymized_${fileToDownload.name .split(".") .slice(0, -1) .join(".")}.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 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é

{file ? ( ) : ( )}
{file ? (

{file.name}

{formatFileSize(file.size)}

) : (

Glissez votre document

Ou cliquez ici

)}

Options d'Anonymisation

{piiOptions.map((option) => ( ))}
{isProcessing && (
Traitement... {Math.round(progress)}%
)} {error && (

{error}

)}
{file && !isProcessing && ( )}
{[ { icon: Shield, title: "RGPD", subtitle: "Conforme" }, { icon: Clock, title: "Rapide", subtitle: "Local" }, { icon: Lock, title: "Sécurisé", subtitle: "Sans Serveur" }, ].map((item, index) => (

{item.title}

{item.subtitle}

))}
{showPresidioModal && ( setShowPresidioModal(false)} /> )}
); }