feat: système de tooltips universel sur tous les termes techniques
- Nouveau composant ui/Tooltip.tsx (createPortal → pas de clipping overflow) - Tooltip : bulle au survol avec ajustement viewport automatique - InfoTip : icône ⓘ avec bulle intégrée - Nouveau ui/tooltips.ts : 50+ définitions en français (clustering, ML features, TCP spoofing, general) - ui/DataTable.tsx : prop tooltip sur Column → InfoTip dans les en-têtes - ClusteringView : ⓘ sur Sensibilité, k, arêtes, toutes les stats, légende CRITICAL/HIGH/MEDIUM/LOW, sidebar (score risque, radar, TTL/MSS/Score ML/Vélocité/Headless/UA-CH) - MLFeaturesView : <title> SVG sur axes radar et scatter, tooltip sur colonnes Fuzzing/Type/Signaux - TcpSpoofingView : tooltip sur colonnes TTL/MSS/Scale/OS/Confiance/Verdict - App.tsx : tooltip sur Alertes 24h et niveaux CRITICAL/HIGH/MEDIUM Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
import { useState, useEffect, useMemo } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import DataTable, { Column } from './ui/DataTable';
|
||||
import { TIPS } from './ui/tooltips';
|
||||
|
||||
// ─── Types ────────────────────────────────────────────────────────────────────
|
||||
|
||||
@ -100,14 +101,14 @@ function ErrorMessage({ message }: { message: string }) {
|
||||
// ─── Radar Chart (SVG octagonal) ─────────────────────────────────────────────
|
||||
|
||||
const RADAR_AXES = [
|
||||
{ key: 'fuzzing_score', label: 'Fuzzing' },
|
||||
{ key: 'velocity_score', label: 'Vélocité' },
|
||||
{ key: 'fake_nav_score', label: 'Fausse nav' },
|
||||
{ key: 'ua_mismatch_score', label: 'UA/CH mismatch' },
|
||||
{ key: 'sni_mismatch_score', label: 'SNI mismatch' },
|
||||
{ key: 'orphan_score', label: 'Orphan ratio' },
|
||||
{ key: 'path_repetition_score', label: 'Répétition URL' },
|
||||
{ key: 'payload_anomaly_score', label: 'Payload anormal' },
|
||||
{ key: 'fuzzing_score', label: 'Fuzzing', tip: TIPS.fuzzing },
|
||||
{ key: 'velocity_score', label: 'Vélocité', tip: TIPS.velocity },
|
||||
{ key: 'fake_nav_score', label: 'Fausse nav', tip: TIPS.fake_nav },
|
||||
{ key: 'ua_mismatch_score', label: 'UA/CH mismatch', tip: TIPS.ua_mismatch },
|
||||
{ key: 'sni_mismatch_score', label: 'SNI mismatch', tip: TIPS.sni_mismatch },
|
||||
{ key: 'orphan_score', label: 'Orphan ratio', tip: TIPS.orphan_ratio },
|
||||
{ key: 'path_repetition_score', label: 'Répétition URL', tip: TIPS.path_repetition },
|
||||
{ key: 'payload_anomaly_score', label: 'Payload anormal', tip: TIPS.payload_anomaly },
|
||||
] as const;
|
||||
|
||||
type RadarKey = typeof RADAR_AXES[number]['key'];
|
||||
@ -182,7 +183,7 @@ function RadarChart({ data }: { data: RadarData }) {
|
||||
<circle key={i} cx={x} cy={y} r="3" fill="rgba(239,68,68,0.9)" />
|
||||
))}
|
||||
|
||||
{/* Axis labels */}
|
||||
{/* Axis labels — survolez pour la définition */}
|
||||
{RADAR_AXES.map((axis, i) => {
|
||||
const [x, y] = pointFor(i, maxR + 18);
|
||||
const anchor = x < cx - 5 ? 'end' : x > cx + 5 ? 'start' : 'middle';
|
||||
@ -195,7 +196,9 @@ function RadarChart({ data }: { data: RadarData }) {
|
||||
fontSize="10"
|
||||
fill="rgba(148,163,184,0.9)"
|
||||
dominantBaseline="middle"
|
||||
style={{ cursor: 'help' }}
|
||||
>
|
||||
<title>{axis.tip}</title>
|
||||
{axis.label}
|
||||
</text>
|
||||
);
|
||||
@ -257,7 +260,10 @@ function ScatterPlot({ points }: { points: ScatterPoint[] }) {
|
||||
{xTicks.map((v) => (
|
||||
<text key={v} x={toSvgX(v)} y={H - padB + 12} textAnchor="middle" fontSize="9" fill="rgba(148,163,184,0.7)">{v}</text>
|
||||
))}
|
||||
<text x={(W - padL - padR) / 2 + padL} y={H - 2} textAnchor="middle" fontSize="10" fill="rgba(148,163,184,0.8)">Fuzzing Index →</text>
|
||||
<text x={(W - padL - padR) / 2 + padL} y={H - 2} textAnchor="middle" fontSize="10" fill="rgba(148,163,184,0.8)" style={{ cursor: 'help' }}>
|
||||
<title>{TIPS.fuzzing_index}</title>
|
||||
Fuzzing Index →
|
||||
</text>
|
||||
|
||||
{/* Y axis */}
|
||||
<line x1={padL} y1={padT} x2={padL} y2={H - padB} stroke="rgba(100,116,139,0.4)" strokeWidth="1" />
|
||||
@ -361,6 +367,7 @@ function AnomaliesTable({
|
||||
{
|
||||
key: 'fuzzing_index',
|
||||
label: 'Fuzzing',
|
||||
tooltip: TIPS.fuzzing_index,
|
||||
align: 'right',
|
||||
render: (v: number) => (
|
||||
<span className={`px-1.5 py-0.5 rounded text-xs font-semibold ${fuzzingBadgeClass(v)}`}>
|
||||
@ -371,6 +378,7 @@ function AnomaliesTable({
|
||||
{
|
||||
key: 'attack_type',
|
||||
label: 'Type',
|
||||
tooltip: 'Type d\'attaque détecté : Brute Force 🔑, Flood 🌊, Scraper 🕷️, Spoofing 🎭, Scanner 🔍',
|
||||
render: (v: string) => (
|
||||
<span title={v} className="text-sm">{attackTypeEmoji(v)}</span>
|
||||
),
|
||||
@ -378,6 +386,7 @@ function AnomaliesTable({
|
||||
{
|
||||
key: '_signals',
|
||||
label: 'Signaux',
|
||||
tooltip: '⚠️ UA/CH mismatch · 🎭 Fausse navigation · 🔄 UA rotatif · 🌐 SNI mismatch',
|
||||
sortable: false,
|
||||
render: (_: unknown, row: MLAnomaly) => (
|
||||
<span className="flex gap-0.5">
|
||||
|
||||
Reference in New Issue
Block a user