import { useState } from 'react'; import { Link } from 'react-router-dom'; import { VariabilityAttributes, AttributeValue } from '../api/client'; interface VariabilityPanelProps { attributes: VariabilityAttributes; } export function VariabilityPanel({ attributes }: VariabilityPanelProps) { const [showModal, setShowModal] = useState<{ type: string; title: string; items: string[]; total: number; } | null>(null); const [loading, setLoading] = useState(false); // Fonction pour charger la liste des IPs associées const loadAssociatedIPs = async (attrType: string, value: string, total: number) => { setLoading(true); try { const response = await fetch(`/api/variability/${attrType}/${encodeURIComponent(value)}/ips?limit=100`); const data = await response.json(); setShowModal({ type: 'ips', title: `${data.total || total} IPs associées à ${value}`, items: data.ips || [], total: data.total || total, }); } catch (error) { console.error('Erreur chargement IPs:', error); } setLoading(false); }; return (

Variabilité des Attributs

{/* JA4 Fingerprints */} {attributes.ja4 && attributes.ja4.length > 0 && ( item.value} getLink={(item) => `/investigation/ja4/${encodeURIComponent(item.value)}`} onViewAll={(value, count) => loadAssociatedIPs('ja4', value, count)} showViewAll viewAllLabel="Voir les IPs" /> )} {/* User-Agents */} {attributes.user_agents && attributes.user_agents.length > 0 && (

User-Agents ({attributes.user_agents.length})

{attributes.user_agents.slice(0, 10).map((item, index) => (
{item.value}
{item.count}
{item.percentage?.toFixed(1)}%
))}
{attributes.user_agents.length > 10 && (

... et {attributes.user_agents.length - 10} autres (top 10 affiché)

)}
)} {/* Pays */} {attributes.countries && attributes.countries.length > 0 && ( item.value} getLink={(item) => `/detections/country/${encodeURIComponent(item.value)}`} onViewAll={(value, count) => loadAssociatedIPs('country', value, count)} showViewAll viewAllLabel="Voir les IPs" /> )} {/* ASN */} {attributes.asns && attributes.asns.length > 0 && ( item.value} getLink={(item) => { const asnNumber = item.value.match(/AS(\d+)/)?.[1] || item.value; return `/detections/asn/${encodeURIComponent(asnNumber)}`; }} onViewAll={(value, count) => loadAssociatedIPs('asn', value, count)} showViewAll viewAllLabel="Voir les IPs" /> )} {/* Hosts */} {attributes.hosts && attributes.hosts.length > 0 && ( item.value} getLink={(item) => `/detections/host/${encodeURIComponent(item.value)}`} onViewAll={(value, count) => loadAssociatedIPs('host', value, count)} showViewAll viewAllLabel="Voir les IPs" /> )} {/* Threat Levels */} {attributes.threat_levels && attributes.threat_levels.length > 0 && ( item.value} getLink={(item) => `/detections?threat_level=${encodeURIComponent(item.value)}`} onViewAll={(value, count) => loadAssociatedIPs('threat_level', value, count)} showViewAll viewAllLabel="Voir les IPs" /> )} {/* Modal pour afficher la liste complète */} {showModal && (
{/* Header */}

{showModal.title}

{/* Content */}
{loading ? (
Chargement...
) : showModal.items.length > 0 ? (
{showModal.items.map((item, index) => (
{item}
))} {showModal.total > showModal.items.length && (

Affichage de {showModal.items.length} sur {showModal.total} éléments

)}
) : (
Aucune donnée disponible
)}
{/* Footer */}
)}
); } // Composant AttributeSection function AttributeSection({ title, items, getValue, getLink, onViewAll, showViewAll = false, viewAllLabel = 'Voir les IPs', }: { title: string; items: AttributeValue[]; getValue: (item: AttributeValue) => string; getLink: (item: AttributeValue) => string; onViewAll?: (value: string, count: number) => void; showViewAll?: boolean; viewAllLabel?: string; }) { const displayItems = items.slice(0, 10); return (

{title} ({items.length})

{showViewAll && items.length > 0 && ( )}
{displayItems.map((item, index) => ( ))}
{items.length > 10 && (

... et {items.length - 10} autres (top 10 affiché)

)}
); } // Composant AttributeRow function AttributeRow({ value, getValue, getLink, }: { value: AttributeValue; getValue: (item: AttributeValue) => string; getLink: (item: AttributeValue) => string; }) { const percentage = value.percentage || 0; return (
{getValue(value)}
{value.count}
{percentage.toFixed(1)}%
); } // Helper pour la couleur de la barre function getPercentageColor(percentage: number): string { if (percentage >= 50) return 'bg-threat-critical'; if (percentage >= 25) return 'bg-threat-high'; if (percentage >= 10) return 'bg-threat-medium'; return 'bg-threat-low'; }