diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 82784a6..4155bb7 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -321,6 +321,57 @@ function RouteTracker() { return null; } +// ─── MainContent : layout adaptatif selon la route ─────────────────────────── +// Les vues "canvas" ont besoin d'une hauteur fixe sans padding +// pour que leurs colonnes scroll indépendamment. +const FULLHEIGHT_ROUTES = ['/clustering']; + +function MainContent({ counts: _counts }: { counts: AlertCounts | null }) { + const location = useLocation(); + const isFullHeight = FULLHEIGHT_ROUTES.some(r => location.pathname.startsWith(r)); + + if (isFullHeight) { + return ( +
+ + } /> + +
+ ); + } + + return ( +
+ + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + +
+ ); +} + // ─── App ────────────────────────────────────────────────────────────────────── export default function App() { @@ -362,36 +413,8 @@ export default function App() { {/* Fixed top header */} - {/* Scrollable page content */} -
- - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - -
+ {/* Page content — full-height sans padding pour les vues canvas */} + diff --git a/frontend/src/components/ClusteringView.tsx b/frontend/src/components/ClusteringView.tsx index ccadef6..3b9bebe 100644 --- a/frontend/src/components/ClusteringView.tsx +++ b/frontend/src/components/ClusteringView.tsx @@ -330,8 +330,8 @@ export default function ClusteringView() { return (
- {/* ── Panneau gauche ── */} -
+ {/* ── Panneau gauche (scroll indépendant) ── */} +

🔬 Clustering IPs

Rendu WebGL · K-means++ sur toutes les IPs

@@ -440,12 +440,55 @@ export default function ClusteringView() {
{/* ── Canvas WebGL (deck.gl) ── */} -
- {!data && !loading && !computing && ( -
- Cliquez sur Recalculer pour démarrer +
+ + {/* Animation de calcul — remplace le canvas pendant le traitement */} + {(computing || loading) && ( +
+ {/* Noeuds pulsants animés */} +
+ {/* Anneau tournant */} +
+
+
+
+
+
+ {/* Noeuds orbitaux représentant les clusters */} + {([0,1,2,3,4,5,6,7] as const).map((i) => { + const angle = (i / 8) * 2 * Math.PI; + const r = 88; + const x = 50 + (r / 1.12) * Math.cos(angle); + const y = 50 + (r / 1.12) * Math.sin(angle); + const colors = ['#dc2626','#f97316','#eab308','#22c55e','#3b82f6','#8b5cf6','#ec4899','#14b8a6']; + return ( +
+ ); + })} + {/* Centre */} +
+
🔬
+
+
+

Clustering en cours…

+

K-means++ · 31 features · toutes les IPs

+

Mise à jour automatique toutes les 3 secondes

)} + + {/* Message état vide */} + {!data && !loading && !computing && ( +
+ 🔬 + Cliquez sur Recalculer pour démarrer +
+ )} + +
{/* Header */}