import { ReactNode } from "react"; export const patterns = [ { regex: //g, className: "bg-blue-200 text-blue-800", label: "Personne", }, { regex: //g, className: "bg-green-200 text-green-800", label: "Adresse Email", }, { regex: //g, className: "bg-purple-200 text-purple-800", label: "N° de Téléphone", }, { regex: //g, className: "bg-red-200 text-red-800", label: "Lieu", }, { regex: //g, className: "bg-yellow-200 text-yellow-800", label: "IBAN", }, { regex: //g, className: "bg-indigo-200 text-indigo-800", label: "Organisation", }, { regex: //g, className: "bg-pink-200 text-pink-800", label: "Date", }, { regex: //g, className: "bg-cyan-200 text-cyan-800", label: "Adresse (BE)", }, { regex: //g, className: "bg-violet-200 text-violet-800", label: "N° de Tél. (BE)", }, { regex: //g, className: "bg-orange-200 text-orange-800", label: "Carte de Crédit", }, { regex: //g, className: "bg-teal-200 text-teal-800", label: "URL", }, { regex: //g, className: "bg-gray-300 text-gray-900", label: "Adresse IP", }, { regex: //g, className: "bg-pink-300 text-pink-900", label: "Date & Heure", }, { regex: //g, className: "bg-red-300 text-red-900", label: "N° Registre National", }, { regex: //g, className: "bg-yellow-300 text-yellow-900", label: "TVA (BE)", }, { regex: //g, className: "bg-lime-200 text-lime-800", label: "N° d'entreprise (BE)", }, { regex: //g, className: "bg-emerald-200 text-emerald-800", label: "ID Pro (BE)", }, ]; interface EntityMapping { originalValue: string; anonymizedValue: string; entityType: string; startIndex: number; endIndex: number; } export const highlightEntities = ( text: string, entityMappings?: EntityMapping[] ): ReactNode => { if (!text) return text; 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; let matchCount = 0; // Compteur pour ce type d'entité 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) { matchCount++; // Incrémenter le compteur pour ce type let displayLabel = pattern.label; const displayClass = pattern.className; if (entityMappings) { // Chercher le mapping correspondant à cette position et ce type const matchingMapping = entityMappings.find( (mapping) => mapping.entityType === pattern.label ); if (matchingMapping) { // Utiliser directement la valeur anonymisée du mapping // qui correspond à cette occurrence (basée sur l'ordre d'apparition) const entityType = pattern.label; const mappingsOfThisType = entityMappings.filter( (m) => m.entityType === entityType ); // Prendre le mapping correspondant à cette occurrence if (mappingsOfThisType[matchCount - 1]) { displayLabel = mappingsOfThisType[matchCount - 1].anonymizedValue; } else { // Fallback si pas de mapping trouvé displayLabel = `${entityType} [${matchCount}]`; } } } const element = ( {displayLabel} ); 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; };