glisser
This commit is contained in:
@@ -117,41 +117,79 @@ export const useAnonymization = ({
|
||||
}
|
||||
});
|
||||
|
||||
const seen = new Set<string>();
|
||||
// 2. Créer un mapping basé sur l'ordre d'apparition dans le texte anonymisé
|
||||
const uniqueMappings: EntityMapping[] = [];
|
||||
const addedCounts = new Map<string, number>();
|
||||
|
||||
// 2. N'ajouter que les entités réellement anonymisées avec un compteur
|
||||
data.analyzerResults
|
||||
.sort((a, b) => a.start - b.start) // Trier par ordre d'apparition
|
||||
.forEach((result) => {
|
||||
const entityType = result.entity_type;
|
||||
const maxCount = tagCounts.get(entityType) || 0;
|
||||
const currentCount = addedCounts.get(entityType) || 0;
|
||||
|
||||
if (currentCount < maxCount) {
|
||||
const originalValue = textToProcess.substring(
|
||||
result.start,
|
||||
result.end
|
||||
);
|
||||
const frenchLabel = entityTypeMap.get(entityType) || entityType;
|
||||
const uniqueKey = `${frenchLabel}|${originalValue}`;
|
||||
|
||||
if (!seen.has(uniqueKey)) {
|
||||
const newCount = (addedCounts.get(entityType) || 0) + 1;
|
||||
addedCounts.set(entityType, newCount);
|
||||
|
||||
uniqueMappings.push({
|
||||
entityType: frenchLabel,
|
||||
originalValue: originalValue,
|
||||
anonymizedValue: `${frenchLabel} [${newCount}]`,
|
||||
startIndex: result.start,
|
||||
endIndex: result.end,
|
||||
});
|
||||
seen.add(uniqueKey);
|
||||
}
|
||||
|
||||
// Vérifier que les données nécessaires sont disponibles
|
||||
if (!data.analyzerResults || !data.anonymizedText) {
|
||||
setEntityMappings([]);
|
||||
return;
|
||||
}
|
||||
|
||||
const entityCounters = new Map<string, number>();
|
||||
|
||||
// Parcourir le texte anonymisé pour trouver les tags dans l'ordre
|
||||
const anonymizedText = data.anonymizedText;
|
||||
const allMatches: Array<{
|
||||
match: RegExpMatchArray;
|
||||
entityType: string;
|
||||
position: number;
|
||||
}> = [];
|
||||
|
||||
// Trouver tous les tags dans le texte anonymisé
|
||||
patterns.forEach(pattern => {
|
||||
const entityTypeKey = pattern.regex.toString().match(/<([A-Z_]+)>/)?.[1];
|
||||
if (entityTypeKey) {
|
||||
const regex = new RegExp(pattern.regex.source, 'g');
|
||||
let match;
|
||||
while ((match = regex.exec(anonymizedText)) !== null) {
|
||||
allMatches.push({
|
||||
match,
|
||||
entityType: entityTypeKey,
|
||||
position: match.index
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Trier par position dans le texte anonymisé
|
||||
allMatches.sort((a, b) => a.position - b.position);
|
||||
|
||||
// Créer les mappings dans l'ordre d'apparition
|
||||
const seen = new Set<string>();
|
||||
allMatches.forEach(({ entityType }) => {
|
||||
const frenchLabel = entityTypeMap.get(entityType) || entityType;
|
||||
const currentCount = (entityCounters.get(entityType) || 0) + 1;
|
||||
entityCounters.set(entityType, currentCount);
|
||||
|
||||
// Trouver l'entité correspondante dans les résultats d'analyse
|
||||
const correspondingResult = data.analyzerResults
|
||||
?.filter(result => result.entity_type === entityType)
|
||||
.find(result => {
|
||||
const originalValue = textToProcess.substring(result.start, result.end);
|
||||
const uniqueKey = `${frenchLabel}|${originalValue}|${currentCount}`;
|
||||
return !seen.has(uniqueKey);
|
||||
});
|
||||
|
||||
if (correspondingResult) {
|
||||
const originalValue = textToProcess.substring(
|
||||
correspondingResult.start,
|
||||
correspondingResult.end
|
||||
);
|
||||
const uniqueKey = `${frenchLabel}|${originalValue}|${currentCount}`;
|
||||
|
||||
if (!seen.has(uniqueKey)) {
|
||||
uniqueMappings.push({
|
||||
entityType: frenchLabel,
|
||||
originalValue: originalValue,
|
||||
anonymizedValue: `${frenchLabel} [${currentCount}]`,
|
||||
startIndex: correspondingResult.start,
|
||||
endIndex: correspondingResult.end,
|
||||
});
|
||||
seen.add(uniqueKey);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setEntityMappings(uniqueMappings);
|
||||
} else if (data.text) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import { SampleTextComponent } from "./SampleTextComponent";
|
||||
import { SupportedDataTypes } from "./SupportedDataTypes";
|
||||
import { AnonymizationInterface } from "./AnonymizationInterface";
|
||||
import { highlightEntities } from "../utils/highlightEntities";
|
||||
import { useState } from "react";
|
||||
|
||||
interface EntityMapping {
|
||||
originalValue: string;
|
||||
@@ -57,6 +58,86 @@ export const FileUploadComponent = ({
|
||||
setIsExampleLoaded,
|
||||
entityMappings, // Ajouter cette prop ici
|
||||
}: FileUploadComponentProps) => {
|
||||
const [isDragOver, setIsDragOver] = useState(false);
|
||||
|
||||
// Fonction pour valider le type de fichier
|
||||
const isValidFileType = (file: File) => {
|
||||
const allowedTypes = ["text/plain", "application/pdf"];
|
||||
const allowedExtensions = [".txt", ".pdf"];
|
||||
|
||||
return (
|
||||
allowedTypes.includes(file.type) ||
|
||||
allowedExtensions.some((ext) => file.name.toLowerCase().endsWith(ext))
|
||||
);
|
||||
};
|
||||
|
||||
// Gestionnaires de glisser-déposer
|
||||
const handleDragOver = (e: React.DragEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
const handleDragEnter = (e: React.DragEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsDragOver(true);
|
||||
};
|
||||
|
||||
const handleDragLeave = (e: React.DragEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
// Vérifier si on quitte vraiment la zone de drop
|
||||
if (!e.currentTarget.contains(e.relatedTarget as Node)) {
|
||||
setIsDragOver(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDrop = (e: React.DragEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsDragOver(false);
|
||||
|
||||
const files = Array.from(e.dataTransfer.files);
|
||||
if (files.length > 0) {
|
||||
const file = files[0];
|
||||
|
||||
// Vérifier le type de fichier
|
||||
if (!isValidFileType(file)) {
|
||||
alert(
|
||||
"Type de fichier non supporté. Veuillez sélectionner un fichier PDF ou TXT."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Vérifier la taille (5MB max)
|
||||
if (file.size > 5 * 1024 * 1024) {
|
||||
alert("Le fichier est trop volumineux. Taille maximale : 5MB.");
|
||||
return;
|
||||
}
|
||||
|
||||
const syntheticEvent = {
|
||||
target: { files: [file] },
|
||||
} as unknown as React.ChangeEvent<HTMLInputElement>;
|
||||
|
||||
handleFileChange(syntheticEvent);
|
||||
}
|
||||
};
|
||||
|
||||
// Gestionnaire de changement de fichier modifié pour valider le type
|
||||
const handleFileChangeWithValidation = (
|
||||
e: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file && !isValidFileType(file)) {
|
||||
alert(
|
||||
"Type de fichier non supporté. Veuillez sélectionner un fichier PDF ou TXT."
|
||||
);
|
||||
e.target.value = ""; // Reset l'input
|
||||
return;
|
||||
}
|
||||
handleFileChange(e);
|
||||
};
|
||||
|
||||
// On passe en preview seulement si :
|
||||
// 1. Un fichier est uploadé OU
|
||||
// 2. On a un résultat d'anonymisation
|
||||
@@ -144,7 +225,7 @@ export const FileUploadComponent = ({
|
||||
<div className="text-xs sm:text-sm text-gray-700 whitespace-pre-wrap break-words overflow-wrap-anywhere leading-relaxed">
|
||||
{highlightEntities(
|
||||
outputText || "Aucun contenu à afficher",
|
||||
entityMappings // Ajouter les mappings ici
|
||||
entityMappings
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -410,30 +491,59 @@ export const FileUploadComponent = ({
|
||||
</div>
|
||||
|
||||
{/* Colonne droite - Zone upload */}
|
||||
<div className="border-2 border-dashed border-[#092727] rounded-xl bg-gray-50 hover:bg-gray-100 hover:border-[#0a3030] transition-all duration-300">
|
||||
<div
|
||||
className={`border-2 border-dashed rounded-xl transition-all duration-300 ${
|
||||
isDragOver
|
||||
? "border-[#f7ab6e] bg-[#f7ab6e]/10 scale-105"
|
||||
: "border-[#092727] bg-gray-50 hover:bg-gray-100 hover:border-[#0a3030]"
|
||||
}`}
|
||||
onDragOver={handleDragOver}
|
||||
onDragEnter={handleDragEnter}
|
||||
onDragLeave={handleDragLeave}
|
||||
onDrop={handleDrop}
|
||||
>
|
||||
<label className="flex flex-col items-center justify-center cursor-pointer group p-3 sm:p-4 h-full min-h-[200px]">
|
||||
{/* Upload Icon */}
|
||||
<div className="w-10 h-10 bg-[#f7ab6e] rounded-full flex items-center justify-center mb-3 transition-colors duration-300">
|
||||
<div
|
||||
className={`w-10 h-10 rounded-full flex items-center justify-center mb-3 transition-colors duration-300 ${
|
||||
isDragOver ? "bg-[#f7ab6e] scale-110" : "bg-[#f7ab6e]"
|
||||
}`}
|
||||
>
|
||||
<Upload className="h-5 w-5 text-white" />
|
||||
</div>
|
||||
|
||||
{/* Titre */}
|
||||
<h3 className="text-lg font-semibold text-[#092727] mb-1 group-hover:text-[#0a3030] transition-colors duration-300 text-center">
|
||||
Déposez votre fichier ici
|
||||
<h3
|
||||
className={`text-lg font-semibold mb-1 transition-colors duration-300 text-center ${
|
||||
isDragOver
|
||||
? "text-[#f7ab6e]"
|
||||
: "text-[#092727] group-hover:text-[#0a3030]"
|
||||
}`}
|
||||
>
|
||||
{isDragOver
|
||||
? "Déposez votre fichier"
|
||||
: "Déposez votre fichier ici"}
|
||||
</h3>
|
||||
<p className="text-sm text-[#092727] opacity-80 mb-3 text-center group-hover:opacity-90 transition-opacity duration-300">
|
||||
<p
|
||||
className={`text-sm mb-3 text-center transition-opacity duration-300 ${
|
||||
isDragOver
|
||||
? "text-[#f7ab6e] opacity-90"
|
||||
: "text-[#092727] opacity-80 group-hover:opacity-90"
|
||||
}`}
|
||||
>
|
||||
ou cliquez pour sélectionner
|
||||
</p>
|
||||
|
||||
{/* File Info */}
|
||||
<div className="flex s items-center gap-1 text-xs text-[#092727] opacity-60">
|
||||
<span>📄 Fichiers TXT, PDF</span> - <span>Max 5MB</span>
|
||||
<div className="flex items-center gap-1 text-xs text-[#092727] opacity-60">
|
||||
<span>📄 Fichiers PDF et TXT uniquement</span> -{" "}
|
||||
<span>Max 5MB</span>
|
||||
</div>
|
||||
|
||||
{/* Hidden Input */}
|
||||
<input
|
||||
type="file"
|
||||
onChange={handleFileChange}
|
||||
onChange={handleFileChangeWithValidation}
|
||||
accept=".txt,.pdf"
|
||||
className="hidden"
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user