172 lines
6.2 KiB
TypeScript
172 lines
6.2 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useCallback } from "react";
|
|
import { FileUploadComponent } from "./components/FileUploadComponent";
|
|
import { EntityMappingTable } from "./components/EntityMappingTable";
|
|
import { ProgressBar } from "./components/ProgressBar";
|
|
import { useFileHandler } from "./components/FileHandler";
|
|
import { useAnonymization } from "./components/AnonymizationLogic";
|
|
import { useDownloadActions } from "./components/DownloadActions";
|
|
|
|
import { EntityMapping } from "./config/entityLabels"; // Importer l'interface unifiée
|
|
|
|
// Supprimer l'interface locale EntityMapping (lignes 12-18)
|
|
|
|
export default function Home() {
|
|
const [sourceText, setSourceText] = useState("");
|
|
const [outputText, setOutputText] = useState("");
|
|
const [anonymizedText, setAnonymizedText] = useState(""); // Nouveau state pour le texte anonymisé de Presidio
|
|
const [uploadedFile, setUploadedFile] = useState<File | null>(null);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [isLoadingFile, setIsLoadingFile] = useState(false);
|
|
const [entityMappings, setEntityMappings] = useState<EntityMapping[]>([]);
|
|
const [isExampleLoaded, setIsExampleLoaded] = useState(false);
|
|
|
|
const progressSteps = ["Téléversement", "Prévisualisation", "Anonymisation"];
|
|
|
|
const getCurrentStep = () => {
|
|
if (outputText) return 3;
|
|
if (uploadedFile || (sourceText && sourceText.trim())) return 2;
|
|
return 1;
|
|
};
|
|
|
|
// Fonction pour recommencer (retourner à l'état initial)
|
|
const handleRestart = () => {
|
|
setSourceText("");
|
|
setOutputText("");
|
|
setUploadedFile(null);
|
|
setError(null);
|
|
setIsLoadingFile(false);
|
|
setEntityMappings([]);
|
|
setIsExampleLoaded(false);
|
|
};
|
|
|
|
// Fonction pour mettre à jour les mappings depuis l'éditeur interactif
|
|
const handleMappingsUpdate = useCallback(
|
|
(updatedMappings: EntityMapping[]) => {
|
|
setEntityMappings(updatedMappings);
|
|
},
|
|
[]
|
|
);
|
|
|
|
// Hooks personnalisés pour la logique métier
|
|
const { handleFileChange } = useFileHandler({
|
|
setUploadedFile,
|
|
setSourceText,
|
|
setError,
|
|
setIsLoadingFile,
|
|
});
|
|
|
|
const { anonymizeData, isProcessing } = useAnonymization({
|
|
setOutputText,
|
|
setError,
|
|
setEntityMappings,
|
|
setAnonymizedText, // Passer la fonction pour stocker le texte anonymisé
|
|
});
|
|
|
|
const { copyToClipboard, downloadText } = useDownloadActions({
|
|
outputText,
|
|
entityMappings,
|
|
anonymizedText, // Passer le texte anonymisé de Presidio
|
|
});
|
|
|
|
// Fonction wrapper pour appeler anonymizeData avec les bonnes données
|
|
const handleAnonymize = (category?: string) => {
|
|
anonymizeData({ file: uploadedFile, text: sourceText, category });
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen w-full overflow-hidden">
|
|
{/* Main Content */}
|
|
<div className="max-w-6xl mx-auto px-2 sm:px-4 py-4 sm:py-8 space-y-4">
|
|
{/* Progress Bar */}
|
|
<ProgressBar currentStep={getCurrentStep()} steps={progressSteps} />
|
|
|
|
{/* Upload Section */}
|
|
<div className="bg-white rounded-2xl border border-gray-50 overflow-hidden">
|
|
<div className="p-1 sm:p-3">
|
|
<FileUploadComponent
|
|
uploadedFile={uploadedFile}
|
|
handleFileChange={handleFileChange}
|
|
sourceText={sourceText}
|
|
setSourceText={setSourceText}
|
|
setUploadedFile={setUploadedFile}
|
|
onAnonymize={handleAnonymize}
|
|
isProcessing={isProcessing}
|
|
canAnonymize={
|
|
uploadedFile !== null ||
|
|
Boolean(sourceText && sourceText.trim())
|
|
}
|
|
isLoadingFile={isLoadingFile}
|
|
onRestart={handleRestart}
|
|
outputText={outputText}
|
|
copyToClipboard={copyToClipboard}
|
|
downloadText={downloadText}
|
|
isExampleLoaded={isExampleLoaded}
|
|
setIsExampleLoaded={setIsExampleLoaded}
|
|
entityMappings={entityMappings}
|
|
onMappingsUpdate={handleMappingsUpdate}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Entity Mapping Table - Seulement si outputText existe */}
|
|
{outputText && (
|
|
<div className="bg-white rounded-2xl border border-gray-100 overflow-hidden">
|
|
<div className="p-1 sm:p-3">
|
|
<EntityMappingTable mappings={entityMappings} />
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Error Message - Version améliorée */}
|
|
{error && (
|
|
<div className="bg-red-50 border border-red-200 rounded-xl p-3 sm:p-4 mx-2 sm:mx-0">
|
|
<div className="flex items-start space-x-3">
|
|
<svg
|
|
className="w-5 h-5 text-red-500 flex-shrink-0 mt-0.5"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
strokeWidth={2}
|
|
d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
/>
|
|
</svg>
|
|
<div className="flex-1">
|
|
<h3 className="text-red-800 text-sm font-semibold mb-2">
|
|
{error.includes("scanné")
|
|
? "📄 PDF Scanné Détecté"
|
|
: error.includes("HTTP")
|
|
? "🚨 Erreur de Traitement"
|
|
: "⚠️ Erreur"}
|
|
</h3>
|
|
<div className="text-red-700 text-xs sm:text-sm leading-relaxed">
|
|
{error.split("\n").map((line, index) => (
|
|
<div key={index} className={index > 0 ? "mt-2" : ""}>
|
|
{line.startsWith("💡") ? (
|
|
<div className="bg-blue-50 border border-blue-200 rounded-lg p-3 mt-3">
|
|
<div className="text-blue-800 font-medium text-sm">
|
|
{line}
|
|
</div>
|
|
</div>
|
|
) : line.startsWith("-") ? (
|
|
<div className="ml-4 text-blue-700">{line}</div>
|
|
) : (
|
|
line
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|