89 lines
3.3 KiB
TypeScript
89 lines
3.3 KiB
TypeScript
import { ReactNode } from 'react';
|
|
|
|
export const highlightEntities = (text: string): ReactNode => {
|
|
if (!text) return text;
|
|
|
|
// Patterns pour les différents types d'entités Presidio
|
|
const patterns = [
|
|
// ✅ Patterns Presidio existants
|
|
{ regex: /<PERSON>/g, className: "bg-blue-200 text-blue-800", label: "Personne" },
|
|
{ regex: /<EMAIL_ADDRESS>/g, className: "bg-green-200 text-green-800", label: "Email" },
|
|
{ regex: /<PHONE_NUMBER>/g, className: "bg-purple-200 text-purple-800", label: "Téléphone" },
|
|
{ regex: /<LOCATION>/g, className: "bg-red-200 text-red-800", label: "Lieu" },
|
|
{ regex: /<IBAN>/g, className: "bg-yellow-200 text-yellow-800", label: "IBAN" },
|
|
{ regex: /<ORGANIZATION>/g, className: "bg-indigo-200 text-indigo-800", label: "Organisation" },
|
|
|
|
// 🆕 Patterns spécifiques détectés dans votre texte
|
|
{ regex: /<FLEXIBLE_DATE>/g, className: "bg-pink-200 text-pink-800", label: "Date" },
|
|
{ regex: /<BE_ADDRESS>/g, className: "bg-cyan-200 text-cyan-800", label: "Adresse BE" },
|
|
{ regex: /<BE_PHONE_NUMBER>/g, className: "bg-violet-200 text-violet-800", label: "Tél. BE" },
|
|
{ regex: /<BE_ENTERPRISE_NUMBER>/g, className: "bg-orange-200 text-orange-800", label: "N° Entreprise BE" },
|
|
{ regex: /<BE_PRO_ID>/g, className: "bg-emerald-200 text-emerald-800", label: "ID Professionnel BE" },
|
|
{ regex: /<IP_ADDRESS>/g, className: "bg-slate-200 text-slate-800", label: "Adresse IP" },
|
|
|
|
// Anciens patterns (pour compatibilité)
|
|
{ regex: /\[([^\]]+)\]/g, className: "bg-[#f7ab6e] text-[#092727]", label: "Anonymisé" },
|
|
];
|
|
|
|
const replacements: Array<{ start: number; end: number; element: ReactNode }> = [];
|
|
|
|
// Trouver toutes les correspondances
|
|
patterns.forEach((pattern, patternIndex) => {
|
|
const regex = new RegExp(pattern.regex.source, pattern.regex.flags);
|
|
let match;
|
|
|
|
while ((match = regex.exec(text)) !== null) {
|
|
const start = match.index;
|
|
const end = match.index + match[0].length;
|
|
|
|
// Vérifier qu'il n'y a pas de chevauchement avec des remplacements existants
|
|
const hasOverlap = replacements.some(r =>
|
|
(start >= r.start && start < r.end) || (end > r.start && end <= r.end)
|
|
);
|
|
|
|
if (!hasOverlap) {
|
|
const element = (
|
|
<span
|
|
key={`${patternIndex}-${start}`}
|
|
className={`${pattern.className} px-2 py-1 rounded-md font-medium text-xs inline-block mx-0.5 shadow-sm border`}
|
|
title={`${pattern.label} anonymisé`}
|
|
>
|
|
{match[0]}
|
|
</span>
|
|
);
|
|
|
|
replacements.push({ start, end, element });
|
|
}
|
|
}
|
|
});
|
|
|
|
// Trier les remplacements par position
|
|
replacements.sort((a, b) => a.start - b.start);
|
|
|
|
// Construire le résultat final
|
|
if (replacements.length === 0) {
|
|
return text;
|
|
}
|
|
|
|
const parts: ReactNode[] = [];
|
|
let lastIndex = 0;
|
|
|
|
replacements.forEach((replacement) => {
|
|
// Ajouter le texte avant le remplacement
|
|
if (replacement.start > lastIndex) {
|
|
parts.push(text.slice(lastIndex, replacement.start));
|
|
}
|
|
|
|
// Ajouter l'élément de remplacement
|
|
parts.push(replacement.element);
|
|
|
|
lastIndex = replacement.end;
|
|
});
|
|
|
|
// Ajouter le texte restant
|
|
if (lastIndex < text.length) {
|
|
parts.push(text.slice(lastIndex));
|
|
}
|
|
|
|
return parts;
|
|
}; |