feat: add plans, design system, CONSIGNE and brainstorm assets
Ajoute les trois plans d'implémentation (agent Rust, serveur Go, dashboard), les consignes de design, les fichiers de brainstorming et le .gitignore. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
<h2>Approches architecturales</h2>
|
||||
<p class="subtitle">Base de données + protocole temps réel — choisir une approche</p>
|
||||
|
||||
<div class="options">
|
||||
<div class="option" data-choice="a" onclick="toggleSelect(this)">
|
||||
<div class="letter">A</div>
|
||||
<div class="content">
|
||||
<h3>SQLite + SSE</h3>
|
||||
<p><strong>SQLite</strong> — fichier unique, pas de conteneur supplémentaire, SQL classique.<br>
|
||||
<strong>Server-Sent Events</strong> — flux unidirectionnel serveur→browser, plus simple que WebSocket.</p>
|
||||
<div class="pros-cons">
|
||||
<div class="pros"><h4>Avantages</h4><ul><li>Zéro dépendance supplémentaire</li><li>Déploiement ultra-simple</li><li>SSE natif dans les browsers modernes</li></ul></div>
|
||||
<div class="cons"><h4>Limites</h4><ul><li>Performances sur gros volumes de séries temporelles</li><li>SSE unidirectionnel (pas d'envoi de commandes futures)</li></ul></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="option" data-choice="b" onclick="toggleSelect(this)">
|
||||
<div class="letter">B</div>
|
||||
<div class="content">
|
||||
<h3>InfluxDB + WebSocket</h3>
|
||||
<p><strong>InfluxDB</strong> — time-series DB optimisée, requêtes temporelles puissantes.<br>
|
||||
<strong>WebSocket</strong> — bidirectionnel, extensible pour des commandes futures vers les agents.</p>
|
||||
<div class="pros-cons">
|
||||
<div class="pros"><h4>Avantages</h4><ul><li>Optimisé pour les métriques temporelles</li><li>WebSocket = extensible (alertes, commandes)</li><li>Grafana compatible nativement</li></ul></div>
|
||||
<div class="cons"><h4>Limites</h4>
|
||||
<ul><li>Conteneur InfluxDB supplémentaire dans le compose</li><li>Courbe d'apprentissage Flux/InfluxQL</li></ul></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="option" data-choice="c" onclick="toggleSelect(this)">
|
||||
<div class="letter">C</div>
|
||||
<div class="content">
|
||||
<h3>SQLite + WebSocket ⭐ Recommandé</h3>
|
||||
<p><strong>SQLite</strong> — simplicité opérationnelle, suffisant pour 20+ agents avec rétention configurable.<br>
|
||||
<strong>WebSocket</strong> — bidirectionnel dès le départ, sans surcoût opérationnel.</p>
|
||||
<div class="pros-cons">
|
||||
<div class="pros"><h4>Avantages</h4><ul><li>Pas de conteneur DB supplémentaire</li><li>WebSocket prêt pour extensions futures</li><li>Simple à debugger et sauvegarder</li></ul></div>
|
||||
<div class="cons"><h4>Limites</h4><ul><li>Requêtes temporelles moins expressives qu'InfluxDB</li><li>Scalabilité limitée au-delà de ~100 agents</li></ul></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
<div style="display:flex;align-items:center;justify-content:center;min-height:60vh;flex-direction:column;gap:16px">
|
||||
<p style="font-size:2em">⚙️</p>
|
||||
<p class="subtitle">Brainstorming Nanometrics — questions en cours dans le terminal...</p>
|
||||
</div>
|
||||
@@ -0,0 +1 @@
|
||||
{"type":"server-started","port":60787,"host":"127.0.0.1","url_host":"localhost","url":"http://localhost:60787","screen_dir":"/home/gilles/projects/nano_metrics/.superpowers/brainstorm/599106-1779425616/content","state_dir":"/home/gilles/projects/nano_metrics/.superpowers/brainstorm/599106-1779425616/state"}
|
||||
@@ -0,0 +1,44 @@
|
||||
<h2>Approches architecturales</h2>
|
||||
<p class="subtitle">Base de données + protocole temps réel — choisir une approche</p>
|
||||
|
||||
<div class="options">
|
||||
<div class="option" data-choice="a" onclick="toggleSelect(this)">
|
||||
<div class="letter">A</div>
|
||||
<div class="content">
|
||||
<h3>SQLite + SSE</h3>
|
||||
<p><strong>SQLite</strong> — fichier unique, pas de conteneur supplémentaire, SQL classique.<br>
|
||||
<strong>Server-Sent Events</strong> — flux unidirectionnel serveur→browser, plus simple que WebSocket.</p>
|
||||
<div class="pros-cons">
|
||||
<div class="pros"><h4>Avantages</h4><ul><li>Zéro dépendance supplémentaire</li><li>Déploiement ultra-simple</li><li>SSE natif dans les browsers modernes</li></ul></div>
|
||||
<div class="cons"><h4>Limites</h4><ul><li>Performances sur gros volumes de séries temporelles</li><li>SSE unidirectionnel (pas d'envoi de commandes futures)</li></ul></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="option" data-choice="b" onclick="toggleSelect(this)">
|
||||
<div class="letter">B</div>
|
||||
<div class="content">
|
||||
<h3>InfluxDB + WebSocket</h3>
|
||||
<p><strong>InfluxDB</strong> — time-series DB optimisée, requêtes temporelles puissantes.<br>
|
||||
<strong>WebSocket</strong> — bidirectionnel, extensible pour des commandes futures vers les agents.</p>
|
||||
<div class="pros-cons">
|
||||
<div class="pros"><h4>Avantages</h4><ul><li>Optimisé pour les métriques temporelles</li><li>WebSocket = extensible (alertes, commandes)</li><li>Grafana compatible nativement</li></ul></div>
|
||||
<div class="cons"><h4>Limites</h4>
|
||||
<ul><li>Conteneur InfluxDB supplémentaire dans le compose</li><li>Courbe d'apprentissage Flux/InfluxQL</li></ul></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="option" data-choice="c" onclick="toggleSelect(this)">
|
||||
<div class="letter">C</div>
|
||||
<div class="content">
|
||||
<h3>SQLite + WebSocket ⭐ Recommandé</h3>
|
||||
<p><strong>SQLite</strong> — simplicité opérationnelle, suffisant pour 20+ agents avec rétention configurable.<br>
|
||||
<strong>WebSocket</strong> — bidirectionnel dès le départ, sans surcoût opérationnel.</p>
|
||||
<div class="pros-cons">
|
||||
<div class="pros"><h4>Avantages</h4><ul><li>Pas de conteneur DB supplémentaire</li><li>WebSocket prêt pour extensions futures</li><li>Simple à debugger et sauvegarder</li></ul></div>
|
||||
<div class="cons"><h4>Limites</h4><ul><li>Requêtes temporelles moins expressives qu'InfluxDB</li><li>Scalabilité limitée au-delà de ~100 agents</li></ul></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,646 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Nanometrics — Layout A v2</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Share+Tech+Mono:wght@400&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<script src="/helper.js"></script>
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
:root[data-theme="dark"] {
|
||||
--accent:#fe8019; --accent-soft:#d65d0e; --accent-glow:rgba(254,128,25,0.3);
|
||||
--bg-0:#1d1813; --bg-1:#2a231d; --bg-2:#32291f; --bg-3:#3c332a; --bg-4:#4a3f33; --bg-5:#57493c;
|
||||
--ink-1:#f2e5c7; --ink-2:#d5c4a1; --ink-3:#a89984; --ink-4:#7c6f64;
|
||||
--ok:#4dbb26; --warn:#fabd2f; --err:#fb4934; --info:#83a598; --blue:#3db0d1;
|
||||
--border-1:rgba(255,255,255,0.06); --border-2:rgba(255,255,255,0.12); --border-3:rgba(255,255,255,0.22);
|
||||
--shadow-2:0 4px 16px rgba(0,0,0,0.5);
|
||||
--tile-3d:0 1px 0 rgba(255,255,255,0.08) inset,0 -1px 0 rgba(0,0,0,0.3) inset,0 4px 16px rgba(0,0,0,0.5);
|
||||
--font-ui:'Inter',system-ui,sans-serif; --font-mono:'JetBrains Mono',monospace; --font-terminal:'Share Tech Mono',monospace;
|
||||
}
|
||||
:root[data-theme="light"] {
|
||||
--accent:#af3a03; --accent-soft:#d65d0e; --accent-glow:rgba(175,58,3,0.2);
|
||||
--bg-0:#d5c4a1; --bg-1:#ebdbb2; --bg-2:#d5c4a1; --bg-3:#bdae93; --bg-4:#a89984; --bg-5:#928374;
|
||||
--ink-1:#3c3836; --ink-2:#504945; --ink-3:#665c54; --ink-4:#7c6f64;
|
||||
--ok:#3c911c; --warn:#b57614; --err:#9d0006; --info:#427b58; --blue:#2d82a3;
|
||||
--border-1:rgba(0,0,0,0.08); --border-2:rgba(0,0,0,0.15); --border-3:rgba(0,0,0,0.25);
|
||||
--shadow-2:0 4px 16px rgba(0,0,0,0.15);
|
||||
--tile-3d:0 1px 0 rgba(255,255,255,0.5) inset,0 -1px 0 rgba(0,0,0,0.1) inset,0 4px 16px rgba(0,0,0,0.12);
|
||||
--font-ui:'Inter',system-ui,sans-serif; --font-mono:'JetBrains Mono',monospace; --font-terminal:'Share Tech Mono',monospace;
|
||||
}
|
||||
|
||||
body { background:var(--bg-1); color:var(--ink-1); font-family:var(--font-ui); font-size:13px; height:100vh; display:flex; flex-direction:column; overflow:hidden; transition:background .2s, color .2s; }
|
||||
|
||||
/* ══ HEADER ══ */
|
||||
.header {
|
||||
background:var(--bg-2); border-bottom:1px solid var(--border-2);
|
||||
padding:0 20px; height:48px; display:flex; align-items:center; gap:14px; flex-shrink:0;
|
||||
}
|
||||
.header-logo { display:flex; align-items:center; gap:8px; }
|
||||
.header-logo .led { width:9px; height:9px; border-radius:50%; background:var(--accent); box-shadow:0 0 8px var(--accent-glow); animation:pulse 2s infinite; }
|
||||
@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:.5} }
|
||||
.header-logo .name { font-weight:700; font-size:14px; letter-spacing:.04em; color:var(--ink-1); font-family:var(--font-terminal); }
|
||||
.header-logo .version { font-size:10px; color:var(--ink-4); font-family:var(--font-terminal); }
|
||||
|
||||
.header-spacer { flex:1; }
|
||||
|
||||
.header-stats { display:flex; gap:16px; align-items:center; }
|
||||
.h-stat { display:flex; align-items:center; gap:6px; }
|
||||
.h-stat .s-label { font-size:10px; color:var(--ink-4); font-family:var(--font-terminal); letter-spacing:.06em; }
|
||||
.h-stat .s-count { font-family:var(--font-mono); font-size:13px; font-weight:600; }
|
||||
.h-stat .s-count.ok { color:var(--ok); }
|
||||
.h-stat .s-count.warn { color:var(--warn); }
|
||||
.h-stat .s-count.err { color:var(--err); }
|
||||
|
||||
.header-sep { width:1px; height:24px; background:var(--border-2); }
|
||||
|
||||
.icon-btn {
|
||||
width:34px; height:34px; border-radius:8px; border:1px solid var(--border-2);
|
||||
background:var(--bg-3); color:var(--ink-2); font-size:14px;
|
||||
display:flex; align-items:center; justify-content:center; cursor:pointer;
|
||||
transition:background .12s, color .12s;
|
||||
position:relative;
|
||||
}
|
||||
.icon-btn:hover { background:var(--bg-4); color:var(--accent); }
|
||||
.icon-btn:active { background:var(--bg-5); box-shadow:inset 0 2px 4px rgba(0,0,0,0.3); }
|
||||
.icon-btn .tooltip {
|
||||
position:absolute; top:calc(100% + 6px); right:0;
|
||||
background:var(--bg-0); color:var(--ink-1); font-size:11px; font-family:var(--font-ui);
|
||||
padding:4px 8px; border-radius:5px; border:1px solid var(--border-2);
|
||||
white-space:nowrap; opacity:0; pointer-events:none; transition:opacity .12s;
|
||||
}
|
||||
.icon-btn:hover .tooltip { opacity:1; }
|
||||
|
||||
/* ══ MAIN / GRID ══ */
|
||||
.main { flex:1; padding:16px; overflow-y:auto; }
|
||||
|
||||
.agents-grid {
|
||||
display:grid;
|
||||
grid-template-columns:repeat(auto-fill, minmax(240px, 1fr));
|
||||
gap:12px;
|
||||
}
|
||||
|
||||
.tile {
|
||||
background:var(--bg-3); border-radius:10px; padding:14px 16px;
|
||||
border:1px solid var(--border-1); box-shadow:var(--tile-3d);
|
||||
cursor:pointer; transition:border-color .15s, box-shadow .15s;
|
||||
display:flex; flex-direction:column; gap:10px;
|
||||
}
|
||||
.tile:hover { border-color:var(--border-3); box-shadow:var(--tile-3d), 0 0 0 1px var(--border-2); }
|
||||
.tile.warn-tile { border-color:rgba(250,189,47,.3); }
|
||||
.tile.err-tile { border-color:rgba(251,73,52,.35); box-shadow:var(--tile-3d),0 0 18px rgba(251,73,52,.12); }
|
||||
.tile.offline-tile { opacity:.55; }
|
||||
|
||||
/* tile header */
|
||||
.tile-head { display:flex; align-items:center; gap:8px; }
|
||||
.t-icon-wrap { width:28px; height:28px; border-radius:6px; background:var(--bg-4); display:flex; align-items:center; justify-content:center; color:var(--accent); font-size:13px; }
|
||||
.t-info { flex:1; min-width:0; }
|
||||
.t-hostname { font-weight:600; font-size:13px; color:var(--ink-1); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
||||
.t-ip { font-family:var(--font-mono); font-size:10px; color:var(--ink-4); }
|
||||
.t-led { width:8px; height:8px; border-radius:50%; flex-shrink:0; }
|
||||
.t-led.ok { background:var(--ok); box-shadow:0 0 6px var(--ok); }
|
||||
.t-led.warn { background:var(--warn); box-shadow:0 0 6px var(--warn); animation:pulse 1.5s infinite; }
|
||||
.t-led.err { background:var(--err); box-shadow:0 0 8px var(--err); animation:pulse 1s infinite; }
|
||||
.t-led.off { background:var(--ink-4); }
|
||||
|
||||
/* gauges */
|
||||
.tile-gauges { display:flex; flex-direction:column; gap:5px; }
|
||||
.gauge-row { display:flex; align-items:center; gap:8px; }
|
||||
.g-label { font-size:10px; color:var(--ink-4); font-family:var(--font-terminal); letter-spacing:.04em; width:28px; }
|
||||
.g-bar { flex:1; height:5px; border-radius:3px; background:var(--bg-1); overflow:hidden; }
|
||||
.g-fill { height:100%; border-radius:3px; background:var(--ok); transition:width .4s ease; }
|
||||
.g-fill.warn { background:var(--warn); }
|
||||
.g-fill.err { background:var(--err); }
|
||||
.g-val { font-family:var(--font-mono); font-size:11px; color:var(--ink-2); width:32px; text-align:right; }
|
||||
|
||||
/* tile footer */
|
||||
.tile-foot { display:flex; align-items:center; justify-content:space-between; }
|
||||
.t-uptime { font-family:var(--font-terminal); font-size:10px; color:var(--ink-4); }
|
||||
.t-uptime i { margin-right:3px; }
|
||||
|
||||
/* ══ FOOTER ══ */
|
||||
.footer {
|
||||
background:var(--bg-0); border-top:1px solid var(--border-2);
|
||||
height:28px; display:flex; align-items:center;
|
||||
font-family:var(--font-terminal); font-size:11px; color:var(--ink-4);
|
||||
flex-shrink:0;
|
||||
}
|
||||
.f-mode { background:var(--accent); color:var(--bg-0); padding:0 12px; height:100%; display:flex; align-items:center; font-weight:700; font-size:11px; }
|
||||
.f-cell { padding:0 14px; border-right:1px solid var(--border-1); display:flex; align-items:center; gap:6px; height:100%; }
|
||||
.f-cell i { font-size:10px; }
|
||||
.f-cell .f-val { font-family:var(--font-mono); color:var(--ink-2); }
|
||||
.f-cell .f-val.warn { color:var(--warn); }
|
||||
.f-cell .f-bar { width:40px; height:4px; border-radius:2px; background:var(--bg-3); overflow:hidden; display:inline-block; vertical-align:middle; margin-left:4px; }
|
||||
.f-cell .f-bar-fill { height:100%; border-radius:2px; background:var(--ok); }
|
||||
.f-cell .f-bar-fill.warn { background:var(--warn); }
|
||||
.f-spacer { flex:1; }
|
||||
.f-right { padding:0 14px; display:flex; align-items:center; gap:6px; height:100%; }
|
||||
.f-right .f-update { color:var(--ink-3); }
|
||||
|
||||
/* ══ POPUP ══ */
|
||||
.popup-overlay {
|
||||
position:fixed; inset:0; background:rgba(0,0,0,0.65); z-index:100;
|
||||
display:flex; align-items:center; justify-content:center;
|
||||
backdrop-filter:blur(2px);
|
||||
}
|
||||
.popup {
|
||||
background:var(--bg-2); border:1px solid var(--border-3); border-radius:12px;
|
||||
width:480px; max-width:95vw; box-shadow:0 24px 64px rgba(0,0,0,0.7);
|
||||
display:flex; flex-direction:column; overflow:hidden;
|
||||
}
|
||||
.popup-header {
|
||||
background:var(--bg-3); padding:14px 18px; border-bottom:1px solid var(--border-2);
|
||||
display:flex; align-items:center; gap:10px;
|
||||
}
|
||||
.popup-header .ph-icon { width:32px; height:32px; border-radius:8px; background:var(--bg-4); display:flex; align-items:center; justify-content:center; color:var(--accent); font-size:15px; }
|
||||
.popup-header .ph-info { flex:1; }
|
||||
.popup-header .ph-host { font-weight:700; font-size:14px; color:var(--ink-1); }
|
||||
.popup-header .ph-ip { font-family:var(--font-mono); font-size:11px; color:var(--ink-4); }
|
||||
.popup-header .ph-led { width:10px; height:10px; border-radius:50%; background:var(--ok); box-shadow:0 0 8px var(--ok); }
|
||||
.popup-header .close-btn { width:28px; height:28px; border-radius:6px; background:var(--bg-5); color:var(--ink-3); display:flex; align-items:center; justify-content:center; cursor:pointer; font-size:12px; }
|
||||
.popup-header .close-btn:hover { background:var(--err); color:white; }
|
||||
|
||||
.popup-body { padding:18px; display:flex; flex-direction:column; gap:16px; }
|
||||
|
||||
.popup-kpis { display:grid; grid-template-columns:repeat(4,1fr); gap:8px; }
|
||||
.kpi-card {
|
||||
background:var(--bg-3); border-radius:8px; padding:10px 12px;
|
||||
border:1px solid var(--border-1); box-shadow:var(--tile-3d);
|
||||
}
|
||||
.kpi-label { font-size:9px; color:var(--ink-4); font-family:var(--font-terminal); letter-spacing:.06em; margin-bottom:4px; }
|
||||
.kpi-value { font-family:var(--font-mono); font-size:20px; font-weight:700; color:var(--ink-1); line-height:1; }
|
||||
.kpi-value .unit { font-size:11px; color:var(--ink-3); font-weight:400; }
|
||||
.kpi-value.ok { color:var(--ok); }
|
||||
.kpi-value.warn { color:var(--warn); }
|
||||
.kpi-value.err { color:var(--err); }
|
||||
.kpi-sub { font-size:10px; color:var(--ink-4); font-family:var(--font-mono); margin-top:3px; }
|
||||
|
||||
.popup-section-title { font-size:10px; color:var(--ink-4); font-family:var(--font-terminal); letter-spacing:.08em; margin-bottom:8px; }
|
||||
|
||||
.popup-gauges { display:flex; flex-direction:column; gap:7px; }
|
||||
.pg-row { display:flex; align-items:center; gap:10px; }
|
||||
.pg-label { font-size:11px; color:var(--ink-3); width:80px; font-family:var(--font-terminal); }
|
||||
.pg-bar { flex:1; height:7px; border-radius:4px; background:var(--bg-1); overflow:hidden; }
|
||||
.pg-fill { height:100%; border-radius:4px; background:var(--ok); }
|
||||
.pg-fill.warn { background:var(--warn); }
|
||||
.pg-fill.err { background:var(--err); }
|
||||
.pg-val { font-family:var(--font-mono); font-size:12px; color:var(--ink-2); width:60px; text-align:right; }
|
||||
|
||||
.popup-meta { display:grid; grid-template-columns:1fr 1fr; gap:6px; }
|
||||
.meta-item { background:var(--bg-3); border-radius:6px; padding:8px 10px; border:1px solid var(--border-1); }
|
||||
.meta-label { font-size:9px; color:var(--ink-4); font-family:var(--font-terminal); letter-spacing:.06em; }
|
||||
.meta-value { font-family:var(--font-mono); font-size:12px; color:var(--ink-2); margin-top:2px; }
|
||||
|
||||
.popup-footer {
|
||||
padding:12px 18px; border-top:1px solid var(--border-2);
|
||||
display:flex; align-items:center; gap:10px;
|
||||
background:var(--bg-3);
|
||||
}
|
||||
.pf-uptime { font-family:var(--font-terminal); font-size:11px; color:var(--ink-4); flex:1; }
|
||||
.pf-btn {
|
||||
padding:6px 14px; border-radius:8px; border:1px solid var(--border-2);
|
||||
background:var(--bg-4); color:var(--ink-2); font-size:12px; font-family:var(--font-ui);
|
||||
cursor:pointer; display:flex; align-items:center; gap:6px;
|
||||
}
|
||||
.pf-btn:hover { background:var(--bg-5); }
|
||||
.pf-btn.primary { background:var(--accent); color:var(--bg-0); border-color:var(--accent-soft); font-weight:600; }
|
||||
.pf-btn.primary:hover { background:var(--accent-soft); }
|
||||
|
||||
/* ══ CONFIG PANEL ══ */
|
||||
.config-overlay {
|
||||
position:fixed; inset:0; background:rgba(0,0,0,0.65); z-index:100;
|
||||
display:flex; align-items:center; justify-content:center;
|
||||
backdrop-filter:blur(2px);
|
||||
}
|
||||
.config-panel {
|
||||
background:var(--bg-2); border:1px solid var(--border-3); border-radius:12px;
|
||||
width:380px; box-shadow:0 24px 64px rgba(0,0,0,0.7); overflow:hidden;
|
||||
}
|
||||
.config-header {
|
||||
background:var(--bg-3); padding:14px 18px; border-bottom:1px solid var(--border-2);
|
||||
display:flex; align-items:center; gap:10px;
|
||||
}
|
||||
.config-header i { color:var(--accent); font-size:16px; }
|
||||
.config-header .ch-title { flex:1; font-weight:600; font-size:14px; }
|
||||
.config-header .close-btn { width:28px; height:28px; border-radius:6px; background:var(--bg-5); color:var(--ink-3); display:flex; align-items:center; justify-content:center; cursor:pointer; font-size:12px; }
|
||||
.config-body { padding:18px; display:flex; flex-direction:column; gap:14px; }
|
||||
.config-group { display:flex; flex-direction:column; gap:6px; }
|
||||
.cg-label { font-size:10px; color:var(--ink-4); font-family:var(--font-terminal); letter-spacing:.08em; }
|
||||
.cg-row { display:flex; align-items:center; gap:10px; }
|
||||
.cg-row span { font-size:12px; color:var(--ink-3); min-width:80px; }
|
||||
.cg-slider { flex:1; accent-color:var(--accent); }
|
||||
.cg-val { font-family:var(--font-mono); font-size:12px; color:var(--ink-2); width:40px; text-align:right; }
|
||||
.cg-select {
|
||||
flex:1; background:var(--bg-3); border:1px solid var(--border-2); border-radius:6px;
|
||||
color:var(--ink-1); padding:6px 10px; font-size:12px; font-family:var(--font-ui);
|
||||
}
|
||||
.config-footer {
|
||||
padding:12px 18px; border-top:1px solid var(--border-2); background:var(--bg-3);
|
||||
display:flex; gap:8px; justify-content:flex-end;
|
||||
}
|
||||
|
||||
/* ══ NOTICE BAR ══ */
|
||||
.notice { background:var(--bg-3); border-bottom:1px solid var(--border-1); padding:6px 20px; display:flex; align-items:center; gap:8px; font-size:11px; color:var(--ink-3); }
|
||||
.notice i { color:var(--accent); }
|
||||
|
||||
/* scrollbar */
|
||||
::-webkit-scrollbar{width:6px} ::-webkit-scrollbar-track{background:var(--bg-1)} ::-webkit-scrollbar-thumb{background:var(--bg-4);border-radius:3px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- ══ HEADER ══ -->
|
||||
<div class="header">
|
||||
<div class="header-logo">
|
||||
<div class="led"></div>
|
||||
<span class="name">NANOMETRICS</span>
|
||||
<span class="version">v1.0</span>
|
||||
</div>
|
||||
|
||||
<div class="header-spacer"></div>
|
||||
|
||||
<div class="header-stats">
|
||||
<div class="h-stat">
|
||||
<span class="s-label">AGENTS</span>
|
||||
<span class="s-count" style="color:var(--ink-2)">8</span>
|
||||
</div>
|
||||
<div class="h-stat">
|
||||
<span class="s-label">OK</span>
|
||||
<span class="s-count ok">5</span>
|
||||
</div>
|
||||
<div class="h-stat">
|
||||
<span class="s-label">WARN</span>
|
||||
<span class="s-count warn">2</span>
|
||||
</div>
|
||||
<div class="h-stat">
|
||||
<span class="s-label">ERR</span>
|
||||
<span class="s-count err">1</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="header-sep"></div>
|
||||
|
||||
<!-- Thème clair/sombre -->
|
||||
<div class="icon-btn" onclick="toggleTheme()" id="theme-btn">
|
||||
<i class="fa-solid fa-moon" id="theme-icon"></i>
|
||||
<span class="tooltip">Basculer thème</span>
|
||||
</div>
|
||||
|
||||
<!-- Configuration -->
|
||||
<div class="icon-btn" onclick="document.getElementById('config-overlay').style.display='flex'">
|
||||
<i class="fa-solid fa-sliders"></i>
|
||||
<span class="tooltip">Configuration serveur</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══ MAIN ══ -->
|
||||
<div class="main">
|
||||
<div class="agents-grid" id="agents-grid">
|
||||
|
||||
<!-- tuile ok -->
|
||||
<div class="tile" onclick="document.getElementById('popup-overlay').style.display='flex'">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon-wrap"><i class="fa-solid fa-server"></i></div>
|
||||
<div class="t-info">
|
||||
<div class="t-hostname">srv-prod-01</div>
|
||||
<div class="t-ip">10.0.0.11</div>
|
||||
</div>
|
||||
<div class="t-led ok"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="gauge-row"><span class="g-label">CPU</span><div class="g-bar"><div class="g-fill" style="width:42%"></div></div><span class="g-val">42%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">RAM</span><div class="g-bar"><div class="g-fill" style="width:58%"></div></div><span class="g-val">58%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">DISK</span><div class="g-bar"><div class="g-fill" style="width:31%"></div></div><span class="g-val">31%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot">
|
||||
<span class="t-uptime"><i class="fa-solid fa-clock"></i>14j 6h</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- tuile warn -->
|
||||
<div class="tile warn-tile">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon-wrap"><i class="fa-solid fa-server"></i></div>
|
||||
<div class="t-info">
|
||||
<div class="t-hostname">srv-backup-02</div>
|
||||
<div class="t-ip">10.0.0.12</div>
|
||||
</div>
|
||||
<div class="t-led warn"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="gauge-row"><span class="g-label">CPU</span><div class="g-bar"><div class="g-fill warn" style="width:78%"></div></div><span class="g-val">78%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">RAM</span><div class="g-bar"><div class="g-fill warn" style="width:72%"></div></div><span class="g-val">72%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">DISK</span><div class="g-bar"><div class="g-fill" style="width:20%"></div></div><span class="g-val">20%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot">
|
||||
<span class="t-uptime"><i class="fa-solid fa-clock"></i>3j 14h</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- tuile err -->
|
||||
<div class="tile err-tile">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon-wrap" style="color:var(--err)"><i class="fa-solid fa-microchip"></i></div>
|
||||
<div class="t-info">
|
||||
<div class="t-hostname">rpi-sensor-03</div>
|
||||
<div class="t-ip">10.0.0.23</div>
|
||||
</div>
|
||||
<div class="t-led err"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="gauge-row"><span class="g-label">CPU</span><div class="g-bar"><div class="g-fill" style="width:12%"></div></div><span class="g-val">12%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">RAM</span><div class="g-bar"><div class="g-fill" style="width:38%"></div></div><span class="g-val">38%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">DISK</span><div class="g-bar"><div class="g-fill err" style="width:94%"></div></div><span class="g-val">94%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot">
|
||||
<span class="t-uptime"><i class="fa-solid fa-clock"></i>62j 1h</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- tuile ok -->
|
||||
<div class="tile">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon-wrap"><i class="fa-solid fa-hard-drive"></i></div>
|
||||
<div class="t-info">
|
||||
<div class="t-hostname">nas-storage-04</div>
|
||||
<div class="t-ip">10.0.0.30</div>
|
||||
</div>
|
||||
<div class="t-led ok"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="gauge-row"><span class="g-label">CPU</span><div class="g-bar"><div class="g-fill" style="width:8%"></div></div><span class="g-val">8%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">RAM</span><div class="g-bar"><div class="g-fill" style="width:45%"></div></div><span class="g-val">45%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">DISK</span><div class="g-bar"><div class="g-fill" style="width:66%"></div></div><span class="g-val">66%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot">
|
||||
<span class="t-uptime"><i class="fa-solid fa-clock"></i>128j 3h</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- tuile ok -->
|
||||
<div class="tile">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon-wrap"><i class="fa-solid fa-database"></i></div>
|
||||
<div class="t-info">
|
||||
<div class="t-hostname">db-primary-05</div>
|
||||
<div class="t-ip">10.0.0.40</div>
|
||||
</div>
|
||||
<div class="t-led ok"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="gauge-row"><span class="g-label">CPU</span><div class="g-bar"><div class="g-fill" style="width:25%"></div></div><span class="g-val">25%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">RAM</span><div class="g-bar"><div class="g-fill" style="width:61%"></div></div><span class="g-val">61%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">DISK</span><div class="g-bar"><div class="g-fill" style="width:48%"></div></div><span class="g-val">48%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot">
|
||||
<span class="t-uptime"><i class="fa-solid fa-clock"></i>7j 22h</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- tuile warn -->
|
||||
<div class="tile warn-tile">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon-wrap"><i class="fa-solid fa-globe"></i></div>
|
||||
<div class="t-info">
|
||||
<div class="t-hostname">web-front-06</div>
|
||||
<div class="t-ip">10.0.0.51</div>
|
||||
</div>
|
||||
<div class="t-led warn"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="gauge-row"><span class="g-label">CPU</span><div class="g-bar"><div class="g-fill warn" style="width:71%"></div></div><span class="g-val">71%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">RAM</span><div class="g-bar"><div class="g-fill" style="width:55%"></div></div><span class="g-val">55%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">DISK</span><div class="g-bar"><div class="g-fill" style="width:28%"></div></div><span class="g-val">28%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot">
|
||||
<span class="t-uptime"><i class="fa-solid fa-clock"></i>21j 8h</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- tuile offline -->
|
||||
<div class="tile offline-tile">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon-wrap" style="color:var(--ink-4)"><i class="fa-solid fa-server"></i></div>
|
||||
<div class="t-info">
|
||||
<div class="t-hostname">srv-dev-07</div>
|
||||
<div class="t-ip">10.0.0.60</div>
|
||||
</div>
|
||||
<div class="t-led off"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="gauge-row"><span class="g-label">CPU</span><div class="g-bar"><div class="g-fill" style="width:0%"></div></div><span class="g-val" style="color:var(--ink-4)">—</span></div>
|
||||
<div class="gauge-row"><span class="g-label">RAM</span><div class="g-bar"><div class="g-fill" style="width:0%"></div></div><span class="g-val" style="color:var(--ink-4)">—</span></div>
|
||||
<div class="gauge-row"><span class="g-label">DISK</span><div class="g-bar"><div class="g-fill" style="width:0%"></div></div><span class="g-val" style="color:var(--ink-4)">—</span></div>
|
||||
</div>
|
||||
<div class="tile-foot">
|
||||
<span class="t-uptime" style="color:var(--err)"><i class="fa-solid fa-circle-xmark"></i>Hors ligne · vu il y a 2h</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- tuile ok -->
|
||||
<div class="tile">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon-wrap"><i class="fa-solid fa-network-wired"></i></div>
|
||||
<div class="t-info">
|
||||
<div class="t-hostname">gateway-08</div>
|
||||
<div class="t-ip">10.0.0.1</div>
|
||||
</div>
|
||||
<div class="t-led ok"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="gauge-row"><span class="g-label">CPU</span><div class="g-bar"><div class="g-fill" style="width:5%"></div></div><span class="g-val">5%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">RAM</span><div class="g-bar"><div class="g-fill" style="width:22%"></div></div><span class="g-val">22%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">DISK</span><div class="g-bar"><div class="g-fill" style="width:15%"></div></div><span class="g-val">15%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot">
|
||||
<span class="t-uptime"><i class="fa-solid fa-clock"></i>365j 0h</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══ FOOTER ══ -->
|
||||
<div class="footer">
|
||||
<div class="f-mode">LIVE</div>
|
||||
<div class="f-cell">
|
||||
<i class="fa-solid fa-server"></i>
|
||||
<span>SERVEUR</span>
|
||||
</div>
|
||||
<div class="f-cell">
|
||||
<i class="fa-solid fa-microchip"></i>
|
||||
<span>CPU</span>
|
||||
<span class="f-val">18%</span>
|
||||
<span class="f-bar"><span class="f-bar-fill" style="width:18%"></span></span>
|
||||
</div>
|
||||
<div class="f-cell">
|
||||
<i class="fa-solid fa-memory"></i>
|
||||
<span>MEM</span>
|
||||
<span class="f-val warn">71%</span>
|
||||
<span class="f-bar"><span class="f-bar-fill warn" style="width:71%"></span></span>
|
||||
</div>
|
||||
<div class="f-spacer"></div>
|
||||
<div class="f-right">
|
||||
<i class="fa-solid fa-rotate"></i>
|
||||
<span class="f-update">Dernière actualisation : <span style="color:var(--ink-2);font-family:var(--font-mono)">22:14:07</span></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══ POPUP DÉTAIL (visible par défaut pour la maquette) ══ -->
|
||||
<div class="popup-overlay" id="popup-overlay" onclick="if(event.target===this)this.style.display='none'">
|
||||
<div class="popup" onclick="event.stopPropagation()">
|
||||
<div class="popup-header">
|
||||
<div class="ph-icon"><i class="fa-solid fa-server"></i></div>
|
||||
<div class="ph-info">
|
||||
<div class="ph-host">srv-prod-01</div>
|
||||
<div class="ph-ip">10.0.0.11</div>
|
||||
</div>
|
||||
<div class="ph-led"></div>
|
||||
<div class="close-btn" onclick="document.getElementById('popup-overlay').style.display='none'"><i class="fa-solid fa-xmark"></i></div>
|
||||
</div>
|
||||
|
||||
<div class="popup-body">
|
||||
<!-- KPIs -->
|
||||
<div>
|
||||
<div class="popup-section-title">MÉTRIQUES ACTUELLES</div>
|
||||
<div class="popup-kpis">
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-label">CPU</div>
|
||||
<div class="kpi-value ok">42<span class="unit">%</span></div>
|
||||
<div class="kpi-sub">4 cœurs</div>
|
||||
</div>
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-label">MÉMOIRE</div>
|
||||
<div class="kpi-value">3.7<span class="unit">Go</span></div>
|
||||
<div class="kpi-sub">/ 8 Go · 46%</div>
|
||||
</div>
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-label">DISQUE</div>
|
||||
<div class="kpi-value">62<span class="unit">Go</span></div>
|
||||
<div class="kpi-sub">/ 200 Go · 31%</div>
|
||||
</div>
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-label">UPTIME</div>
|
||||
<div class="kpi-value" style="font-size:15px;color:var(--ink-1)">14j 6h</div>
|
||||
<div class="kpi-sub">depuis dernier boot</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Jauges détaillées -->
|
||||
<div>
|
||||
<div class="popup-section-title">UTILISATION DÉTAILLÉE</div>
|
||||
<div class="popup-gauges">
|
||||
<div class="pg-row"><span class="pg-label">CPU total</span><div class="pg-bar"><div class="pg-fill" style="width:42%"></div></div><span class="pg-val">42% / 100%</span></div>
|
||||
<div class="pg-row"><span class="pg-label">RAM utilisée</span><div class="pg-bar"><div class="pg-fill" style="width:46%"></div></div><span class="pg-val">3.7 / 8 Go</span></div>
|
||||
<div class="pg-row"><span class="pg-label">Disque utilisé</span><div class="pg-bar"><div class="pg-fill" style="width:31%"></div></div><span class="pg-val">62 / 200 Go</span></div>
|
||||
<div class="pg-row"><span class="pg-label">Disque libre</span><div class="pg-bar"><div class="pg-fill" style="width:69%"></div></div><span class="pg-val">138 Go libre</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Métadonnées -->
|
||||
<div>
|
||||
<div class="popup-section-title">INFORMATIONS SYSTÈME</div>
|
||||
<div class="popup-meta">
|
||||
<div class="meta-item"><div class="meta-label">HOSTNAME</div><div class="meta-value">srv-prod-01</div></div>
|
||||
<div class="meta-item"><div class="meta-label">ADRESSE IP</div><div class="meta-value" style="font-family:var(--font-mono)">10.0.0.11</div></div>
|
||||
<div class="meta-item"><div class="meta-label">DERNIER CONTACT</div><div class="meta-value" style="font-family:var(--font-mono)">22:14:07</div></div>
|
||||
<div class="meta-item"><div class="meta-label">FRÉQUENCE</div><div class="meta-value" style="font-family:var(--font-mono)">CPU 2s · Disk 60s</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="popup-footer">
|
||||
<span class="pf-uptime"><i class="fa-solid fa-clock" style="margin-right:5px"></i>En ligne depuis 14 jours 6 heures</span>
|
||||
<button class="pf-btn"><i class="fa-solid fa-chart-line"></i> Historique</button>
|
||||
<button class="pf-btn primary"><i class="fa-solid fa-xmark"></i> Fermer</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══ CONFIG PANEL ══ -->
|
||||
<div class="config-overlay" id="config-overlay" style="display:none" onclick="if(event.target===this)this.style.display='none'">
|
||||
<div class="config-panel" onclick="event.stopPropagation()">
|
||||
<div class="config-header">
|
||||
<i class="fa-solid fa-sliders"></i>
|
||||
<span class="ch-title">Configuration interface</span>
|
||||
<div class="close-btn" onclick="document.getElementById('config-overlay').style.display='none'"><i class="fa-solid fa-xmark"></i></div>
|
||||
</div>
|
||||
<div class="config-body">
|
||||
<div class="config-group">
|
||||
<div class="cg-label">TAILLE DES TUILES</div>
|
||||
<div class="cg-row">
|
||||
<span>Largeur min.</span>
|
||||
<input type="range" class="cg-slider" min="160" max="400" value="240">
|
||||
<span class="cg-val">240px</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="config-group">
|
||||
<div class="cg-label">TAILLE DU TEXTE</div>
|
||||
<div class="cg-row">
|
||||
<span>Interface</span>
|
||||
<input type="range" class="cg-slider" min="10" max="18" value="13">
|
||||
<span class="cg-val">13px</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="config-group">
|
||||
<div class="cg-label">SEUILS D'ALERTE (défaut)</div>
|
||||
<div class="cg-row">
|
||||
<span>Warning CPU/RAM</span>
|
||||
<input type="range" class="cg-slider" min="50" max="95" value="70">
|
||||
<span class="cg-val">70%</span>
|
||||
</div>
|
||||
<div class="cg-row">
|
||||
<span>Erreur CPU/RAM</span>
|
||||
<input type="range" class="cg-slider" min="60" max="100" value="85">
|
||||
<span class="cg-val">85%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="config-group">
|
||||
<div class="cg-label">AGENTS HORS LIGNE</div>
|
||||
<div class="cg-row">
|
||||
<span>Affichage</span>
|
||||
<select class="cg-select">
|
||||
<option>Visible (grisé)</option>
|
||||
<option>Masqué</option>
|
||||
<option>Dernière position</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="config-group">
|
||||
<div class="cg-label">RÉTENTION HISTORIQUE</div>
|
||||
<div class="cg-row">
|
||||
<span>Durée</span>
|
||||
<select class="cg-select">
|
||||
<option>7 jours</option>
|
||||
<option selected>30 jours</option>
|
||||
<option>90 jours</option>
|
||||
<option>1 an</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="config-footer">
|
||||
<button class="pf-btn" onclick="document.getElementById('config-overlay').style.display='none'">Annuler</button>
|
||||
<button class="pf-btn primary"><i class="fa-solid fa-floppy-disk"></i> Sauvegarder</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function toggleTheme() {
|
||||
const html = document.documentElement;
|
||||
const icon = document.getElementById('theme-icon');
|
||||
if (html.dataset.theme === 'dark') {
|
||||
html.dataset.theme = 'light';
|
||||
icon.className = 'fa-solid fa-sun';
|
||||
} else {
|
||||
html.dataset.theme = 'dark';
|
||||
icon.className = 'fa-solid fa-moon';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,602 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Nanometrics — Layout v3</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Share+Tech+Mono:wght@400&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<script src="/helper.js"></script>
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
:root[data-theme="dark"] {
|
||||
--accent:#fe8019; --accent-soft:#d65d0e; --accent-glow:rgba(254,128,25,0.3);
|
||||
--bg-0:#1d1813; --bg-1:#2a231d; --bg-2:#32291f; --bg-3:#3c332a; --bg-4:#4a3f33; --bg-5:#57493c;
|
||||
--ink-1:#f2e5c7; --ink-2:#d5c4a1; --ink-3:#a89984; --ink-4:#7c6f64;
|
||||
--ok:#4dbb26; --warn:#fabd2f; --err:#fb4934; --info:#83a598; --blue:#3db0d1;
|
||||
--border-1:rgba(255,255,255,0.06); --border-2:rgba(255,255,255,0.12); --border-3:rgba(255,255,255,0.22);
|
||||
--tile-3d:0 1px 0 rgba(255,255,255,0.08) inset,0 -1px 0 rgba(0,0,0,0.3) inset,0 4px 16px rgba(0,0,0,0.5);
|
||||
--font-ui:'Inter',system-ui,sans-serif; --font-mono:'JetBrains Mono',monospace; --font-terminal:'Share Tech Mono',monospace;
|
||||
}
|
||||
:root[data-theme="light"] {
|
||||
--accent:#af3a03; --accent-soft:#d65d0e; --accent-glow:rgba(175,58,3,0.2);
|
||||
--bg-0:#d5c4a1; --bg-1:#ebdbb2; --bg-2:#d5c4a1; --bg-3:#bdae93; --bg-4:#a89984; --bg-5:#928374;
|
||||
--ink-1:#3c3836; --ink-2:#504945; --ink-3:#665c54; --ink-4:#7c6f64;
|
||||
--ok:#3c911c; --warn:#b57614; --err:#9d0006; --info:#427b58; --blue:#2d82a3;
|
||||
--border-1:rgba(0,0,0,0.08); --border-2:rgba(0,0,0,0.15); --border-3:rgba(0,0,0,0.25);
|
||||
--tile-3d:0 1px 0 rgba(255,255,255,0.5) inset,0 -1px 0 rgba(0,0,0,0.1) inset,0 4px 16px rgba(0,0,0,0.12);
|
||||
--font-ui:'Inter',system-ui,sans-serif; --font-mono:'JetBrains Mono',monospace; --font-terminal:'Share Tech Mono',monospace;
|
||||
}
|
||||
|
||||
body { background:var(--bg-1); color:var(--ink-1); font-family:var(--font-ui); font-size:13px; height:100vh; display:flex; flex-direction:column; overflow:hidden; transition:background .2s, color .2s; }
|
||||
|
||||
/* ══ HEADER ══ */
|
||||
.header { background:var(--bg-2); border-bottom:1px solid var(--border-2); padding:0 20px; height:48px; display:flex; align-items:center; gap:14px; flex-shrink:0; }
|
||||
.header-logo { display:flex; align-items:center; gap:8px; }
|
||||
.logo-led { width:9px; height:9px; border-radius:50%; background:var(--accent); box-shadow:0 0 8px var(--accent-glow); animation:blink 2s infinite; }
|
||||
@keyframes blink { 0%,100%{opacity:1} 50%{opacity:.4} }
|
||||
.logo-name { font-weight:700; font-size:14px; letter-spacing:.05em; color:var(--ink-1); font-family:var(--font-terminal); }
|
||||
.logo-ver { font-size:10px; color:var(--ink-4); font-family:var(--font-terminal); }
|
||||
.h-sep { width:1px; height:24px; background:var(--border-2); }
|
||||
.h-spacer { flex:1; }
|
||||
.h-stats { display:flex; gap:14px; align-items:center; }
|
||||
.h-stat { display:flex; align-items:center; gap:5px; font-size:12px; }
|
||||
.h-stat .lbl { font-size:9px; color:var(--ink-4); font-family:var(--font-terminal); letter-spacing:.06em; }
|
||||
.h-stat .val { font-family:var(--font-mono); font-weight:700; }
|
||||
.val-ok{color:var(--ok)} .val-warn{color:var(--warn)} .val-err{color:var(--err)} .val-n{color:var(--ink-2)}
|
||||
|
||||
/* icon buttons header */
|
||||
.hbtn {
|
||||
width:34px; height:34px; border-radius:8px; border:1px solid var(--border-2);
|
||||
background:var(--bg-3); color:var(--ink-2); font-size:14px;
|
||||
display:flex; align-items:center; justify-content:center; cursor:pointer;
|
||||
position:relative; transition:background .12s, color .12s;
|
||||
}
|
||||
.hbtn:hover { background:var(--bg-4); color:var(--accent); }
|
||||
.hbtn:active { box-shadow:inset 0 2px 4px rgba(0,0,0,.35); }
|
||||
.hbtn .tip {
|
||||
position:absolute; top:calc(100% + 6px); right:0; z-index:50;
|
||||
background:var(--bg-0); color:var(--ink-1); font-size:11px;
|
||||
padding:4px 8px; border-radius:5px; border:1px solid var(--border-2);
|
||||
white-space:nowrap; opacity:0; pointer-events:none; transition:opacity .12s;
|
||||
font-family:var(--font-ui);
|
||||
}
|
||||
.hbtn:hover .tip { opacity:1; }
|
||||
|
||||
/* ══ GRID ══ */
|
||||
.main { flex:1; padding:14px 16px; overflow-y:auto; }
|
||||
.agents-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(220px,1fr)); gap:10px; }
|
||||
|
||||
/* ══ TILE ══ */
|
||||
.tile {
|
||||
background:var(--bg-3); border-radius:10px; padding:12px 14px;
|
||||
border:1px solid var(--border-1); box-shadow:var(--tile-3d);
|
||||
cursor:pointer; transition:border-color .15s, box-shadow .15s;
|
||||
display:flex; flex-direction:column; gap:9px;
|
||||
}
|
||||
.tile:hover { border-color:var(--border-3); }
|
||||
.tile.t-warn { border-color:rgba(250,189,47,.28); }
|
||||
.tile.t-err { border-color:rgba(251,73,52,.32); box-shadow:var(--tile-3d),0 0 16px rgba(251,73,52,.1); }
|
||||
.tile.t-off { opacity:.5; cursor:default; }
|
||||
|
||||
.tile-head { display:flex; align-items:center; gap:8px; }
|
||||
.t-icon { width:28px; height:28px; border-radius:7px; background:var(--bg-4); display:flex; align-items:center; justify-content:center; color:var(--accent); font-size:13px; flex-shrink:0; }
|
||||
.t-names { flex:1; min-width:0; }
|
||||
.t-host { font-weight:600; font-size:13px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
||||
.t-ip { font-family:var(--font-mono); font-size:10px; color:var(--ink-4); }
|
||||
.t-status { width:8px; height:8px; border-radius:50%; flex-shrink:0; }
|
||||
.s-ok { background:var(--ok); box-shadow:0 0 6px var(--ok); }
|
||||
.s-warn { background:var(--warn); box-shadow:0 0 6px var(--warn); animation:blink 1.5s infinite; }
|
||||
.s-err { background:var(--err); box-shadow:0 0 8px var(--err); animation:blink 1s infinite; }
|
||||
.s-off { background:var(--ink-4); }
|
||||
|
||||
/* gauges avec icône + tooltip */
|
||||
.tile-gauges { display:flex; flex-direction:column; gap:5px; }
|
||||
.g-row { display:flex; align-items:center; gap:7px; }
|
||||
|
||||
.g-icon-wrap {
|
||||
width:18px; height:18px; display:flex; align-items:center; justify-content:center;
|
||||
font-size:11px; color:var(--ink-3); flex-shrink:0;
|
||||
position:relative; cursor:help;
|
||||
}
|
||||
.g-icon-wrap .g-tip {
|
||||
position:absolute; bottom:calc(100% + 5px); left:50%; transform:translateX(-50%);
|
||||
background:var(--bg-0); color:var(--ink-1); font-size:10px;
|
||||
padding:3px 7px; border-radius:4px; border:1px solid var(--border-2);
|
||||
white-space:nowrap; opacity:0; pointer-events:none; transition:opacity .12s;
|
||||
z-index:10; font-family:var(--font-ui);
|
||||
}
|
||||
.g-icon-wrap:hover .g-tip { opacity:1; }
|
||||
|
||||
.g-bar { flex:1; height:5px; border-radius:3px; background:var(--bg-1); overflow:hidden; }
|
||||
.g-fill { height:100%; border-radius:3px; background:var(--ok); transition:width .4s; }
|
||||
.g-fill.w { background:var(--warn); }
|
||||
.g-fill.e { background:var(--err); }
|
||||
.g-val { font-family:var(--font-mono); font-size:11px; color:var(--ink-2); width:34px; text-align:right; flex-shrink:0; }
|
||||
|
||||
.tile-foot { display:flex; align-items:center; gap:5px; font-family:var(--font-terminal); font-size:10px; color:var(--ink-4); }
|
||||
.tile-foot i { font-size:9px; }
|
||||
.tile-foot.offline { color:var(--err); }
|
||||
|
||||
/* ══ FOOTER ══ */
|
||||
.footer { background:var(--bg-0); border-top:1px solid var(--border-2); height:26px; display:flex; align-items:center; font-family:var(--font-terminal); font-size:11px; color:var(--ink-4); flex-shrink:0; }
|
||||
.f-mode { background:var(--accent); color:var(--bg-0); padding:0 12px; height:100%; display:flex; align-items:center; font-weight:700; font-size:11px; letter-spacing:.04em; }
|
||||
.f-cell { padding:0 12px; border-right:1px solid var(--border-1); display:flex; align-items:center; gap:5px; height:100%; }
|
||||
.f-icon { font-size:10px; color:var(--ink-4); }
|
||||
.f-val { font-family:var(--font-mono); color:var(--ink-2); }
|
||||
.f-val.w { color:var(--warn); }
|
||||
.f-minibar { width:36px; height:4px; border-radius:2px; background:var(--bg-3); overflow:hidden; }
|
||||
.f-minifill { height:100%; border-radius:2px; background:var(--ok); }
|
||||
.f-minifill.w { background:var(--warn); }
|
||||
.f-spacer { flex:1; }
|
||||
.f-right { padding:0 12px; display:flex; align-items:center; gap:6px; color:var(--ink-3); }
|
||||
.f-right .f-time { font-family:var(--font-mono); color:var(--ink-2); }
|
||||
|
||||
/* ══ POPUP DÉTAIL ══ */
|
||||
.overlay { position:fixed; inset:0; background:rgba(0,0,0,.65); z-index:100; display:flex; align-items:center; justify-content:center; backdrop-filter:blur(2px); }
|
||||
.popup { background:var(--bg-2); border:1px solid var(--border-3); border-radius:12px; width:500px; max-width:96vw; box-shadow:0 24px 64px rgba(0,0,0,.7); display:flex; flex-direction:column; overflow:hidden; }
|
||||
|
||||
.pop-head { background:var(--bg-3); padding:14px 18px; border-bottom:1px solid var(--border-2); display:flex; align-items:center; gap:10px; }
|
||||
.pop-head-icon { width:32px; height:32px; border-radius:8px; background:var(--bg-4); display:flex; align-items:center; justify-content:center; color:var(--accent); font-size:15px; }
|
||||
.pop-head-info { flex:1; }
|
||||
.pop-host { font-weight:700; font-size:14px; }
|
||||
.pop-ip { font-family:var(--font-mono); font-size:11px; color:var(--ink-4); }
|
||||
.pop-led { width:10px; height:10px; border-radius:50%; background:var(--ok); box-shadow:0 0 8px var(--ok); }
|
||||
.pop-close { width:28px; height:28px; border-radius:6px; background:var(--bg-5); color:var(--ink-3); display:flex; align-items:center; justify-content:center; cursor:pointer; font-size:12px; }
|
||||
.pop-close:hover { background:var(--err); color:#fff; }
|
||||
|
||||
.pop-body { padding:16px 18px; display:flex; flex-direction:column; gap:14px; }
|
||||
|
||||
.sec-title { font-size:9px; color:var(--ink-4); font-family:var(--font-terminal); letter-spacing:.08em; margin-bottom:7px; }
|
||||
|
||||
/* KPI cards */
|
||||
.kpi-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:7px; }
|
||||
.kpi { background:var(--bg-3); border-radius:8px; padding:10px 12px; border:1px solid var(--border-1); box-shadow:var(--tile-3d); }
|
||||
.kpi-lbl { font-size:9px; color:var(--ink-4); font-family:var(--font-terminal); letter-spacing:.06em; }
|
||||
.kpi-val { font-family:var(--font-mono); font-size:20px; font-weight:700; line-height:1.1; margin-top:2px; }
|
||||
.kpi-val .u { font-size:10px; color:var(--ink-3); font-weight:400; }
|
||||
.kpi-sub { font-size:10px; color:var(--ink-4); font-family:var(--font-mono); margin-top:2px; }
|
||||
|
||||
/* gauges détail popup */
|
||||
.det-gauges { display:flex; flex-direction:column; gap:7px; }
|
||||
.dg-row { display:flex; align-items:center; gap:10px; }
|
||||
.dg-icon { width:22px; text-align:center; font-size:13px; position:relative; cursor:help; }
|
||||
.dg-icon .dg-tip {
|
||||
position:absolute; bottom:calc(100%+5px); left:50%; transform:translateX(-50%);
|
||||
background:var(--bg-0); color:var(--ink-1); font-size:10px;
|
||||
padding:3px 8px; border-radius:4px; border:1px solid var(--border-2);
|
||||
white-space:nowrap; opacity:0; pointer-events:none; transition:opacity .1s; z-index:10;
|
||||
font-family:var(--font-ui);
|
||||
}
|
||||
.dg-icon:hover .dg-tip { opacity:1; }
|
||||
.dg-bar { flex:1; height:7px; border-radius:4px; background:var(--bg-1); overflow:hidden; }
|
||||
.dg-fill { height:100%; border-radius:4px; background:var(--ok); }
|
||||
.dg-fill.w { background:var(--warn); }
|
||||
.dg-fill.e { background:var(--err); }
|
||||
.dg-val { font-family:var(--font-mono); font-size:12px; color:var(--ink-2); width:90px; text-align:right; }
|
||||
|
||||
/* meta */
|
||||
.meta-grid { display:grid; grid-template-columns:1fr 1fr; gap:6px; }
|
||||
.meta { background:var(--bg-3); border-radius:6px; padding:8px 10px; border:1px solid var(--border-1); }
|
||||
.meta-lbl { font-size:9px; color:var(--ink-4); font-family:var(--font-terminal); letter-spacing:.06em; }
|
||||
.meta-val { font-family:var(--font-mono); font-size:12px; color:var(--ink-2); margin-top:2px; }
|
||||
|
||||
.pop-foot { padding:12px 18px; border-top:1px solid var(--border-2); background:var(--bg-3); display:flex; align-items:center; gap:8px; }
|
||||
.pop-uptime { font-family:var(--font-terminal); font-size:11px; color:var(--ink-4); flex:1; }
|
||||
.btn { padding:6px 14px; border-radius:8px; border:1px solid var(--border-2); background:var(--bg-4); color:var(--ink-2); font-size:12px; font-family:var(--font-ui); cursor:pointer; display:flex; align-items:center; gap:6px; transition:background .1s; }
|
||||
.btn:hover { background:var(--bg-5); }
|
||||
.btn.primary { background:var(--accent); color:var(--bg-0); border-color:var(--accent-soft); font-weight:600; }
|
||||
.btn.primary:hover { background:var(--accent-soft); }
|
||||
|
||||
/* ══ CONFIG PANEL ══ */
|
||||
.cfg { background:var(--bg-2); border:1px solid var(--border-3); border-radius:12px; width:380px; box-shadow:0 24px 64px rgba(0,0,0,.7); overflow:hidden; }
|
||||
.cfg-head { background:var(--bg-3); padding:14px 18px; border-bottom:1px solid var(--border-2); display:flex; align-items:center; gap:10px; }
|
||||
.cfg-head i { color:var(--accent); font-size:15px; }
|
||||
.cfg-title { flex:1; font-weight:600; }
|
||||
.cfg-body { padding:18px; display:flex; flex-direction:column; gap:13px; }
|
||||
.cfg-group { display:flex; flex-direction:column; gap:6px; }
|
||||
.cfg-lbl { font-size:9px; color:var(--ink-4); font-family:var(--font-terminal); letter-spacing:.08em; }
|
||||
.cfg-row { display:flex; align-items:center; gap:10px; }
|
||||
.cfg-row > span { font-size:12px; color:var(--ink-3); min-width:90px; }
|
||||
.cfg-slider { flex:1; accent-color:var(--accent); }
|
||||
.cfg-v { font-family:var(--font-mono); font-size:12px; color:var(--ink-2); width:44px; text-align:right; }
|
||||
.cfg-select { flex:1; background:var(--bg-3); border:1px solid var(--border-2); border-radius:6px; color:var(--ink-1); padding:6px 10px; font-size:12px; }
|
||||
.cfg-foot { padding:12px 18px; border-top:1px solid var(--border-2); background:var(--bg-3); display:flex; gap:8px; justify-content:flex-end; }
|
||||
|
||||
::-webkit-scrollbar{width:6px} ::-webkit-scrollbar-track{background:var(--bg-1)} ::-webkit-scrollbar-thumb{background:var(--bg-4);border-radius:3px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- HEADER -->
|
||||
<div class="header">
|
||||
<div class="header-logo">
|
||||
<div class="logo-led"></div>
|
||||
<span class="logo-name">NANOMETRICS</span>
|
||||
<span class="logo-ver">v1.0</span>
|
||||
</div>
|
||||
<div class="h-sep"></div>
|
||||
<div class="h-stats">
|
||||
<div class="h-stat"><span class="lbl">AGENTS</span><span class="val val-n">8</span></div>
|
||||
<div class="h-stat"><span class="lbl">OK</span><span class="val val-ok">5</span></div>
|
||||
<div class="h-stat"><span class="lbl">WARN</span><span class="val val-warn">2</span></div>
|
||||
<div class="h-stat"><span class="lbl">ERR</span><span class="val val-err">1</span></div>
|
||||
</div>
|
||||
<div class="h-spacer"></div>
|
||||
<div class="hbtn" onclick="toggleTheme()">
|
||||
<i class="fa-solid fa-moon" id="theme-icon"></i>
|
||||
<span class="tip">Thème clair / sombre</span>
|
||||
</div>
|
||||
<div class="hbtn" onclick="document.getElementById('cfg-overlay').style.display='flex'">
|
||||
<i class="fa-solid fa-sliders"></i>
|
||||
<span class="tip">Configuration interface</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- GRID -->
|
||||
<div class="main">
|
||||
<div class="agents-grid">
|
||||
|
||||
<!-- srv-prod-01 · ok -->
|
||||
<div class="tile" onclick="showPopup()">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon"><i class="fa-solid fa-server"></i></div>
|
||||
<div class="t-names"><div class="t-host">srv-prod-01</div><div class="t-ip">10.0.0.11</div></div>
|
||||
<div class="t-status s-ok"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row">
|
||||
<div class="g-icon-wrap"><i class="fa-solid fa-microchip"></i><span class="g-tip">Processeur (CPU)</span></div>
|
||||
<div class="g-bar"><div class="g-fill" style="width:42%"></div></div>
|
||||
<span class="g-val">42%</span>
|
||||
</div>
|
||||
<div class="g-row">
|
||||
<div class="g-icon-wrap"><i class="fa-solid fa-memory"></i><span class="g-tip">Mémoire RAM</span></div>
|
||||
<div class="g-bar"><div class="g-fill" style="width:58%"></div></div>
|
||||
<span class="g-val">58%</span>
|
||||
</div>
|
||||
<div class="g-row">
|
||||
<div class="g-icon-wrap"><i class="fa-solid fa-hard-drive"></i><span class="g-tip">Espace disque</span></div>
|
||||
<div class="g-bar"><div class="g-fill" style="width:31%"></div></div>
|
||||
<span class="g-val">31%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>14j 6h</div>
|
||||
</div>
|
||||
|
||||
<!-- srv-backup-02 · warn -->
|
||||
<div class="tile t-warn">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon"><i class="fa-solid fa-server"></i></div>
|
||||
<div class="t-names"><div class="t-host">srv-backup-02</div><div class="t-ip">10.0.0.12</div></div>
|
||||
<div class="t-status s-warn"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row">
|
||||
<div class="g-icon-wrap" style="color:var(--warn)"><i class="fa-solid fa-microchip"></i><span class="g-tip">Processeur (CPU) — élevé</span></div>
|
||||
<div class="g-bar"><div class="g-fill w" style="width:78%"></div></div>
|
||||
<span class="g-val" style="color:var(--warn)">78%</span>
|
||||
</div>
|
||||
<div class="g-row">
|
||||
<div class="g-icon-wrap" style="color:var(--warn)"><i class="fa-solid fa-memory"></i><span class="g-tip">Mémoire RAM — élevée</span></div>
|
||||
<div class="g-bar"><div class="g-fill w" style="width:72%"></div></div>
|
||||
<span class="g-val" style="color:var(--warn)">72%</span>
|
||||
</div>
|
||||
<div class="g-row">
|
||||
<div class="g-icon-wrap"><i class="fa-solid fa-hard-drive"></i><span class="g-tip">Espace disque</span></div>
|
||||
<div class="g-bar"><div class="g-fill" style="width:20%"></div></div>
|
||||
<span class="g-val">20%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>3j 14h</div>
|
||||
</div>
|
||||
|
||||
<!-- rpi-sensor-03 · err -->
|
||||
<div class="tile t-err">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon" style="color:var(--err)"><i class="fa-solid fa-microchip"></i></div>
|
||||
<div class="t-names"><div class="t-host">rpi-sensor-03</div><div class="t-ip">10.0.0.23</div></div>
|
||||
<div class="t-status s-err"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row">
|
||||
<div class="g-icon-wrap"><i class="fa-solid fa-microchip"></i><span class="g-tip">Processeur (CPU)</span></div>
|
||||
<div class="g-bar"><div class="g-fill" style="width:12%"></div></div>
|
||||
<span class="g-val">12%</span>
|
||||
</div>
|
||||
<div class="g-row">
|
||||
<div class="g-icon-wrap"><i class="fa-solid fa-memory"></i><span class="g-tip">Mémoire RAM</span></div>
|
||||
<div class="g-bar"><div class="g-fill" style="width:38%"></div></div>
|
||||
<span class="g-val">38%</span>
|
||||
</div>
|
||||
<div class="g-row">
|
||||
<div class="g-icon-wrap" style="color:var(--err)"><i class="fa-solid fa-hard-drive"></i><span class="g-tip">Disque critique !</span></div>
|
||||
<div class="g-bar"><div class="g-fill e" style="width:94%"></div></div>
|
||||
<span class="g-val" style="color:var(--err)">94%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>62j 1h</div>
|
||||
</div>
|
||||
|
||||
<!-- nas-04 · ok -->
|
||||
<div class="tile">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon"><i class="fa-solid fa-hard-drive"></i></div>
|
||||
<div class="t-names"><div class="t-host">nas-storage-04</div><div class="t-ip">10.0.0.30</div></div>
|
||||
<div class="t-status s-ok"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row">
|
||||
<div class="g-icon-wrap"><i class="fa-solid fa-microchip"></i><span class="g-tip">Processeur (CPU)</span></div>
|
||||
<div class="g-bar"><div class="g-fill" style="width:8%"></div></div>
|
||||
<span class="g-val">8%</span>
|
||||
</div>
|
||||
<div class="g-row">
|
||||
<div class="g-icon-wrap"><i class="fa-solid fa-memory"></i><span class="g-tip">Mémoire RAM</span></div>
|
||||
<div class="g-bar"><div class="g-fill" style="width:45%"></div></div>
|
||||
<span class="g-val">45%</span>
|
||||
</div>
|
||||
<div class="g-row">
|
||||
<div class="g-icon-wrap"><i class="fa-solid fa-hard-drive"></i><span class="g-tip">Espace disque</span></div>
|
||||
<div class="g-bar"><div class="g-fill" style="width:66%"></div></div>
|
||||
<span class="g-val">66%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>128j 3h</div>
|
||||
</div>
|
||||
|
||||
<!-- db-05 · ok -->
|
||||
<div class="tile">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon"><i class="fa-solid fa-database"></i></div>
|
||||
<div class="t-names"><div class="t-host">db-primary-05</div><div class="t-ip">10.0.0.40</div></div>
|
||||
<div class="t-status s-ok"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row">
|
||||
<div class="g-icon-wrap"><i class="fa-solid fa-microchip"></i><span class="g-tip">Processeur (CPU)</span></div>
|
||||
<div class="g-bar"><div class="g-fill" style="width:25%"></div></div>
|
||||
<span class="g-val">25%</span>
|
||||
</div>
|
||||
<div class="g-row">
|
||||
<div class="g-icon-wrap"><i class="fa-solid fa-memory"></i><span class="g-tip">Mémoire RAM</span></div>
|
||||
<div class="g-bar"><div class="g-fill" style="width:61%"></div></div>
|
||||
<span class="g-val">61%</span>
|
||||
</div>
|
||||
<div class="g-row">
|
||||
<div class="g-icon-wrap"><i class="fa-solid fa-hard-drive"></i><span class="g-tip">Espace disque</span></div>
|
||||
<div class="g-bar"><div class="g-fill" style="width:48%"></div></div>
|
||||
<span class="g-val">48%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>7j 22h</div>
|
||||
</div>
|
||||
|
||||
<!-- web-06 · warn -->
|
||||
<div class="tile t-warn">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon"><i class="fa-solid fa-globe"></i></div>
|
||||
<div class="t-names"><div class="t-host">web-front-06</div><div class="t-ip">10.0.0.51</div></div>
|
||||
<div class="t-status s-warn"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row">
|
||||
<div class="g-icon-wrap" style="color:var(--warn)"><i class="fa-solid fa-microchip"></i><span class="g-tip">Processeur (CPU) — élevé</span></div>
|
||||
<div class="g-bar"><div class="g-fill w" style="width:71%"></div></div>
|
||||
<span class="g-val" style="color:var(--warn)">71%</span>
|
||||
</div>
|
||||
<div class="g-row">
|
||||
<div class="g-icon-wrap"><i class="fa-solid fa-memory"></i><span class="g-tip">Mémoire RAM</span></div>
|
||||
<div class="g-bar"><div class="g-fill" style="width:55%"></div></div>
|
||||
<span class="g-val">55%</span>
|
||||
</div>
|
||||
<div class="g-row">
|
||||
<div class="g-icon-wrap"><i class="fa-solid fa-hard-drive"></i><span class="g-tip">Espace disque</span></div>
|
||||
<div class="g-bar"><div class="g-fill" style="width:28%"></div></div>
|
||||
<span class="g-val">28%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>21j 8h</div>
|
||||
</div>
|
||||
|
||||
<!-- srv-dev-07 · offline -->
|
||||
<div class="tile t-off">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon" style="color:var(--ink-4)"><i class="fa-solid fa-server"></i></div>
|
||||
<div class="t-names"><div class="t-host">srv-dev-07</div><div class="t-ip">10.0.0.60</div></div>
|
||||
<div class="t-status s-off"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row">
|
||||
<div class="g-icon-wrap"><i class="fa-solid fa-microchip"></i><span class="g-tip">Processeur (CPU)</span></div>
|
||||
<div class="g-bar"></div><span class="g-val" style="color:var(--ink-4)">—</span>
|
||||
</div>
|
||||
<div class="g-row">
|
||||
<div class="g-icon-wrap"><i class="fa-solid fa-memory"></i><span class="g-tip">Mémoire RAM</span></div>
|
||||
<div class="g-bar"></div><span class="g-val" style="color:var(--ink-4)">—</span>
|
||||
</div>
|
||||
<div class="g-row">
|
||||
<div class="g-icon-wrap"><i class="fa-solid fa-hard-drive"></i><span class="g-tip">Espace disque</span></div>
|
||||
<div class="g-bar"></div><span class="g-val" style="color:var(--ink-4)">—</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tile-foot offline"><i class="fa-solid fa-circle-xmark"></i>Hors ligne · vu il y a 2h</div>
|
||||
</div>
|
||||
|
||||
<!-- gateway-08 · ok -->
|
||||
<div class="tile">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon"><i class="fa-solid fa-network-wired"></i></div>
|
||||
<div class="t-names"><div class="t-host">gateway-08</div><div class="t-ip">10.0.0.1</div></div>
|
||||
<div class="t-status s-ok"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row">
|
||||
<div class="g-icon-wrap"><i class="fa-solid fa-microchip"></i><span class="g-tip">Processeur (CPU)</span></div>
|
||||
<div class="g-bar"><div class="g-fill" style="width:5%"></div></div>
|
||||
<span class="g-val">5%</span>
|
||||
</div>
|
||||
<div class="g-row">
|
||||
<div class="g-icon-wrap"><i class="fa-solid fa-memory"></i><span class="g-tip">Mémoire RAM</span></div>
|
||||
<div class="g-bar"><div class="g-fill" style="width:22%"></div></div>
|
||||
<span class="g-val">22%</span>
|
||||
</div>
|
||||
<div class="g-row">
|
||||
<div class="g-icon-wrap"><i class="fa-solid fa-hard-drive"></i><span class="g-tip">Espace disque</span></div>
|
||||
<div class="g-bar"><div class="g-fill" style="width:15%"></div></div>
|
||||
<span class="g-val">15%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>365j 0h</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<div class="footer">
|
||||
<div class="f-mode">LIVE</div>
|
||||
<div class="f-cell"><i class="fa-solid fa-server f-icon"></i><span>SERVEUR</span></div>
|
||||
<div class="f-cell">
|
||||
<i class="fa-solid fa-microchip f-icon"></i>
|
||||
<span class="f-val">18%</span>
|
||||
<div class="f-minibar"><div class="f-minifill" style="width:18%"></div></div>
|
||||
</div>
|
||||
<div class="f-cell">
|
||||
<i class="fa-solid fa-memory f-icon"></i>
|
||||
<span class="f-val w">71%</span>
|
||||
<div class="f-minibar"><div class="f-minifill w" style="width:71%"></div></div>
|
||||
</div>
|
||||
<div class="f-spacer"></div>
|
||||
<div class="f-right">
|
||||
<i class="fa-solid fa-rotate"></i>
|
||||
<span>Actualisation : <span class="f-time">22:14:07</span></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- POPUP DÉTAIL -->
|
||||
<div class="overlay" id="popup-overlay" style="display:none" onclick="if(event.target===this)this.style.display='none'">
|
||||
<div class="popup" onclick="event.stopPropagation()">
|
||||
<div class="pop-head">
|
||||
<div class="pop-head-icon"><i class="fa-solid fa-server"></i></div>
|
||||
<div class="pop-head-info">
|
||||
<div class="pop-host">srv-prod-01</div>
|
||||
<div class="pop-ip">10.0.0.11</div>
|
||||
</div>
|
||||
<div class="pop-led"></div>
|
||||
<div class="pop-close" onclick="document.getElementById('popup-overlay').style.display='none'"><i class="fa-solid fa-xmark"></i></div>
|
||||
</div>
|
||||
<div class="pop-body">
|
||||
<div>
|
||||
<div class="sec-title">MÉTRIQUES ACTUELLES</div>
|
||||
<div class="kpi-grid">
|
||||
<div class="kpi"><div class="kpi-lbl">CPU</div><div class="kpi-val" style="color:var(--ok)">42<span class="u">%</span></div><div class="kpi-sub">4 cœurs</div></div>
|
||||
<div class="kpi"><div class="kpi-lbl">MÉMOIRE</div><div class="kpi-val">3.7<span class="u">Go</span></div><div class="kpi-sub">/ 8 Go · 46%</div></div>
|
||||
<div class="kpi"><div class="kpi-lbl">DISQUE</div><div class="kpi-val">62<span class="u">Go</span></div><div class="kpi-sub">/ 200 Go · 31%</div></div>
|
||||
<div class="kpi"><div class="kpi-lbl">UPTIME</div><div class="kpi-val" style="font-size:15px;color:var(--ink-1)">14j 6h</div><div class="kpi-sub">depuis boot</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sec-title">UTILISATION DÉTAILLÉE</div>
|
||||
<div class="det-gauges">
|
||||
<div class="dg-row">
|
||||
<div class="dg-icon" style="color:var(--ok)"><i class="fa-solid fa-microchip"></i><span class="dg-tip">Processeur (CPU)</span></div>
|
||||
<div class="dg-bar"><div class="dg-fill" style="width:42%"></div></div>
|
||||
<span class="dg-val">42% / 100%</span>
|
||||
</div>
|
||||
<div class="dg-row">
|
||||
<div class="dg-icon"><i class="fa-solid fa-memory"></i><span class="dg-tip">Mémoire RAM utilisée</span></div>
|
||||
<div class="dg-bar"><div class="dg-fill" style="width:46%"></div></div>
|
||||
<span class="dg-val">3.7 / 8 Go</span>
|
||||
</div>
|
||||
<div class="dg-row">
|
||||
<div class="dg-icon"><i class="fa-solid fa-hard-drive"></i><span class="dg-tip">Disque utilisé</span></div>
|
||||
<div class="dg-bar"><div class="dg-fill" style="width:31%"></div></div>
|
||||
<span class="dg-val">62 / 200 Go</span>
|
||||
</div>
|
||||
<div class="dg-row">
|
||||
<div class="dg-icon" style="color:var(--blue)"><i class="fa-solid fa-floppy-disk"></i><span class="dg-tip">Espace libre</span></div>
|
||||
<div class="dg-bar"><div class="dg-fill" style="width:69%;background:var(--blue)"></div></div>
|
||||
<span class="dg-val">138 Go libre</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sec-title">INFORMATIONS</div>
|
||||
<div class="meta-grid">
|
||||
<div class="meta"><div class="meta-lbl">HOSTNAME</div><div class="meta-val">srv-prod-01</div></div>
|
||||
<div class="meta"><div class="meta-lbl">ADRESSE IP</div><div class="meta-val">10.0.0.11</div></div>
|
||||
<div class="meta"><div class="meta-lbl">DERNIER CONTACT</div><div class="meta-val">22:14:07</div></div>
|
||||
<div class="meta"><div class="meta-lbl">FRÉQUENCE</div><div class="meta-val">CPU 2s · Disk 60s</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pop-foot">
|
||||
<span class="pop-uptime"><i class="fa-solid fa-clock" style="margin-right:4px"></i>En ligne depuis 14 jours 6 heures</span>
|
||||
<button class="btn"><i class="fa-solid fa-chart-line"></i> Historique</button>
|
||||
<button class="btn primary" onclick="document.getElementById('popup-overlay').style.display='none'"><i class="fa-solid fa-xmark"></i> Fermer</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CONFIG PANEL -->
|
||||
<div class="overlay" id="cfg-overlay" style="display:none" onclick="if(event.target===this)this.style.display='none'">
|
||||
<div class="cfg" onclick="event.stopPropagation()">
|
||||
<div class="cfg-head">
|
||||
<i class="fa-solid fa-sliders"></i>
|
||||
<span class="cfg-title">Configuration interface</span>
|
||||
<div class="pop-close" onclick="document.getElementById('cfg-overlay').style.display='none'"><i class="fa-solid fa-xmark"></i></div>
|
||||
</div>
|
||||
<div class="cfg-body">
|
||||
<div class="cfg-group">
|
||||
<div class="cfg-lbl">TAILLE DES TUILES</div>
|
||||
<div class="cfg-row"><span>Largeur min.</span><input type="range" class="cfg-slider" min="160" max="400" value="220"><span class="cfg-v">220px</span></div>
|
||||
</div>
|
||||
<div class="cfg-group">
|
||||
<div class="cfg-lbl">TAILLE DU TEXTE</div>
|
||||
<div class="cfg-row"><span>Interface</span><input type="range" class="cfg-slider" min="10" max="18" value="13"><span class="cfg-v">13px</span></div>
|
||||
</div>
|
||||
<div class="cfg-group">
|
||||
<div class="cfg-lbl">SEUILS D'ALERTE</div>
|
||||
<div class="cfg-row"><span>Warning CPU/RAM</span><input type="range" class="cfg-slider" min="50" max="95" value="70"><span class="cfg-v">70%</span></div>
|
||||
<div class="cfg-row"><span>Erreur CPU/RAM</span><input type="range" class="cfg-slider" min="60" max="100" value="85"><span class="cfg-v">85%</span></div>
|
||||
</div>
|
||||
<div class="cfg-group">
|
||||
<div class="cfg-lbl">AGENTS HORS LIGNE</div>
|
||||
<div class="cfg-row"><span>Affichage</span>
|
||||
<select class="cfg-select"><option>Visible (grisé)</option><option>Masqué</option></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cfg-group">
|
||||
<div class="cfg-lbl">RÉTENTION HISTORIQUE</div>
|
||||
<div class="cfg-row"><span>Durée</span>
|
||||
<select class="cfg-select"><option>7 jours</option><option selected>30 jours</option><option>90 jours</option><option>1 an</option></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cfg-foot">
|
||||
<button class="btn" onclick="document.getElementById('cfg-overlay').style.display='none'">Annuler</button>
|
||||
<button class="btn primary"><i class="fa-solid fa-floppy-disk"></i> Sauvegarder</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function toggleTheme() {
|
||||
const h = document.documentElement;
|
||||
const i = document.getElementById('theme-icon');
|
||||
if (h.dataset.theme === 'dark') {
|
||||
h.dataset.theme = 'light';
|
||||
i.className = 'fa-solid fa-sun';
|
||||
} else {
|
||||
h.dataset.theme = 'dark';
|
||||
i.className = 'fa-solid fa-moon';
|
||||
}
|
||||
}
|
||||
function showPopup() {
|
||||
document.getElementById('popup-overlay').style.display = 'flex';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,541 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Nanometrics — Layout v4</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Share+Tech+Mono:wght@400&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<script src="/helper.js"></script>
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
:root[data-theme="dark"] {
|
||||
--accent:#fe8019; --accent-soft:#d65d0e; --accent-glow:rgba(254,128,25,0.3);
|
||||
--bg-0:#1d1813; --bg-1:#2a231d; --bg-2:#32291f; --bg-3:#3c332a; --bg-4:#4a3f33; --bg-5:#57493c;
|
||||
--ink-1:#f2e5c7; --ink-2:#d5c4a1; --ink-3:#a89984; --ink-4:#7c6f64;
|
||||
--ok:#4dbb26; --warn:#fabd2f; --err:#fb4934; --info:#83a598; --blue:#3db0d1; --purple:#c882c8;
|
||||
--border-1:rgba(255,255,255,0.06); --border-2:rgba(255,255,255,0.12); --border-3:rgba(255,255,255,0.22);
|
||||
--tile-3d:0 1px 0 rgba(255,255,255,0.08) inset,0 -1px 0 rgba(0,0,0,0.3) inset,0 4px 16px rgba(0,0,0,0.5);
|
||||
--font-ui:'Inter',system-ui,sans-serif; --font-mono:'JetBrains Mono',monospace; --font-terminal:'Share Tech Mono',monospace;
|
||||
}
|
||||
:root[data-theme="light"] {
|
||||
--accent:#af3a03; --accent-soft:#d65d0e; --accent-glow:rgba(175,58,3,0.2);
|
||||
--bg-0:#d5c4a1; --bg-1:#ebdbb2; --bg-2:#d5c4a1; --bg-3:#bdae93; --bg-4:#a89984; --bg-5:#928374;
|
||||
--ink-1:#3c3836; --ink-2:#504945; --ink-3:#665c54; --ink-4:#7c6f64;
|
||||
--ok:#3c911c; --warn:#b57614; --err:#9d0006; --blue:#2d82a3; --purple:#8c468c;
|
||||
--border-1:rgba(0,0,0,0.08); --border-2:rgba(0,0,0,0.15); --border-3:rgba(0,0,0,0.25);
|
||||
--tile-3d:0 1px 0 rgba(255,255,255,0.5) inset,0 -1px 0 rgba(0,0,0,0.1) inset,0 4px 16px rgba(0,0,0,0.12);
|
||||
--font-ui:'Inter',system-ui,sans-serif; --font-mono:'JetBrains Mono',monospace; --font-terminal:'Share Tech Mono',monospace;
|
||||
}
|
||||
|
||||
body { background:var(--bg-1); color:var(--ink-1); font-family:var(--font-ui); font-size:13px; height:100vh; display:flex; flex-direction:column; overflow:hidden; transition:background .2s,color .2s; }
|
||||
|
||||
/* HEADER */
|
||||
.header { background:var(--bg-2); border-bottom:1px solid var(--border-2); padding:0 20px; height:48px; display:flex; align-items:center; gap:14px; flex-shrink:0; }
|
||||
.logo { display:flex; align-items:center; gap:8px; }
|
||||
.logo-led { width:9px; height:9px; border-radius:50%; background:var(--accent); box-shadow:0 0 8px var(--accent-glow); animation:blink 2s infinite; }
|
||||
@keyframes blink{0%,100%{opacity:1}50%{opacity:.4}}
|
||||
.logo-name { font-weight:700; font-size:14px; letter-spacing:.05em; font-family:var(--font-terminal); }
|
||||
.logo-ver { font-size:10px; color:var(--ink-4); font-family:var(--font-terminal); }
|
||||
.h-sep { width:1px; height:24px; background:var(--border-2); }
|
||||
.h-spacer { flex:1; }
|
||||
.h-stats { display:flex; gap:14px; }
|
||||
.h-stat { display:flex; align-items:center; gap:5px; }
|
||||
.h-stat .lbl { font-size:9px; color:var(--ink-4); font-family:var(--font-terminal); letter-spacing:.06em; }
|
||||
.h-stat .val { font-family:var(--font-mono); font-weight:700; font-size:13px; }
|
||||
.c-ok{color:var(--ok)}.c-warn{color:var(--warn)}.c-err{color:var(--err)}.c-n{color:var(--ink-2)}
|
||||
.hbtn { width:34px; height:34px; border-radius:8px; border:1px solid var(--border-2); background:var(--bg-3); color:var(--ink-2); font-size:14px; display:flex; align-items:center; justify-content:center; cursor:pointer; position:relative; transition:background .12s,color .12s; }
|
||||
.hbtn:hover { background:var(--bg-4); color:var(--accent); }
|
||||
.hbtn .tip { position:absolute; top:calc(100%+6px); right:0; z-index:50; background:var(--bg-0); color:var(--ink-1); font-size:11px; padding:4px 8px; border-radius:5px; border:1px solid var(--border-2); white-space:nowrap; opacity:0; pointer-events:none; transition:opacity .12s; font-family:var(--font-ui); }
|
||||
.hbtn:hover .tip { opacity:1; }
|
||||
|
||||
/* GRID */
|
||||
.main { flex:1; padding:14px 16px; overflow-y:auto; }
|
||||
.agents-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(220px,1fr)); gap:10px; }
|
||||
|
||||
/* TILE */
|
||||
.tile { background:var(--bg-3); border-radius:10px; padding:12px 14px; border:1px solid var(--border-1); box-shadow:var(--tile-3d); cursor:pointer; transition:border-color .15s; display:flex; flex-direction:column; gap:9px; }
|
||||
.tile:hover { border-color:var(--border-3); }
|
||||
.tile.t-warn { border-color:rgba(250,189,47,.28); }
|
||||
.tile.t-err { border-color:rgba(251,73,52,.32); box-shadow:var(--tile-3d),0 0 16px rgba(251,73,52,.1); }
|
||||
.tile.t-off { opacity:.5; cursor:default; }
|
||||
.tile-head { display:flex; align-items:center; gap:8px; }
|
||||
.t-icon { width:28px; height:28px; border-radius:7px; background:var(--bg-4); display:flex; align-items:center; justify-content:center; color:var(--accent); font-size:13px; flex-shrink:0; overflow:hidden; }
|
||||
.t-icon img { width:100%; height:100%; object-fit:cover; border-radius:7px; }
|
||||
.t-names { flex:1; min-width:0; }
|
||||
.t-host { font-weight:600; font-size:13px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
||||
.t-ip { font-family:var(--font-mono); font-size:10px; color:var(--ink-4); }
|
||||
.t-led { width:8px; height:8px; border-radius:50%; flex-shrink:0; }
|
||||
.s-ok{background:var(--ok);box-shadow:0 0 6px var(--ok)}.s-warn{background:var(--warn);box-shadow:0 0 6px var(--warn);animation:blink 1.5s infinite}.s-err{background:var(--err);box-shadow:0 0 8px var(--err);animation:blink 1s infinite}.s-off{background:var(--ink-4)}
|
||||
.tile-gauges { display:flex; flex-direction:column; gap:5px; }
|
||||
.g-row { display:flex; align-items:center; gap:7px; }
|
||||
.g-ico { width:18px; height:18px; display:flex; align-items:center; justify-content:center; font-size:11px; color:var(--ink-3); flex-shrink:0; position:relative; cursor:help; }
|
||||
.g-ico .g-tip { position:absolute; bottom:calc(100%+5px); left:50%; transform:translateX(-50%); background:var(--bg-0); color:var(--ink-1); font-size:10px; padding:3px 7px; border-radius:4px; border:1px solid var(--border-2); white-space:nowrap; opacity:0; pointer-events:none; transition:opacity .12s; z-index:10; font-family:var(--font-ui); }
|
||||
.g-ico:hover .g-tip { opacity:1; }
|
||||
.g-bar { flex:1; height:5px; border-radius:3px; background:var(--bg-1); overflow:hidden; }
|
||||
.g-fill { height:100%; border-radius:3px; background:var(--ok); transition:width .4s; }
|
||||
.g-fill.w{background:var(--warn)}.g-fill.e{background:var(--err)}
|
||||
.g-val { font-family:var(--font-mono); font-size:11px; color:var(--ink-2); width:34px; text-align:right; flex-shrink:0; }
|
||||
.tile-foot { font-family:var(--font-terminal); font-size:10px; color:var(--ink-4); display:flex; align-items:center; gap:5px; }
|
||||
.tile-foot i { font-size:9px; }
|
||||
.tile-foot.offline { color:var(--err); }
|
||||
|
||||
/* FOOTER */
|
||||
.footer { background:var(--bg-0); border-top:1px solid var(--border-2); height:26px; display:flex; align-items:center; font-family:var(--font-terminal); font-size:11px; color:var(--ink-4); flex-shrink:0; }
|
||||
.f-mode { background:var(--accent); color:var(--bg-0); padding:0 12px; height:100%; display:flex; align-items:center; font-weight:700; letter-spacing:.04em; }
|
||||
.f-cell { padding:0 12px; border-right:1px solid var(--border-1); display:flex; align-items:center; gap:5px; height:100%; }
|
||||
.f-val { font-family:var(--font-mono); color:var(--ink-2); }
|
||||
.f-val.w { color:var(--warn); }
|
||||
.f-minibar { width:36px; height:4px; border-radius:2px; background:var(--bg-3); overflow:hidden; }
|
||||
.f-minifill { height:100%; border-radius:2px; background:var(--ok); }
|
||||
.f-minifill.w { background:var(--warn); }
|
||||
.f-spacer { flex:1; }
|
||||
.f-right { padding:0 12px; display:flex; align-items:center; gap:6px; color:var(--ink-3); }
|
||||
.f-time { font-family:var(--font-mono); color:var(--ink-2); }
|
||||
|
||||
/* ══ POPUP ══ */
|
||||
.overlay { position:fixed; inset:0; background:rgba(0,0,0,.65); z-index:100; display:flex; align-items:center; justify-content:center; backdrop-filter:blur(2px); }
|
||||
.popup { background:var(--bg-2); border:1px solid var(--border-3); border-radius:12px; width:540px; max-width:96vw; max-height:92vh; box-shadow:0 24px 64px rgba(0,0,0,.7); display:flex; flex-direction:column; overflow:hidden; }
|
||||
|
||||
/* popup header */
|
||||
.pop-head { background:var(--bg-3); padding:14px 18px; border-bottom:1px solid var(--border-2); display:flex; align-items:center; gap:12px; }
|
||||
|
||||
/* agent icon — cliquable pour upload */
|
||||
.agent-icon-wrap {
|
||||
position:relative; width:44px; height:44px; border-radius:10px; flex-shrink:0;
|
||||
cursor:pointer; overflow:hidden;
|
||||
background:var(--bg-4); display:flex; align-items:center; justify-content:center;
|
||||
color:var(--accent); font-size:18px;
|
||||
border:2px solid var(--border-2); transition:border-color .15s;
|
||||
}
|
||||
.agent-icon-wrap:hover { border-color:var(--accent); }
|
||||
.agent-icon-wrap img { width:100%; height:100%; object-fit:cover; }
|
||||
.agent-icon-overlay {
|
||||
position:absolute; inset:0; background:rgba(0,0,0,.6);
|
||||
display:flex; flex-direction:column; align-items:center; justify-content:center; gap:2px;
|
||||
opacity:0; transition:opacity .15s;
|
||||
font-size:10px; color:#fff; font-family:var(--font-ui);
|
||||
}
|
||||
.agent-icon-overlay i { font-size:14px; }
|
||||
.agent-icon-wrap:hover .agent-icon-overlay { opacity:1; }
|
||||
#icon-upload { display:none; }
|
||||
|
||||
.pop-head-info { flex:1; }
|
||||
.pop-host { font-weight:700; font-size:15px; }
|
||||
.pop-ip { font-family:var(--font-mono); font-size:11px; color:var(--ink-4); }
|
||||
.pop-led { width:10px; height:10px; border-radius:50%; background:var(--ok); box-shadow:0 0 8px var(--ok); flex-shrink:0; }
|
||||
.pop-close { width:28px; height:28px; border-radius:6px; background:var(--bg-5); color:var(--ink-3); display:flex; align-items:center; justify-content:center; cursor:pointer; font-size:12px; transition:background .12s,color .12s; }
|
||||
.pop-close:hover { background:var(--err); color:#fff; }
|
||||
|
||||
/* upload feedback */
|
||||
.upload-hint { font-size:10px; color:var(--ink-4); font-family:var(--font-terminal); margin-top:3px; }
|
||||
|
||||
/* popup body */
|
||||
.pop-body { padding:16px 18px; display:flex; flex-direction:column; gap:14px; overflow-y:auto; }
|
||||
.sec-title { font-size:9px; color:var(--ink-4); font-family:var(--font-terminal); letter-spacing:.08em; margin-bottom:8px; }
|
||||
|
||||
/* KPI row */
|
||||
.kpi-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:7px; }
|
||||
.kpi { background:var(--bg-3); border-radius:8px; padding:10px 12px; border:1px solid var(--border-1); box-shadow:var(--tile-3d); }
|
||||
.kpi-lbl { font-size:9px; color:var(--ink-4); font-family:var(--font-terminal); letter-spacing:.06em; }
|
||||
.kpi-val { font-family:var(--font-mono); font-size:20px; font-weight:700; line-height:1.1; margin-top:2px; }
|
||||
.kpi-val .u { font-size:10px; color:var(--ink-3); font-weight:400; }
|
||||
.kpi-sub { font-size:10px; color:var(--ink-4); font-family:var(--font-mono); margin-top:2px; }
|
||||
|
||||
/* ── COURBES HISTORIQUES ── */
|
||||
.charts-grid { display:grid; grid-template-columns:1fr 1fr; gap:10px; }
|
||||
.chart-card { background:var(--bg-3); border-radius:8px; padding:10px 12px; border:1px solid var(--border-1); box-shadow:var(--tile-3d); }
|
||||
.chart-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:8px; }
|
||||
.chart-label { display:flex; align-items:center; gap:6px; font-size:10px; font-family:var(--font-terminal); letter-spacing:.06em; color:var(--ink-3); }
|
||||
.chart-label i { font-size:11px; }
|
||||
.chart-cur { font-family:var(--font-mono); font-size:16px; font-weight:700; }
|
||||
.chart-svg { width:100%; height:56px; display:block; }
|
||||
.chart-axis { display:flex; justify-content:space-between; margin-top:3px; font-family:var(--font-terminal); font-size:9px; color:var(--ink-4); }
|
||||
|
||||
/* disk jauge conservée */
|
||||
.det-gauges { display:flex; flex-direction:column; gap:7px; }
|
||||
.dg-row { display:flex; align-items:center; gap:10px; }
|
||||
.dg-ico { width:22px; text-align:center; font-size:13px; position:relative; cursor:help; }
|
||||
.dg-ico .dg-tip { position:absolute; bottom:calc(100%+5px); left:50%; transform:translateX(-50%); background:var(--bg-0); color:var(--ink-1); font-size:10px; padding:3px 8px; border-radius:4px; border:1px solid var(--border-2); white-space:nowrap; opacity:0; pointer-events:none; transition:opacity .1s; z-index:10; font-family:var(--font-ui); }
|
||||
.dg-ico:hover .dg-tip { opacity:1; }
|
||||
.dg-bar { flex:1; height:7px; border-radius:4px; background:var(--bg-1); overflow:hidden; }
|
||||
.dg-fill { height:100%; border-radius:4px; background:var(--ok); }
|
||||
.dg-fill.w{background:var(--warn)}.dg-fill.e{background:var(--err)}
|
||||
.dg-val { font-family:var(--font-mono); font-size:12px; color:var(--ink-2); width:90px; text-align:right; }
|
||||
|
||||
/* meta */
|
||||
.meta-grid { display:grid; grid-template-columns:1fr 1fr; gap:6px; }
|
||||
.meta { background:var(--bg-3); border-radius:6px; padding:8px 10px; border:1px solid var(--border-1); }
|
||||
.meta-lbl { font-size:9px; color:var(--ink-4); font-family:var(--font-terminal); letter-spacing:.06em; }
|
||||
.meta-val { font-family:var(--font-mono); font-size:12px; color:var(--ink-2); margin-top:2px; }
|
||||
|
||||
/* popup footer — pas de bouton Fermer */
|
||||
.pop-foot { padding:10px 18px; border-top:1px solid var(--border-2); background:var(--bg-3); display:flex; align-items:center; gap:8px; }
|
||||
.pop-uptime { font-family:var(--font-terminal); font-size:11px; color:var(--ink-4); flex:1; }
|
||||
.btn { padding:6px 14px; border-radius:8px; border:1px solid var(--border-2); background:var(--bg-4); color:var(--ink-2); font-size:12px; font-family:var(--font-ui); cursor:pointer; display:flex; align-items:center; gap:6px; transition:background .1s; }
|
||||
.btn:hover { background:var(--bg-5); }
|
||||
.btn.primary { background:var(--accent); color:var(--bg-0); border-color:var(--accent-soft); font-weight:600; }
|
||||
.btn.primary:hover { background:var(--accent-soft); }
|
||||
|
||||
/* scrollbar */
|
||||
::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:var(--bg-1)}::-webkit-scrollbar-thumb{background:var(--bg-4);border-radius:3px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- HEADER -->
|
||||
<div class="header">
|
||||
<div class="logo">
|
||||
<div class="logo-led"></div>
|
||||
<span class="logo-name">NANOMETRICS</span>
|
||||
<span class="logo-ver">v1.0</span>
|
||||
</div>
|
||||
<div class="h-sep"></div>
|
||||
<div class="h-stats">
|
||||
<div class="h-stat"><span class="lbl">AGENTS</span><span class="val c-n">8</span></div>
|
||||
<div class="h-stat"><span class="lbl">OK</span><span class="val c-ok">5</span></div>
|
||||
<div class="h-stat"><span class="lbl">WARN</span><span class="val c-warn">2</span></div>
|
||||
<div class="h-stat"><span class="lbl">ERR</span><span class="val c-err">1</span></div>
|
||||
</div>
|
||||
<div class="h-spacer"></div>
|
||||
<div class="hbtn" onclick="toggleTheme()">
|
||||
<i class="fa-solid fa-moon" id="theme-icon"></i>
|
||||
<span class="tip">Thème clair / sombre</span>
|
||||
</div>
|
||||
<div class="hbtn" onclick="alert('Config panel')">
|
||||
<i class="fa-solid fa-sliders"></i>
|
||||
<span class="tip">Configuration interface</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- GRID -->
|
||||
<div class="main">
|
||||
<div class="agents-grid">
|
||||
<div class="tile" onclick="showPopup()">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon"><i class="fa-solid fa-server"></i></div>
|
||||
<div class="t-names"><div class="t-host">srv-prod-01</div><div class="t-ip">10.0.0.11</div></div>
|
||||
<div class="t-led s-ok"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row"><div class="g-ico"><i class="fa-solid fa-microchip"></i><span class="g-tip">Processeur (CPU)</span></div><div class="g-bar"><div class="g-fill" style="width:42%"></div></div><span class="g-val">42%</span></div>
|
||||
<div class="g-row"><div class="g-ico"><i class="fa-solid fa-memory"></i><span class="g-tip">Mémoire RAM</span></div><div class="g-bar"><div class="g-fill" style="width:58%"></div></div><span class="g-val">58%</span></div>
|
||||
<div class="g-row"><div class="g-ico"><i class="fa-solid fa-hard-drive"></i><span class="g-tip">Espace disque</span></div><div class="g-bar"><div class="g-fill" style="width:31%"></div></div><span class="g-val">31%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>14j 6h</div>
|
||||
</div>
|
||||
<div class="tile t-warn">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon"><i class="fa-solid fa-server"></i></div>
|
||||
<div class="t-names"><div class="t-host">srv-backup-02</div><div class="t-ip">10.0.0.12</div></div>
|
||||
<div class="t-led s-warn"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row"><div class="g-ico" style="color:var(--warn)"><i class="fa-solid fa-microchip"></i><span class="g-tip">CPU — élevé</span></div><div class="g-bar"><div class="g-fill w" style="width:78%"></div></div><span class="g-val" style="color:var(--warn)">78%</span></div>
|
||||
<div class="g-row"><div class="g-ico" style="color:var(--warn)"><i class="fa-solid fa-memory"></i><span class="g-tip">RAM — élevée</span></div><div class="g-bar"><div class="g-fill w" style="width:72%"></div></div><span class="g-val" style="color:var(--warn)">72%</span></div>
|
||||
<div class="g-row"><div class="g-ico"><i class="fa-solid fa-hard-drive"></i><span class="g-tip">Espace disque</span></div><div class="g-bar"><div class="g-fill" style="width:20%"></div></div><span class="g-val">20%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>3j 14h</div>
|
||||
</div>
|
||||
<div class="tile t-err">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon" style="color:var(--err)"><i class="fa-solid fa-microchip"></i></div>
|
||||
<div class="t-names"><div class="t-host">rpi-sensor-03</div><div class="t-ip">10.0.0.23</div></div>
|
||||
<div class="t-led s-err"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row"><div class="g-ico"><i class="fa-solid fa-microchip"></i><span class="g-tip">CPU</span></div><div class="g-bar"><div class="g-fill" style="width:12%"></div></div><span class="g-val">12%</span></div>
|
||||
<div class="g-row"><div class="g-ico"><i class="fa-solid fa-memory"></i><span class="g-tip">RAM</span></div><div class="g-bar"><div class="g-fill" style="width:38%"></div></div><span class="g-val">38%</span></div>
|
||||
<div class="g-row"><div class="g-ico" style="color:var(--err)"><i class="fa-solid fa-hard-drive"></i><span class="g-tip">Disque critique !</span></div><div class="g-bar"><div class="g-fill e" style="width:94%"></div></div><span class="g-val" style="color:var(--err)">94%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>62j 1h</div>
|
||||
</div>
|
||||
<div class="tile">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon"><i class="fa-solid fa-hard-drive"></i></div>
|
||||
<div class="t-names"><div class="t-host">nas-storage-04</div><div class="t-ip">10.0.0.30</div></div>
|
||||
<div class="t-led s-ok"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row"><div class="g-ico"><i class="fa-solid fa-microchip"></i><span class="g-tip">CPU</span></div><div class="g-bar"><div class="g-fill" style="width:8%"></div></div><span class="g-val">8%</span></div>
|
||||
<div class="g-row"><div class="g-ico"><i class="fa-solid fa-memory"></i><span class="g-tip">RAM</span></div><div class="g-bar"><div class="g-fill" style="width:45%"></div></div><span class="g-val">45%</span></div>
|
||||
<div class="g-row"><div class="g-ico"><i class="fa-solid fa-hard-drive"></i><span class="g-tip">Disque</span></div><div class="g-bar"><div class="g-fill" style="width:66%"></div></div><span class="g-val">66%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>128j 3h</div>
|
||||
</div>
|
||||
<div class="tile">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon"><i class="fa-solid fa-database"></i></div>
|
||||
<div class="t-names"><div class="t-host">db-primary-05</div><div class="t-ip">10.0.0.40</div></div>
|
||||
<div class="t-led s-ok"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row"><div class="g-ico"><i class="fa-solid fa-microchip"></i><span class="g-tip">CPU</span></div><div class="g-bar"><div class="g-fill" style="width:25%"></div></div><span class="g-val">25%</span></div>
|
||||
<div class="g-row"><div class="g-ico"><i class="fa-solid fa-memory"></i><span class="g-tip">RAM</span></div><div class="g-bar"><div class="g-fill" style="width:61%"></div></div><span class="g-val">61%</span></div>
|
||||
<div class="g-row"><div class="g-ico"><i class="fa-solid fa-hard-drive"></i><span class="g-tip">Disque</span></div><div class="g-bar"><div class="g-fill" style="width:48%"></div></div><span class="g-val">48%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>7j 22h</div>
|
||||
</div>
|
||||
<div class="tile t-warn">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon"><i class="fa-solid fa-globe"></i></div>
|
||||
<div class="t-names"><div class="t-host">web-front-06</div><div class="t-ip">10.0.0.51</div></div>
|
||||
<div class="t-led s-warn"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row"><div class="g-ico" style="color:var(--warn)"><i class="fa-solid fa-microchip"></i><span class="g-tip">CPU — élevé</span></div><div class="g-bar"><div class="g-fill w" style="width:71%"></div></div><span class="g-val" style="color:var(--warn)">71%</span></div>
|
||||
<div class="g-row"><div class="g-ico"><i class="fa-solid fa-memory"></i><span class="g-tip">RAM</span></div><div class="g-bar"><div class="g-fill" style="width:55%"></div></div><span class="g-val">55%</span></div>
|
||||
<div class="g-row"><div class="g-ico"><i class="fa-solid fa-hard-drive"></i><span class="g-tip">Disque</span></div><div class="g-bar"><div class="g-fill" style="width:28%"></div></div><span class="g-val">28%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>21j 8h</div>
|
||||
</div>
|
||||
<div class="tile t-off">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon" style="color:var(--ink-4)"><i class="fa-solid fa-server"></i></div>
|
||||
<div class="t-names"><div class="t-host">srv-dev-07</div><div class="t-ip">10.0.0.60</div></div>
|
||||
<div class="t-led s-off"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row"><div class="g-ico"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"></div><span class="g-val" style="color:var(--ink-4)">—</span></div>
|
||||
<div class="g-row"><div class="g-ico"><i class="fa-solid fa-memory"></i></div><div class="g-bar"></div><span class="g-val" style="color:var(--ink-4)">—</span></div>
|
||||
<div class="g-row"><div class="g-ico"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"></div><span class="g-val" style="color:var(--ink-4)">—</span></div>
|
||||
</div>
|
||||
<div class="tile-foot offline"><i class="fa-solid fa-circle-xmark"></i>Hors ligne · vu il y a 2h</div>
|
||||
</div>
|
||||
<div class="tile">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon"><i class="fa-solid fa-network-wired"></i></div>
|
||||
<div class="t-names"><div class="t-host">gateway-08</div><div class="t-ip">10.0.0.1</div></div>
|
||||
<div class="t-led s-ok"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row"><div class="g-ico"><i class="fa-solid fa-microchip"></i><span class="g-tip">CPU</span></div><div class="g-bar"><div class="g-fill" style="width:5%"></div></div><span class="g-val">5%</span></div>
|
||||
<div class="g-row"><div class="g-ico"><i class="fa-solid fa-memory"></i><span class="g-tip">RAM</span></div><div class="g-bar"><div class="g-fill" style="width:22%"></div></div><span class="g-val">22%</span></div>
|
||||
<div class="g-row"><div class="g-ico"><i class="fa-solid fa-hard-drive"></i><span class="g-tip">Disque</span></div><div class="g-bar"><div class="g-fill" style="width:15%"></div></div><span class="g-val">15%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>365j 0h</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<div class="footer">
|
||||
<div class="f-mode">LIVE</div>
|
||||
<div class="f-cell"><i class="fa-solid fa-server" style="font-size:10px"></i><span>SERVEUR</span></div>
|
||||
<div class="f-cell"><i class="fa-solid fa-microchip" style="font-size:10px"></i><span class="f-val">18%</span><div class="f-minibar"><div class="f-minifill" style="width:18%"></div></div></div>
|
||||
<div class="f-cell"><i class="fa-solid fa-memory" style="font-size:10px"></i><span class="f-val w">71%</span><div class="f-minibar"><div class="f-minifill w" style="width:71%"></div></div></div>
|
||||
<div class="f-spacer"></div>
|
||||
<div class="f-right"><i class="fa-solid fa-rotate"></i><span>Actualisation : <span class="f-time">22:14:07</span></span></div>
|
||||
</div>
|
||||
|
||||
<!-- ══ POPUP ══ -->
|
||||
<div class="overlay" id="popup-overlay" style="display:flex" onclick="if(event.target===this)this.style.display='none'">
|
||||
<div class="popup" onclick="event.stopPropagation()">
|
||||
|
||||
<!-- HEADER POPUP -->
|
||||
<div class="pop-head">
|
||||
|
||||
<!-- Icône agent — cliquer pour uploader -->
|
||||
<div class="agent-icon-wrap" onclick="document.getElementById('icon-upload').click()" title="Changer l'icône">
|
||||
<i class="fa-solid fa-server" id="agent-icon-fa"></i>
|
||||
<img id="agent-icon-img" src="" style="display:none">
|
||||
<div class="agent-icon-overlay">
|
||||
<i class="fa-solid fa-camera"></i>
|
||||
<span>Changer</span>
|
||||
</div>
|
||||
</div>
|
||||
<input type="file" id="icon-upload" accept=".svg,.jpg,.jpeg,.png,.webp" onchange="handleIconUpload(event)">
|
||||
|
||||
<div class="pop-head-info">
|
||||
<div class="pop-host">srv-prod-01</div>
|
||||
<div class="pop-ip">10.0.0.11</div>
|
||||
<div class="upload-hint" id="upload-hint">Cliquer sur l'icône pour personnaliser · SVG, JPG, PNG, WEBP · max 128×128 px</div>
|
||||
</div>
|
||||
<div class="pop-led"></div>
|
||||
<div class="pop-close" onclick="document.getElementById('popup-overlay').style.display='none'">
|
||||
<i class="fa-solid fa-xmark"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- BODY -->
|
||||
<div class="pop-body">
|
||||
|
||||
<!-- KPIs actuels -->
|
||||
<div>
|
||||
<div class="sec-title">MÉTRIQUES ACTUELLES</div>
|
||||
<div class="kpi-grid">
|
||||
<div class="kpi"><div class="kpi-lbl">CPU</div><div class="kpi-val c-ok">42<span class="u">%</span></div><div class="kpi-sub">4 cœurs</div></div>
|
||||
<div class="kpi"><div class="kpi-lbl">MÉMOIRE</div><div class="kpi-val">3.7<span class="u">Go</span></div><div class="kpi-sub">/ 8 Go · 46%</div></div>
|
||||
<div class="kpi"><div class="kpi-lbl">DISQUE</div><div class="kpi-val">62<span class="u">Go</span></div><div class="kpi-sub">/ 200 Go · 31%</div></div>
|
||||
<div class="kpi"><div class="kpi-lbl">UPTIME</div><div class="kpi-val" style="font-size:15px;color:var(--ink-1)">14j 6h</div><div class="kpi-sub">depuis boot</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- COURBES HISTORIQUES CPU + RAM -->
|
||||
<div>
|
||||
<div class="sec-title">HISTORIQUE — 30 DERNIÈRES MINUTES</div>
|
||||
<div class="charts-grid">
|
||||
|
||||
<!-- CPU chart -->
|
||||
<div class="chart-card">
|
||||
<div class="chart-header">
|
||||
<div class="chart-label" style="color:var(--accent)"><i class="fa-solid fa-microchip"></i>CPU</div>
|
||||
<span class="chart-cur c-ok">42%</span>
|
||||
</div>
|
||||
<svg class="chart-svg" viewBox="0 0 200 56" preserveAspectRatio="none" id="cpu-chart"></svg>
|
||||
<div class="chart-axis"><span>-30min</span><span>-15min</span><span>maintenant</span></div>
|
||||
</div>
|
||||
|
||||
<!-- RAM chart -->
|
||||
<div class="chart-card">
|
||||
<div class="chart-header">
|
||||
<div class="chart-label" style="color:var(--blue)"><i class="fa-solid fa-memory"></i>RAM</div>
|
||||
<span class="chart-cur" style="color:var(--blue)">46%</span>
|
||||
</div>
|
||||
<svg class="chart-svg" viewBox="0 0 200 56" preserveAspectRatio="none" id="ram-chart"></svg>
|
||||
<div class="chart-axis"><span>-30min</span><span>-15min</span><span>maintenant</span></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DISQUE — jauge barre conservée (pas de série temporelle pertinente) -->
|
||||
<div>
|
||||
<div class="sec-title">STOCKAGE</div>
|
||||
<div class="det-gauges">
|
||||
<div class="dg-row">
|
||||
<div class="dg-ico"><i class="fa-solid fa-hard-drive"></i><span class="dg-tip">Disque utilisé</span></div>
|
||||
<div class="dg-bar"><div class="dg-fill" style="width:31%"></div></div>
|
||||
<span class="dg-val">62 / 200 Go</span>
|
||||
</div>
|
||||
<div class="dg-row">
|
||||
<div class="dg-ico" style="color:var(--blue)"><i class="fa-solid fa-floppy-disk"></i><span class="dg-tip">Espace libre</span></div>
|
||||
<div class="dg-bar"><div class="dg-fill" style="width:69%;background:var(--blue)"></div></div>
|
||||
<span class="dg-val">138 Go libre</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- INFOS -->
|
||||
<div>
|
||||
<div class="sec-title">INFORMATIONS</div>
|
||||
<div class="meta-grid">
|
||||
<div class="meta"><div class="meta-lbl">HOSTNAME</div><div class="meta-val">srv-prod-01</div></div>
|
||||
<div class="meta"><div class="meta-lbl">ADRESSE IP</div><div class="meta-val">10.0.0.11</div></div>
|
||||
<div class="meta"><div class="meta-lbl">DERNIER CONTACT</div><div class="meta-val">22:14:07</div></div>
|
||||
<div class="meta"><div class="meta-lbl">FRÉQUENCE</div><div class="meta-val">CPU 2s · Disk 60s</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- FOOTER popup — pas de bouton Fermer -->
|
||||
<div class="pop-foot">
|
||||
<span class="pop-uptime"><i class="fa-solid fa-clock" style="margin-right:4px"></i>En ligne depuis 14 jours 6 heures</span>
|
||||
<button class="btn"><i class="fa-solid fa-chart-line"></i> Historique complet</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/* ── thème ── */
|
||||
function toggleTheme() {
|
||||
const h = document.documentElement;
|
||||
const i = document.getElementById('theme-icon');
|
||||
h.dataset.theme = h.dataset.theme === 'dark' ? 'light' : 'dark';
|
||||
i.className = h.dataset.theme === 'dark' ? 'fa-solid fa-moon' : 'fa-solid fa-sun';
|
||||
drawCharts();
|
||||
}
|
||||
|
||||
/* ── upload icône ── */
|
||||
function handleIconUpload(e) {
|
||||
const file = e.target.files[0];
|
||||
if (!file) return;
|
||||
const allowed = ['image/svg+xml','image/jpeg','image/png','image/webp'];
|
||||
if (!allowed.includes(file.type)) { alert('Format non supporté'); return; }
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(ev) {
|
||||
if (file.type === 'image/svg+xml') {
|
||||
// SVG : afficher directement
|
||||
showAgentIcon(ev.target.result, true);
|
||||
} else {
|
||||
// Raster : redimensionner canvas max 128×128 en respectant le ratio
|
||||
const img = new Image();
|
||||
img.onload = function() {
|
||||
const canvas = document.createElement('canvas');
|
||||
const ratio = Math.min(128 / img.width, 128 / img.height, 1);
|
||||
canvas.width = Math.round(img.width * ratio);
|
||||
canvas.height = Math.round(img.height * ratio);
|
||||
canvas.getContext('2d').drawImage(img, 0, 0, canvas.width, canvas.height);
|
||||
showAgentIcon(canvas.toDataURL(file.type === 'image/png' ? 'image/png' : 'image/jpeg', 0.9), false);
|
||||
};
|
||||
img.src = ev.target.result;
|
||||
}
|
||||
document.getElementById('upload-hint').textContent = '✓ Icône mise à jour — sauvegardée sur le serveur';
|
||||
document.getElementById('upload-hint').style.color = 'var(--ok)';
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
function showAgentIcon(src, isSvg) {
|
||||
document.getElementById('agent-icon-fa').style.display = 'none';
|
||||
const img = document.getElementById('agent-icon-img');
|
||||
img.src = src;
|
||||
img.style.display = 'block';
|
||||
}
|
||||
|
||||
/* ── courbes historiques ── */
|
||||
function makeCurve(pts, color, fillColor, w, h) {
|
||||
if (!pts.length) return '';
|
||||
const maxV = 100, minV = 0;
|
||||
const xs = pts.map((_, i) => (i / (pts.length - 1)) * w);
|
||||
const ys = pts.map(v => h - ((v - minV) / (maxV - minV)) * (h - 4) - 2);
|
||||
|
||||
// zone de seuil warn (70%) — ligne pointillée
|
||||
const warnY = h - ((70 - minV) / (maxV - minV)) * (h - 4) - 2;
|
||||
|
||||
let d = `M ${xs[0]} ${ys[0]}`;
|
||||
for (let i = 1; i < pts.length; i++) {
|
||||
const cx = (xs[i-1] + xs[i]) / 2;
|
||||
d += ` C ${cx} ${ys[i-1]}, ${cx} ${ys[i]}, ${xs[i]} ${ys[i]}`;
|
||||
}
|
||||
|
||||
const fillD = d + ` L ${xs[xs.length-1]} ${h} L ${xs[0]} ${h} Z`;
|
||||
|
||||
return `
|
||||
<defs>
|
||||
<linearGradient id="grad-${color.replace('#','')}" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="${fillColor}" stop-opacity="0.35"/>
|
||||
<stop offset="100%" stop-color="${fillColor}" stop-opacity="0.02"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<line x1="0" y1="${warnY}" x2="${w}" y2="${warnY}"
|
||||
stroke="var(--warn)" stroke-width="0.8" stroke-dasharray="3,3" opacity="0.5"/>
|
||||
<path d="${fillD}" fill="url(#grad-${color.replace('#','')})" />
|
||||
<path d="${d}" fill="none" stroke="${color}" stroke-width="1.5" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<circle cx="${xs[xs.length-1]}" cy="${ys[ys.length-1]}" r="2.5" fill="${color}"/>
|
||||
`;
|
||||
}
|
||||
|
||||
function drawCharts() {
|
||||
const cpuPts = [38,41,35,40,44,42,39,45,50,48,43,42,44,41,40,43,47,45,42,44,41,43,42,40,43,41,44,43,41,42];
|
||||
const ramPts = [44,44,45,45,46,46,47,47,46,46,45,46,46,47,47,46,46,45,46,46,46,46,46,46,46,46,46,46,46,46];
|
||||
|
||||
const accent = getComputedStyle(document.documentElement).getPropertyValue('--accent').trim();
|
||||
const blue = getComputedStyle(document.documentElement).getPropertyValue('--blue').trim();
|
||||
|
||||
document.getElementById('cpu-chart').innerHTML = makeCurve(cpuPts, accent, accent, 200, 56);
|
||||
document.getElementById('ram-chart').innerHTML = makeCurve(ramPts, blue, blue, 200, 56);
|
||||
}
|
||||
|
||||
window.addEventListener('load', drawCharts);
|
||||
|
||||
function showPopup() {
|
||||
document.getElementById('popup-overlay').style.display = 'flex';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,577 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Nanometrics — Layout v5</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Share+Tech+Mono:wght@400&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<script src="/helper.js"></script>
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
:root[data-theme="dark"] {
|
||||
--accent:#fe8019; --accent-soft:#d65d0e; --accent-glow:rgba(254,128,25,0.28);
|
||||
--bg-0:#1d1813; --bg-1:#2a231d; --bg-2:#32291f; --bg-3:#3c332a; --bg-4:#4a3f33; --bg-5:#57493c;
|
||||
--ink-1:#f2e5c7; --ink-2:#d5c4a1; --ink-3:#a89984; --ink-4:#7c6f64;
|
||||
--ok:#4dbb26; --warn:#fabd2f; --err:#fb4934; --info:#83a598; --blue:#3db0d1; --purple:#c882c8;
|
||||
--border-1:rgba(255,255,255,0.06); --border-2:rgba(255,255,255,0.12); --border-3:rgba(255,255,255,0.26);
|
||||
--tile-3d:0 1px 0 rgba(255,255,255,0.08) inset,0 -1px 0 rgba(0,0,0,0.3) inset,0 6px 20px rgba(0,0,0,0.5);
|
||||
--tile-press:inset 0 2px 8px rgba(0,0,0,0.5),inset 0 1px 3px rgba(0,0,0,0.4);
|
||||
--hover-glow:0 0 0 1px var(--accent-soft),0 0 24px var(--accent-glow),0 6px 20px rgba(0,0,0,0.5);
|
||||
--font-ui:'Inter',system-ui,sans-serif; --font-mono:'JetBrains Mono',monospace; --font-terminal:'Share Tech Mono',monospace;
|
||||
}
|
||||
:root[data-theme="light"] {
|
||||
--accent:#af3a03; --accent-soft:#d65d0e; --accent-glow:rgba(175,58,3,0.18);
|
||||
--bg-0:#d5c4a1; --bg-1:#ebdbb2; --bg-2:#d5c4a1; --bg-3:#bdae93; --bg-4:#a89984; --bg-5:#928374;
|
||||
--ink-1:#3c3836; --ink-2:#504945; --ink-3:#665c54; --ink-4:#7c6f64;
|
||||
--ok:#3c911c; --warn:#b57614; --err:#9d0006; --blue:#2d82a3; --purple:#8c468c;
|
||||
--border-1:rgba(0,0,0,0.08); --border-2:rgba(0,0,0,0.15); --border-3:rgba(0,0,0,0.3);
|
||||
--tile-3d:0 1px 0 rgba(255,255,255,0.55) inset,0 -1px 0 rgba(0,0,0,0.08) inset,0 4px 14px rgba(0,0,0,0.13);
|
||||
--tile-press:inset 0 2px 6px rgba(0,0,0,0.2),inset 0 1px 3px rgba(0,0,0,0.15);
|
||||
--hover-glow:0 0 0 1px var(--accent-soft),0 0 18px var(--accent-glow),0 4px 14px rgba(0,0,0,0.13);
|
||||
--font-ui:'Inter',system-ui,sans-serif; --font-mono:'JetBrains Mono',monospace; --font-terminal:'Share Tech Mono',monospace;
|
||||
}
|
||||
|
||||
body { background:var(--bg-1); color:var(--ink-1); font-family:var(--font-ui); font-size:13px; height:100vh; display:flex; flex-direction:column; overflow:hidden; transition:background .2s,color .2s; }
|
||||
|
||||
/* ══ TOOLTIP SYSTÈME GLOBAL ══
|
||||
Chaque élément avec [data-tip] affiche un tooltip centré via JS,
|
||||
hors du flux — plus de problème d'offset ni de clip. */
|
||||
#tooltip {
|
||||
position:fixed; z-index:9999; pointer-events:none;
|
||||
background:var(--bg-0); color:var(--ink-1);
|
||||
border:1px solid var(--border-3); border-radius:5px;
|
||||
padding:4px 9px; font-size:11px; font-family:var(--font-ui);
|
||||
white-space:nowrap; opacity:0; transition:opacity .12s;
|
||||
box-shadow:0 4px 12px rgba(0,0,0,.4);
|
||||
}
|
||||
#tooltip.show { opacity:1; }
|
||||
|
||||
/* HEADER */
|
||||
.header { background:var(--bg-2); border-bottom:1px solid var(--border-2); padding:0 20px; height:48px; display:flex; align-items:center; gap:12px; flex-shrink:0; }
|
||||
.logo { display:flex; align-items:center; gap:8px; }
|
||||
.logo-led { width:9px; height:9px; border-radius:50%; background:var(--accent); box-shadow:0 0 8px var(--accent-glow); animation:blink 2s infinite; }
|
||||
@keyframes blink{0%,100%{opacity:1}50%{opacity:.4}}
|
||||
.logo-name { font-weight:700; font-size:14px; letter-spacing:.05em; font-family:var(--font-terminal); }
|
||||
.logo-ver { font-size:10px; color:var(--ink-4); font-family:var(--font-terminal); }
|
||||
.h-sep { width:1px; height:24px; background:var(--border-2); }
|
||||
.h-spacer { flex:1; }
|
||||
.h-stats { display:flex; gap:14px; }
|
||||
.h-stat { display:flex; align-items:center; gap:5px; }
|
||||
.h-stat .lbl { font-size:9px; color:var(--ink-4); font-family:var(--font-terminal); letter-spacing:.06em; }
|
||||
.h-stat .val { font-family:var(--font-mono); font-weight:700; font-size:13px; }
|
||||
.c-ok{color:var(--ok)}.c-warn{color:var(--warn)}.c-err{color:var(--err)}.c-n{color:var(--ink-2)}
|
||||
|
||||
/* Header icon buttons */
|
||||
.hbtn {
|
||||
width:34px; height:34px; border-radius:8px;
|
||||
border:1px solid var(--border-2); background:var(--bg-3); color:var(--ink-2);
|
||||
font-size:14px; display:flex; align-items:center; justify-content:center;
|
||||
cursor:pointer; user-select:none;
|
||||
transition:background .12s, color .12s, box-shadow .08s, transform .08s;
|
||||
}
|
||||
.hbtn:hover { background:var(--bg-4); color:var(--accent); }
|
||||
.hbtn:active {
|
||||
transform:translateY(1px) scale(.96);
|
||||
box-shadow:var(--tile-press);
|
||||
background:var(--bg-5);
|
||||
}
|
||||
|
||||
/* GRID */
|
||||
.main { flex:1; padding:14px 16px; overflow-y:auto; }
|
||||
.agents-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(220px,1fr)); gap:10px; }
|
||||
|
||||
/* TILE */
|
||||
.tile {
|
||||
background:var(--bg-3); border-radius:10px; padding:12px 14px;
|
||||
border:1px solid var(--border-1); box-shadow:var(--tile-3d);
|
||||
cursor:pointer; user-select:none;
|
||||
display:flex; flex-direction:column; gap:9px;
|
||||
transition:box-shadow .15s, transform .08s, border-color .15s;
|
||||
}
|
||||
.tile:hover {
|
||||
box-shadow:var(--hover-glow);
|
||||
border-color:var(--accent-soft);
|
||||
}
|
||||
.tile:active {
|
||||
transform:translateY(2px) scale(.99);
|
||||
box-shadow:var(--tile-press);
|
||||
border-color:var(--accent);
|
||||
}
|
||||
.tile.t-warn { border-color:rgba(250,189,47,.3); }
|
||||
.tile.t-warn:hover { border-color:var(--warn); box-shadow:0 0 0 1px var(--warn),0 0 22px rgba(250,189,47,.22),0 6px 20px rgba(0,0,0,.5); }
|
||||
.tile.t-err { border-color:rgba(251,73,52,.35); box-shadow:var(--tile-3d),0 0 16px rgba(251,73,52,.12); }
|
||||
.tile.t-err:hover { border-color:var(--err); box-shadow:0 0 0 1px var(--err),0 0 22px rgba(251,73,52,.25),0 6px 20px rgba(0,0,0,.5); }
|
||||
.tile.t-off { opacity:.5; cursor:default; }
|
||||
.tile.t-off:hover { box-shadow:var(--tile-3d); border-color:var(--border-1); }
|
||||
.tile.t-off:active { transform:none; box-shadow:var(--tile-3d); }
|
||||
|
||||
.tile-head { display:flex; align-items:center; gap:8px; }
|
||||
.t-icon { width:28px; height:28px; border-radius:7px; background:var(--bg-4); display:flex; align-items:center; justify-content:center; color:var(--accent); font-size:13px; flex-shrink:0; overflow:hidden; }
|
||||
.t-icon img { width:100%; height:100%; object-fit:cover; }
|
||||
.t-names { flex:1; min-width:0; }
|
||||
.t-host { font-weight:600; font-size:13px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
||||
.t-ip { font-family:var(--font-mono); font-size:10px; color:var(--ink-4); }
|
||||
.t-led { width:8px; height:8px; border-radius:50%; flex-shrink:0; }
|
||||
.s-ok {background:var(--ok);box-shadow:0 0 6px var(--ok)}
|
||||
.s-warn{background:var(--warn);box-shadow:0 0 6px var(--warn);animation:blink 1.5s infinite}
|
||||
.s-err {background:var(--err);box-shadow:0 0 8px var(--err);animation:blink 1s infinite}
|
||||
.s-off {background:var(--ink-4)}
|
||||
|
||||
.tile-gauges { display:flex; flex-direction:column; gap:5px; }
|
||||
.g-row { display:flex; align-items:center; gap:7px; }
|
||||
.g-ico { width:18px; height:18px; display:flex; align-items:center; justify-content:center; font-size:11px; color:var(--ink-3); flex-shrink:0; cursor:help; }
|
||||
.g-bar { flex:1; height:5px; border-radius:3px; background:var(--bg-1); overflow:hidden; }
|
||||
.g-fill { height:100%; border-radius:3px; background:var(--ok); transition:width .4s; }
|
||||
.g-fill.w{background:var(--warn)}.g-fill.e{background:var(--err)}
|
||||
.g-val { font-family:var(--font-mono); font-size:11px; color:var(--ink-2); width:34px; text-align:right; flex-shrink:0; }
|
||||
.tile-foot { font-family:var(--font-terminal); font-size:10px; color:var(--ink-4); display:flex; align-items:center; gap:5px; }
|
||||
.tile-foot i { font-size:9px; }
|
||||
.tile-foot.offline { color:var(--err); }
|
||||
|
||||
/* FOOTER */
|
||||
.footer { background:var(--bg-0); border-top:1px solid var(--border-2); height:26px; display:flex; align-items:center; font-family:var(--font-terminal); font-size:11px; color:var(--ink-4); flex-shrink:0; }
|
||||
.f-mode { background:var(--accent); color:var(--bg-0); padding:0 12px; height:100%; display:flex; align-items:center; font-weight:700; letter-spacing:.04em; }
|
||||
.f-cell { padding:0 12px; border-right:1px solid var(--border-1); display:flex; align-items:center; gap:5px; height:100%; }
|
||||
.f-val { font-family:var(--font-mono); color:var(--ink-2); }
|
||||
.f-val.w { color:var(--warn); }
|
||||
.f-minibar { width:36px; height:4px; border-radius:2px; background:var(--bg-3); overflow:hidden; }
|
||||
.f-minifill { height:100%; border-radius:2px; background:var(--ok); }
|
||||
.f-minifill.w { background:var(--warn); }
|
||||
.f-spacer { flex:1; }
|
||||
.f-right { padding:0 12px; display:flex; align-items:center; gap:6px; color:var(--ink-3); }
|
||||
.f-time { font-family:var(--font-mono); color:var(--ink-2); }
|
||||
|
||||
/* ══ POPUP ══ */
|
||||
.overlay { position:fixed; inset:0; background:rgba(0,0,0,.65); z-index:100; display:flex; align-items:center; justify-content:center; backdrop-filter:blur(2px); }
|
||||
.popup { background:var(--bg-2); border:1px solid var(--border-3); border-radius:12px; width:540px; max-width:96vw; max-height:92vh; box-shadow:0 24px 64px rgba(0,0,0,.7); display:flex; flex-direction:column; overflow:hidden; }
|
||||
.pop-head { background:var(--bg-3); padding:14px 18px; border-bottom:1px solid var(--border-2); display:flex; align-items:center; gap:12px; }
|
||||
.agent-icon-wrap { position:relative; width:44px; height:44px; border-radius:10px; flex-shrink:0; cursor:pointer; overflow:hidden; background:var(--bg-4); display:flex; align-items:center; justify-content:center; color:var(--accent); font-size:18px; border:2px solid var(--border-2); transition:border-color .15s; }
|
||||
.agent-icon-wrap:hover { border-color:var(--accent); }
|
||||
.agent-icon-wrap img { width:100%; height:100%; object-fit:cover; }
|
||||
.agent-icon-overlay { position:absolute; inset:0; background:rgba(0,0,0,.6); display:flex; flex-direction:column; align-items:center; justify-content:center; gap:2px; opacity:0; transition:opacity .15s; font-size:10px; color:#fff; font-family:var(--font-ui); }
|
||||
.agent-icon-overlay i { font-size:14px; }
|
||||
.agent-icon-wrap:hover .agent-icon-overlay { opacity:1; }
|
||||
#icon-upload { display:none; }
|
||||
.pop-head-info { flex:1; }
|
||||
.pop-host { font-weight:700; font-size:15px; }
|
||||
.pop-ip { font-family:var(--font-mono); font-size:11px; color:var(--ink-4); }
|
||||
.upload-hint { font-size:10px; color:var(--ink-4); font-family:var(--font-terminal); margin-top:3px; }
|
||||
.pop-led { width:10px; height:10px; border-radius:50%; background:var(--ok); box-shadow:0 0 8px var(--ok); flex-shrink:0; }
|
||||
.pop-close {
|
||||
width:28px; height:28px; border-radius:6px; background:var(--bg-5); color:var(--ink-3);
|
||||
display:flex; align-items:center; justify-content:center; cursor:pointer; font-size:12px;
|
||||
transition:background .12s, color .12s, transform .08s, box-shadow .08s;
|
||||
border:1px solid var(--border-1); user-select:none;
|
||||
}
|
||||
.pop-close:hover { background:var(--err); color:#fff; }
|
||||
.pop-close:active { transform:translateY(1px) scale(.93); box-shadow:var(--tile-press); }
|
||||
|
||||
.pop-body { padding:16px 18px; display:flex; flex-direction:column; gap:14px; overflow-y:auto; }
|
||||
.sec-title { font-size:9px; color:var(--ink-4); font-family:var(--font-terminal); letter-spacing:.08em; margin-bottom:8px; }
|
||||
.kpi-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:7px; }
|
||||
.kpi { background:var(--bg-3); border-radius:8px; padding:10px 12px; border:1px solid var(--border-1); box-shadow:var(--tile-3d); }
|
||||
.kpi-lbl { font-size:9px; color:var(--ink-4); font-family:var(--font-terminal); letter-spacing:.06em; }
|
||||
.kpi-val { font-family:var(--font-mono); font-size:20px; font-weight:700; line-height:1.1; margin-top:2px; }
|
||||
.kpi-val .u { font-size:10px; color:var(--ink-3); font-weight:400; }
|
||||
.kpi-sub { font-size:10px; color:var(--ink-4); font-family:var(--font-mono); margin-top:2px; }
|
||||
.charts-grid { display:grid; grid-template-columns:1fr 1fr; gap:10px; }
|
||||
.chart-card { background:var(--bg-3); border-radius:8px; padding:10px 12px; border:1px solid var(--border-1); box-shadow:var(--tile-3d); }
|
||||
.chart-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:8px; }
|
||||
.chart-label { display:flex; align-items:center; gap:6px; font-size:10px; font-family:var(--font-terminal); letter-spacing:.06em; color:var(--ink-3); }
|
||||
.chart-cur { font-family:var(--font-mono); font-size:16px; font-weight:700; }
|
||||
.chart-svg { width:100%; height:56px; display:block; }
|
||||
.chart-axis { display:flex; justify-content:space-between; margin-top:3px; font-family:var(--font-terminal); font-size:9px; color:var(--ink-4); }
|
||||
.det-gauges { display:flex; flex-direction:column; gap:7px; }
|
||||
.dg-row { display:flex; align-items:center; gap:10px; }
|
||||
.dg-ico { width:22px; text-align:center; font-size:13px; cursor:help; }
|
||||
.dg-bar { flex:1; height:7px; border-radius:4px; background:var(--bg-1); overflow:hidden; }
|
||||
.dg-fill { height:100%; border-radius:4px; background:var(--ok); }
|
||||
.dg-fill.w{background:var(--warn)}.dg-fill.e{background:var(--err)}
|
||||
.dg-val { font-family:var(--font-mono); font-size:12px; color:var(--ink-2); width:90px; text-align:right; }
|
||||
.meta-grid { display:grid; grid-template-columns:1fr 1fr; gap:6px; }
|
||||
.meta { background:var(--bg-3); border-radius:6px; padding:8px 10px; border:1px solid var(--border-1); }
|
||||
.meta-lbl { font-size:9px; color:var(--ink-4); font-family:var(--font-terminal); letter-spacing:.06em; }
|
||||
.meta-val { font-family:var(--font-mono); font-size:12px; color:var(--ink-2); margin-top:2px; }
|
||||
.pop-foot { padding:10px 18px; border-top:1px solid var(--border-2); background:var(--bg-3); display:flex; align-items:center; gap:8px; }
|
||||
.pop-uptime { font-family:var(--font-terminal); font-size:11px; color:var(--ink-4); flex:1; }
|
||||
|
||||
/* Boutons génériques */
|
||||
.btn {
|
||||
padding:6px 14px; border-radius:8px; border:1px solid var(--border-2);
|
||||
background:var(--bg-4); color:var(--ink-2); font-size:12px; font-family:var(--font-ui);
|
||||
cursor:pointer; display:flex; align-items:center; gap:6px; user-select:none;
|
||||
transition:background .1s, transform .08s, box-shadow .08s;
|
||||
}
|
||||
.btn:hover { background:var(--bg-5); }
|
||||
.btn:active { transform:translateY(1px) scale(.97); box-shadow:var(--tile-press); }
|
||||
.btn.primary { background:var(--accent); color:var(--bg-0); border-color:var(--accent-soft); font-weight:600; }
|
||||
.btn.primary:hover { background:var(--accent-soft); }
|
||||
|
||||
::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:var(--bg-1)}::-webkit-scrollbar-thumb{background:var(--bg-4);border-radius:3px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- TOOLTIP GLOBAL -->
|
||||
<div id="tooltip"></div>
|
||||
|
||||
<!-- HEADER -->
|
||||
<div class="header">
|
||||
<div class="logo">
|
||||
<div class="logo-led"></div>
|
||||
<span class="logo-name">NANOMETRICS</span>
|
||||
<span class="logo-ver">v1.0</span>
|
||||
</div>
|
||||
<div class="h-sep"></div>
|
||||
<div class="h-stats">
|
||||
<div class="h-stat"><span class="lbl">AGENTS</span><span class="val c-n">8</span></div>
|
||||
<div class="h-stat"><span class="lbl">OK</span><span class="val c-ok">5</span></div>
|
||||
<div class="h-stat"><span class="lbl">WARN</span><span class="val c-warn">2</span></div>
|
||||
<div class="h-stat"><span class="lbl">ERR</span><span class="val c-err">1</span></div>
|
||||
</div>
|
||||
<div class="h-spacer"></div>
|
||||
<div class="hbtn" onclick="toggleTheme()" data-tip="Thème clair / sombre">
|
||||
<i class="fa-solid fa-moon" id="theme-icon"></i>
|
||||
</div>
|
||||
<div class="hbtn" data-tip="Configuration interface">
|
||||
<i class="fa-solid fa-sliders"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- GRID -->
|
||||
<div class="main">
|
||||
<div class="agents-grid">
|
||||
|
||||
<div class="tile" onclick="showPopup()">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon"><i class="fa-solid fa-server"></i></div>
|
||||
<div class="t-names"><div class="t-host">srv-prod-01</div><div class="t-ip">10.0.0.11</div></div>
|
||||
<div class="t-led s-ok"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row"><div class="g-ico" data-tip="Processeur (CPU)"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill" style="width:42%"></div></div><span class="g-val">42%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="Mémoire RAM"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill" style="width:58%"></div></div><span class="g-val">58%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="Espace disque"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill" style="width:31%"></div></div><span class="g-val">31%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>14j 6h</div>
|
||||
</div>
|
||||
|
||||
<div class="tile t-warn">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon"><i class="fa-solid fa-server"></i></div>
|
||||
<div class="t-names"><div class="t-host">srv-backup-02</div><div class="t-ip">10.0.0.12</div></div>
|
||||
<div class="t-led s-warn"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row"><div class="g-ico" data-tip="CPU — élevé" style="color:var(--warn)"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill w" style="width:78%"></div></div><span class="g-val" style="color:var(--warn)">78%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="RAM — élevée" style="color:var(--warn)"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill w" style="width:72%"></div></div><span class="g-val" style="color:var(--warn)">72%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="Espace disque"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill" style="width:20%"></div></div><span class="g-val">20%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>3j 14h</div>
|
||||
</div>
|
||||
|
||||
<div class="tile t-err">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon" style="color:var(--err)"><i class="fa-solid fa-microchip"></i></div>
|
||||
<div class="t-names"><div class="t-host">rpi-sensor-03</div><div class="t-ip">10.0.0.23</div></div>
|
||||
<div class="t-led s-err"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row"><div class="g-ico" data-tip="Processeur (CPU)"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill" style="width:12%"></div></div><span class="g-val">12%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="Mémoire RAM"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill" style="width:38%"></div></div><span class="g-val">38%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="Disque critique !" style="color:var(--err)"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill e" style="width:94%"></div></div><span class="g-val" style="color:var(--err)">94%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>62j 1h</div>
|
||||
</div>
|
||||
|
||||
<div class="tile">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon"><i class="fa-solid fa-hard-drive"></i></div>
|
||||
<div class="t-names"><div class="t-host">nas-storage-04</div><div class="t-ip">10.0.0.30</div></div>
|
||||
<div class="t-led s-ok"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row"><div class="g-ico" data-tip="Processeur (CPU)"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill" style="width:8%"></div></div><span class="g-val">8%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="Mémoire RAM"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill" style="width:45%"></div></div><span class="g-val">45%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="Espace disque"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill" style="width:66%"></div></div><span class="g-val">66%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>128j 3h</div>
|
||||
</div>
|
||||
|
||||
<div class="tile">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon"><i class="fa-solid fa-database"></i></div>
|
||||
<div class="t-names"><div class="t-host">db-primary-05</div><div class="t-ip">10.0.0.40</div></div>
|
||||
<div class="t-led s-ok"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row"><div class="g-ico" data-tip="Processeur (CPU)"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill" style="width:25%"></div></div><span class="g-val">25%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="Mémoire RAM"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill" style="width:61%"></div></div><span class="g-val">61%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="Espace disque"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill" style="width:48%"></div></div><span class="g-val">48%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>7j 22h</div>
|
||||
</div>
|
||||
|
||||
<div class="tile t-warn">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon"><i class="fa-solid fa-globe"></i></div>
|
||||
<div class="t-names"><div class="t-host">web-front-06</div><div class="t-ip">10.0.0.51</div></div>
|
||||
<div class="t-led s-warn"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row"><div class="g-ico" data-tip="CPU — élevé" style="color:var(--warn)"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill w" style="width:71%"></div></div><span class="g-val" style="color:var(--warn)">71%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="Mémoire RAM"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill" style="width:55%"></div></div><span class="g-val">55%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="Espace disque"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill" style="width:28%"></div></div><span class="g-val">28%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>21j 8h</div>
|
||||
</div>
|
||||
|
||||
<div class="tile t-off">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon" style="color:var(--ink-4)"><i class="fa-solid fa-server"></i></div>
|
||||
<div class="t-names"><div class="t-host">srv-dev-07</div><div class="t-ip">10.0.0.60</div></div>
|
||||
<div class="t-led s-off"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row"><div class="g-ico"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"></div><span class="g-val" style="color:var(--ink-4)">—</span></div>
|
||||
<div class="g-row"><div class="g-ico"><i class="fa-solid fa-memory"></i></div><div class="g-bar"></div><span class="g-val" style="color:var(--ink-4)">—</span></div>
|
||||
<div class="g-row"><div class="g-ico"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"></div><span class="g-val" style="color:var(--ink-4)">—</span></div>
|
||||
</div>
|
||||
<div class="tile-foot offline"><i class="fa-solid fa-circle-xmark"></i>Hors ligne · vu il y a 2h</div>
|
||||
</div>
|
||||
|
||||
<div class="tile">
|
||||
<div class="tile-head">
|
||||
<div class="t-icon"><i class="fa-solid fa-network-wired"></i></div>
|
||||
<div class="t-names"><div class="t-host">gateway-08</div><div class="t-ip">10.0.0.1</div></div>
|
||||
<div class="t-led s-ok"></div>
|
||||
</div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row"><div class="g-ico" data-tip="Processeur (CPU)"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill" style="width:5%"></div></div><span class="g-val">5%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="Mémoire RAM"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill" style="width:22%"></div></div><span class="g-val">22%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="Espace disque"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill" style="width:15%"></div></div><span class="g-val">15%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>365j 0h</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<div class="footer">
|
||||
<div class="f-mode">LIVE</div>
|
||||
<div class="f-cell"><i class="fa-solid fa-server" style="font-size:10px"></i><span>SERVEUR</span></div>
|
||||
<div class="f-cell"><i class="fa-solid fa-microchip" style="font-size:10px"></i><span class="f-val">18%</span><div class="f-minibar"><div class="f-minifill" style="width:18%"></div></div></div>
|
||||
<div class="f-cell"><i class="fa-solid fa-memory" style="font-size:10px"></i><span class="f-val w">71%</span><div class="f-minibar"><div class="f-minifill w" style="width:71%"></div></div></div>
|
||||
<div class="f-spacer"></div>
|
||||
<div class="f-right"><i class="fa-solid fa-rotate"></i><span>Actualisation : <span class="f-time">22:14:07</span></span></div>
|
||||
</div>
|
||||
|
||||
<!-- POPUP -->
|
||||
<div class="overlay" id="popup-overlay" style="display:flex" onclick="if(event.target===this)this.style.display='none'">
|
||||
<div class="popup" onclick="event.stopPropagation()">
|
||||
<div class="pop-head">
|
||||
<div class="agent-icon-wrap" onclick="document.getElementById('icon-upload').click()" data-tip="Changer l'icône de l'agent">
|
||||
<i class="fa-solid fa-server" id="agent-icon-fa"></i>
|
||||
<img id="agent-icon-img" src="" style="display:none">
|
||||
<div class="agent-icon-overlay"><i class="fa-solid fa-camera"></i><span>Changer</span></div>
|
||||
</div>
|
||||
<input type="file" id="icon-upload" accept=".svg,.jpg,.jpeg,.png,.webp" onchange="handleIconUpload(event)">
|
||||
<div class="pop-head-info">
|
||||
<div class="pop-host">srv-prod-01</div>
|
||||
<div class="pop-ip">10.0.0.11</div>
|
||||
<div class="upload-hint" id="upload-hint">Cliquer sur l'icône pour personnaliser · SVG JPG PNG WEBP · redim. max 128×128 px</div>
|
||||
</div>
|
||||
<div class="pop-led"></div>
|
||||
<div class="pop-close" onclick="document.getElementById('popup-overlay').style.display='none'" data-tip="Fermer">
|
||||
<i class="fa-solid fa-xmark"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pop-body">
|
||||
<div>
|
||||
<div class="sec-title">MÉTRIQUES ACTUELLES</div>
|
||||
<div class="kpi-grid">
|
||||
<div class="kpi"><div class="kpi-lbl">CPU</div><div class="kpi-val c-ok">42<span class="u">%</span></div><div class="kpi-sub">4 cœurs</div></div>
|
||||
<div class="kpi"><div class="kpi-lbl">MÉMOIRE</div><div class="kpi-val">3.7<span class="u">Go</span></div><div class="kpi-sub">/ 8 Go · 46%</div></div>
|
||||
<div class="kpi"><div class="kpi-lbl">DISQUE</div><div class="kpi-val">62<span class="u">Go</span></div><div class="kpi-sub">/ 200 Go · 31%</div></div>
|
||||
<div class="kpi"><div class="kpi-lbl">UPTIME</div><div class="kpi-val" style="font-size:15px;color:var(--ink-1)">14j 6h</div><div class="kpi-sub">depuis boot</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sec-title">HISTORIQUE — 30 DERNIÈRES MINUTES</div>
|
||||
<div class="charts-grid">
|
||||
<div class="chart-card">
|
||||
<div class="chart-header">
|
||||
<div class="chart-label" style="color:var(--accent)"><i class="fa-solid fa-microchip"></i>CPU</div>
|
||||
<span class="chart-cur c-ok">42%</span>
|
||||
</div>
|
||||
<svg class="chart-svg" viewBox="0 0 200 56" preserveAspectRatio="none" id="cpu-chart"></svg>
|
||||
<div class="chart-axis"><span>−30min</span><span>−15min</span><span>now</span></div>
|
||||
</div>
|
||||
<div class="chart-card">
|
||||
<div class="chart-header">
|
||||
<div class="chart-label" style="color:var(--blue)"><i class="fa-solid fa-memory"></i>RAM</div>
|
||||
<span class="chart-cur" style="color:var(--blue)">46%</span>
|
||||
</div>
|
||||
<svg class="chart-svg" viewBox="0 0 200 56" preserveAspectRatio="none" id="ram-chart"></svg>
|
||||
<div class="chart-axis"><span>−30min</span><span>−15min</span><span>now</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sec-title">STOCKAGE</div>
|
||||
<div class="det-gauges">
|
||||
<div class="dg-row">
|
||||
<div class="dg-ico" data-tip="Disque utilisé"><i class="fa-solid fa-hard-drive"></i></div>
|
||||
<div class="dg-bar"><div class="dg-fill" style="width:31%"></div></div>
|
||||
<span class="dg-val">62 / 200 Go</span>
|
||||
</div>
|
||||
<div class="dg-row">
|
||||
<div class="dg-ico" data-tip="Espace libre" style="color:var(--blue)"><i class="fa-solid fa-floppy-disk"></i></div>
|
||||
<div class="dg-bar"><div class="dg-fill" style="width:69%;background:var(--blue)"></div></div>
|
||||
<span class="dg-val">138 Go libre</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sec-title">INFORMATIONS</div>
|
||||
<div class="meta-grid">
|
||||
<div class="meta"><div class="meta-lbl">HOSTNAME</div><div class="meta-val">srv-prod-01</div></div>
|
||||
<div class="meta"><div class="meta-lbl">ADRESSE IP</div><div class="meta-val">10.0.0.11</div></div>
|
||||
<div class="meta"><div class="meta-lbl">DERNIER CONTACT</div><div class="meta-val">22:14:07</div></div>
|
||||
<div class="meta"><div class="meta-lbl">FRÉQUENCE</div><div class="meta-val">CPU 2s · Disk 60s</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pop-foot">
|
||||
<span class="pop-uptime"><i class="fa-solid fa-clock" style="margin-right:4px"></i>En ligne depuis 14 jours 6 heures</span>
|
||||
<button class="btn"><i class="fa-solid fa-chart-line"></i> Historique complet</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/* ══ TOOLTIP GLOBAL — fixé sur le document, jamais clippé ══ */
|
||||
const tip = document.getElementById('tooltip');
|
||||
let tipTimer;
|
||||
|
||||
document.addEventListener('mouseover', e => {
|
||||
const el = e.target.closest('[data-tip]');
|
||||
if (!el) return;
|
||||
clearTimeout(tipTimer);
|
||||
tipTimer = setTimeout(() => {
|
||||
tip.textContent = el.dataset.tip;
|
||||
tip.classList.add('show');
|
||||
moveTip(e);
|
||||
}, 120);
|
||||
});
|
||||
|
||||
document.addEventListener('mousemove', e => {
|
||||
if (tip.classList.contains('show')) moveTip(e);
|
||||
});
|
||||
|
||||
document.addEventListener('mouseout', e => {
|
||||
const el = e.target.closest('[data-tip]');
|
||||
if (!el) return;
|
||||
clearTimeout(tipTimer);
|
||||
tip.classList.remove('show');
|
||||
});
|
||||
|
||||
function moveTip(e) {
|
||||
const tw = tip.offsetWidth, th = tip.offsetHeight;
|
||||
let x = e.clientX - tw / 2;
|
||||
let y = e.clientY - th - 10;
|
||||
/* garder dans le viewport */
|
||||
x = Math.max(6, Math.min(x, window.innerWidth - tw - 6));
|
||||
if (y < 6) y = e.clientY + 18;
|
||||
tip.style.left = x + 'px';
|
||||
tip.style.top = y + 'px';
|
||||
}
|
||||
|
||||
/* ══ THÈME ══ */
|
||||
function toggleTheme() {
|
||||
const h = document.documentElement;
|
||||
h.dataset.theme = h.dataset.theme === 'dark' ? 'light' : 'dark';
|
||||
document.getElementById('theme-icon').className =
|
||||
h.dataset.theme === 'dark' ? 'fa-solid fa-moon' : 'fa-solid fa-sun';
|
||||
drawCharts();
|
||||
}
|
||||
|
||||
/* ══ UPLOAD ICÔNE ══ */
|
||||
function handleIconUpload(e) {
|
||||
const file = e.target.files[0];
|
||||
if (!file) return;
|
||||
const allowed = ['image/svg+xml','image/jpeg','image/png','image/webp'];
|
||||
if (!allowed.includes(file.type)) { alert('Format non supporté'); return; }
|
||||
const reader = new FileReader();
|
||||
reader.onload = ev => {
|
||||
if (file.type === 'image/svg+xml') {
|
||||
applyIcon(ev.target.result);
|
||||
} else {
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
const r = Math.min(128 / img.width, 128 / img.height, 1);
|
||||
canvas.width = Math.round(img.width * r);
|
||||
canvas.height = Math.round(img.height * r);
|
||||
canvas.getContext('2d').drawImage(img, 0, 0, canvas.width, canvas.height);
|
||||
applyIcon(canvas.toDataURL(file.type === 'image/png' ? 'image/png' : 'image/jpeg', 0.9));
|
||||
};
|
||||
img.src = ev.target.result;
|
||||
}
|
||||
const hint = document.getElementById('upload-hint');
|
||||
hint.textContent = '✓ Icône mise à jour — sauvegardée sur le serveur';
|
||||
hint.style.color = 'var(--ok)';
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
function applyIcon(src) {
|
||||
document.getElementById('agent-icon-fa').style.display = 'none';
|
||||
const img = document.getElementById('agent-icon-img');
|
||||
img.src = src; img.style.display = 'block';
|
||||
}
|
||||
|
||||
/* ══ COURBES ══ */
|
||||
function makeCurve(pts, stroke, fill, w, h) {
|
||||
const xs = pts.map((_, i) => (i / (pts.length - 1)) * w);
|
||||
const ys = pts.map(v => h - (v / 100) * (h - 6) - 3);
|
||||
const warnY = h - (70 / 100) * (h - 6) - 3;
|
||||
let d = `M${xs[0]} ${ys[0]}`;
|
||||
for (let i = 1; i < pts.length; i++) {
|
||||
const cx = (xs[i-1] + xs[i]) / 2;
|
||||
d += ` C${cx} ${ys[i-1]},${cx} ${ys[i]},${xs[i]} ${ys[i]}`;
|
||||
}
|
||||
const uid = Math.random().toString(36).slice(2);
|
||||
return `
|
||||
<defs>
|
||||
<linearGradient id="g${uid}" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="${fill}" stop-opacity=".4"/>
|
||||
<stop offset="100%" stop-color="${fill}" stop-opacity=".02"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<line x1="0" y1="${warnY}" x2="${w}" y2="${warnY}" stroke="var(--warn)" stroke-width=".8" stroke-dasharray="3,3" opacity=".5"/>
|
||||
<path d="${d} L${xs.at(-1)} ${h} L${xs[0]} ${h}Z" fill="url(#g${uid})"/>
|
||||
<path d="${d}" fill="none" stroke="${stroke}" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<circle cx="${xs.at(-1)}" cy="${ys.at(-1)}" r="2.5" fill="${stroke}"/>
|
||||
`;
|
||||
}
|
||||
|
||||
function drawCharts() {
|
||||
const cpu = [38,41,35,40,44,42,39,45,50,48,43,42,44,41,40,43,47,45,42,44,41,43,42,40,43,41,44,43,41,42];
|
||||
const ram = [44,44,45,45,46,46,47,47,46,46,45,46,46,47,47,46,46,45,46,46,46,46,46,46,46,46,46,46,46,46];
|
||||
const cs = getComputedStyle(document.documentElement);
|
||||
const accent = cs.getPropertyValue('--accent').trim();
|
||||
const blue = cs.getPropertyValue('--blue').trim();
|
||||
document.getElementById('cpu-chart').innerHTML = makeCurve(cpu, accent, accent, 200, 56);
|
||||
document.getElementById('ram-chart').innerHTML = makeCurve(ram, blue, blue, 200, 56);
|
||||
}
|
||||
|
||||
function showPopup() { document.getElementById('popup-overlay').style.display='flex'; }
|
||||
window.addEventListener('load', drawCharts);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,551 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Nanometrics — Layout v6 — Config agent</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Share+Tech+Mono:wght@400&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<script src="/helper.js"></script>
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing:border-box; margin:0; padding:0; }
|
||||
:root[data-theme="dark"] {
|
||||
--accent:#fe8019; --accent-soft:#d65d0e; --accent-glow:rgba(254,128,25,0.28);
|
||||
--bg-0:#1d1813; --bg-1:#2a231d; --bg-2:#32291f; --bg-3:#3c332a; --bg-4:#4a3f33; --bg-5:#57493c;
|
||||
--ink-1:#f2e5c7; --ink-2:#d5c4a1; --ink-3:#a89984; --ink-4:#7c6f64;
|
||||
--ok:#4dbb26; --warn:#fabd2f; --err:#fb4934; --info:#83a598; --blue:#3db0d1; --purple:#c882c8;
|
||||
--border-1:rgba(255,255,255,0.06); --border-2:rgba(255,255,255,0.12); --border-3:rgba(255,255,255,0.26);
|
||||
--tile-3d:0 1px 0 rgba(255,255,255,0.08) inset,0 -1px 0 rgba(0,0,0,0.3) inset,0 6px 20px rgba(0,0,0,0.5);
|
||||
--tile-press:inset 0 2px 8px rgba(0,0,0,0.5),inset 0 1px 3px rgba(0,0,0,0.4);
|
||||
--hover-glow:0 0 0 1px var(--accent-soft),0 0 24px var(--accent-glow),0 6px 20px rgba(0,0,0,0.5);
|
||||
--font-ui:'Inter',system-ui,sans-serif; --font-mono:'JetBrains Mono',monospace; --font-terminal:'Share Tech Mono',monospace;
|
||||
}
|
||||
:root[data-theme="light"] {
|
||||
--accent:#af3a03; --accent-soft:#d65d0e; --accent-glow:rgba(175,58,3,0.18);
|
||||
--bg-0:#d5c4a1; --bg-1:#ebdbb2; --bg-2:#d5c4a1; --bg-3:#bdae93; --bg-4:#a89984; --bg-5:#928374;
|
||||
--ink-1:#3c3836; --ink-2:#504945; --ink-3:#665c54; --ink-4:#7c6f64;
|
||||
--ok:#3c911c; --warn:#b57614; --err:#9d0006; --blue:#2d82a3; --purple:#8c468c;
|
||||
--border-1:rgba(0,0,0,0.08); --border-2:rgba(0,0,0,0.15); --border-3:rgba(0,0,0,0.3);
|
||||
--tile-3d:0 1px 0 rgba(255,255,255,0.55) inset,0 -1px 0 rgba(0,0,0,0.08) inset,0 4px 14px rgba(0,0,0,0.13);
|
||||
--tile-press:inset 0 2px 6px rgba(0,0,0,0.2);
|
||||
--hover-glow:0 0 0 1px var(--accent-soft),0 0 18px var(--accent-glow),0 4px 14px rgba(0,0,0,0.13);
|
||||
--font-ui:'Inter',system-ui,sans-serif; --font-mono:'JetBrains Mono',monospace; --font-terminal:'Share Tech Mono',monospace;
|
||||
}
|
||||
body { background:var(--bg-1); color:var(--ink-1); font-family:var(--font-ui); font-size:13px; height:100vh; display:flex; flex-direction:column; overflow:hidden; transition:background .2s,color .2s; }
|
||||
|
||||
/* TOOLTIP */
|
||||
#tooltip { position:fixed; z-index:9999; pointer-events:none; background:var(--bg-0); color:var(--ink-1); border:1px solid var(--border-3); border-radius:5px; padding:4px 9px; font-size:11px; font-family:var(--font-ui); white-space:nowrap; opacity:0; transition:opacity .12s; box-shadow:0 4px 12px rgba(0,0,0,.4); }
|
||||
#tooltip.show { opacity:1; }
|
||||
|
||||
/* HEADER */
|
||||
.header { background:var(--bg-2); border-bottom:1px solid var(--border-2); padding:0 20px; height:48px; display:flex; align-items:center; gap:12px; flex-shrink:0; }
|
||||
.logo { display:flex; align-items:center; gap:8px; }
|
||||
.logo-led { width:9px; height:9px; border-radius:50%; background:var(--accent); box-shadow:0 0 8px var(--accent-glow); animation:blink 2s infinite; }
|
||||
@keyframes blink{0%,100%{opacity:1}50%{opacity:.4}}
|
||||
.logo-name { font-weight:700; font-size:14px; letter-spacing:.05em; font-family:var(--font-terminal); }
|
||||
.logo-ver { font-size:10px; color:var(--ink-4); font-family:var(--font-terminal); }
|
||||
.h-sep { width:1px; height:24px; background:var(--border-2); }
|
||||
.h-spacer { flex:1; }
|
||||
.h-stats { display:flex; gap:14px; }
|
||||
.h-stat { display:flex; align-items:center; gap:5px; }
|
||||
.h-stat .lbl { font-size:9px; color:var(--ink-4); font-family:var(--font-terminal); letter-spacing:.06em; }
|
||||
.h-stat .val { font-family:var(--font-mono); font-weight:700; font-size:13px; }
|
||||
.c-ok{color:var(--ok)}.c-warn{color:var(--warn)}.c-err{color:var(--err)}.c-n{color:var(--ink-2)}
|
||||
.hbtn { width:34px; height:34px; border-radius:8px; border:1px solid var(--border-2); background:var(--bg-3); color:var(--ink-2); font-size:14px; display:flex; align-items:center; justify-content:center; cursor:pointer; user-select:none; transition:background .12s,color .12s,transform .08s,box-shadow .08s; }
|
||||
.hbtn:hover { background:var(--bg-4); color:var(--accent); }
|
||||
.hbtn:active { transform:translateY(1px) scale(.96); box-shadow:var(--tile-press); }
|
||||
|
||||
/* GRID */
|
||||
.main { flex:1; padding:14px 16px; overflow-y:auto; }
|
||||
.agents-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(220px,1fr)); gap:10px; }
|
||||
.tile { background:var(--bg-3); border-radius:10px; padding:12px 14px; border:1px solid var(--border-1); box-shadow:var(--tile-3d); cursor:pointer; user-select:none; display:flex; flex-direction:column; gap:9px; transition:box-shadow .15s,transform .08s,border-color .15s; }
|
||||
.tile:hover { box-shadow:var(--hover-glow); border-color:var(--accent-soft); }
|
||||
.tile:active { transform:translateY(2px) scale(.99); box-shadow:var(--tile-press); }
|
||||
.tile.t-warn { border-color:rgba(250,189,47,.3); }
|
||||
.tile.t-warn:hover { border-color:var(--warn); box-shadow:0 0 0 1px var(--warn),0 0 22px rgba(250,189,47,.22),0 6px 20px rgba(0,0,0,.5); }
|
||||
.tile.t-err { border-color:rgba(251,73,52,.35); }
|
||||
.tile.t-err:hover { border-color:var(--err); box-shadow:0 0 0 1px var(--err),0 0 22px rgba(251,73,52,.25),0 6px 20px rgba(0,0,0,.5); }
|
||||
.tile.t-off { opacity:.5; cursor:default; }
|
||||
.tile.t-off:hover,.tile.t-off:active { box-shadow:var(--tile-3d); border-color:var(--border-1); transform:none; }
|
||||
.tile-head { display:flex; align-items:center; gap:8px; }
|
||||
.t-icon { width:28px; height:28px; border-radius:7px; background:var(--bg-4); display:flex; align-items:center; justify-content:center; color:var(--accent); font-size:13px; flex-shrink:0; overflow:hidden; }
|
||||
.t-names { flex:1; min-width:0; }
|
||||
.t-host { font-weight:600; font-size:13px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
||||
.t-ip { font-family:var(--font-mono); font-size:10px; color:var(--ink-4); }
|
||||
.t-led { width:8px; height:8px; border-radius:50%; flex-shrink:0; }
|
||||
.s-ok{background:var(--ok);box-shadow:0 0 6px var(--ok)}.s-warn{background:var(--warn);box-shadow:0 0 6px var(--warn);animation:blink 1.5s infinite}.s-err{background:var(--err);box-shadow:0 0 8px var(--err);animation:blink 1s infinite}.s-off{background:var(--ink-4)}
|
||||
.tile-gauges { display:flex; flex-direction:column; gap:5px; }
|
||||
.g-row { display:flex; align-items:center; gap:7px; }
|
||||
.g-ico { width:18px; height:18px; display:flex; align-items:center; justify-content:center; font-size:11px; color:var(--ink-3); flex-shrink:0; cursor:help; }
|
||||
.g-bar { flex:1; height:5px; border-radius:3px; background:var(--bg-1); overflow:hidden; }
|
||||
.g-fill { height:100%; border-radius:3px; background:var(--ok); }
|
||||
.g-fill.w{background:var(--warn)}.g-fill.e{background:var(--err)}
|
||||
.g-val { font-family:var(--font-mono); font-size:11px; color:var(--ink-2); width:34px; text-align:right; }
|
||||
.tile-foot { font-family:var(--font-terminal); font-size:10px; color:var(--ink-4); display:flex; align-items:center; gap:5px; }
|
||||
.tile-foot i { font-size:9px; }
|
||||
|
||||
/* FOOTER */
|
||||
.footer { background:var(--bg-0); border-top:1px solid var(--border-2); height:26px; display:flex; align-items:center; font-family:var(--font-terminal); font-size:11px; color:var(--ink-4); flex-shrink:0; }
|
||||
.f-mode { background:var(--accent); color:var(--bg-0); padding:0 12px; height:100%; display:flex; align-items:center; font-weight:700; letter-spacing:.04em; }
|
||||
.f-cell { padding:0 12px; border-right:1px solid var(--border-1); display:flex; align-items:center; gap:5px; height:100%; }
|
||||
.f-val { font-family:var(--font-mono); color:var(--ink-2); }
|
||||
.f-val.w { color:var(--warn); }
|
||||
.f-minibar { width:36px; height:4px; border-radius:2px; background:var(--bg-3); overflow:hidden; }
|
||||
.f-minifill { height:100%; border-radius:2px; background:var(--ok); }
|
||||
.f-minifill.w { background:var(--warn); }
|
||||
.f-spacer { flex:1; }
|
||||
.f-right { padding:0 12px; display:flex; align-items:center; gap:6px; color:var(--ink-3); }
|
||||
.f-time { font-family:var(--font-mono); color:var(--ink-2); }
|
||||
|
||||
/* ══ OVERLAY / POPUP BASE ══ */
|
||||
.overlay { position:fixed; inset:0; background:rgba(0,0,0,.65); z-index:100; display:flex; align-items:center; justify-content:center; backdrop-filter:blur(2px); }
|
||||
.popup { background:var(--bg-2); border:1px solid var(--border-3); border-radius:12px; box-shadow:0 24px 64px rgba(0,0,0,.7); display:flex; flex-direction:column; overflow:hidden; }
|
||||
.pop-close { width:28px; height:28px; border-radius:6px; background:var(--bg-5); color:var(--ink-3); display:flex; align-items:center; justify-content:center; cursor:pointer; font-size:12px; border:1px solid var(--border-1); transition:background .12s,color .12s,transform .08s; user-select:none; }
|
||||
.pop-close:hover { background:var(--err); color:#fff; }
|
||||
.pop-close:active { transform:translateY(1px) scale(.93); box-shadow:var(--tile-press); }
|
||||
.sec-title { font-size:9px; color:var(--ink-4); font-family:var(--font-terminal); letter-spacing:.08em; margin-bottom:8px; }
|
||||
.btn { padding:6px 14px; border-radius:8px; border:1px solid var(--border-2); background:var(--bg-4); color:var(--ink-2); font-size:12px; font-family:var(--font-ui); cursor:pointer; display:flex; align-items:center; gap:6px; user-select:none; transition:background .1s,transform .08s,box-shadow .08s; }
|
||||
.btn:hover { background:var(--bg-5); }
|
||||
.btn:active { transform:translateY(1px) scale(.97); box-shadow:var(--tile-press); }
|
||||
.btn.primary { background:var(--accent); color:var(--bg-0); border-color:var(--accent-soft); font-weight:600; }
|
||||
.btn.primary:hover { background:var(--accent-soft); }
|
||||
.btn.danger { background:rgba(251,73,52,.15); color:var(--err); border-color:rgba(251,73,52,.3); }
|
||||
.btn.danger:hover { background:rgba(251,73,52,.25); }
|
||||
|
||||
/* ══ POPUP DÉTAIL AGENT ══ */
|
||||
#popup-detail { width:540px; max-width:96vw; max-height:92vh; }
|
||||
.pop-head { background:var(--bg-3); padding:14px 18px; border-bottom:1px solid var(--border-2); display:flex; align-items:center; gap:12px; }
|
||||
.agent-icon-wrap { position:relative; width:44px; height:44px; border-radius:10px; flex-shrink:0; cursor:pointer; overflow:hidden; background:var(--bg-4); display:flex; align-items:center; justify-content:center; color:var(--accent); font-size:18px; border:2px solid var(--border-2); transition:border-color .15s; }
|
||||
.agent-icon-wrap:hover { border-color:var(--accent); }
|
||||
.agent-icon-overlay { position:absolute; inset:0; background:rgba(0,0,0,.6); display:flex; flex-direction:column; align-items:center; justify-content:center; gap:2px; opacity:0; transition:opacity .15s; font-size:10px; color:#fff; }
|
||||
.agent-icon-overlay i { font-size:14px; }
|
||||
.agent-icon-wrap:hover .agent-icon-overlay { opacity:1; }
|
||||
#icon-upload { display:none; }
|
||||
.pop-head-info { flex:1; }
|
||||
.pop-host { font-weight:700; font-size:15px; }
|
||||
.pop-ip { font-family:var(--font-mono); font-size:11px; color:var(--ink-4); }
|
||||
.upload-hint { font-size:10px; color:var(--ink-4); font-family:var(--font-terminal); margin-top:2px; }
|
||||
.pop-led { width:10px; height:10px; border-radius:50%; background:var(--ok); box-shadow:0 0 8px var(--ok); flex-shrink:0; }
|
||||
.pop-body { padding:16px 18px; display:flex; flex-direction:column; gap:14px; overflow-y:auto; }
|
||||
.kpi-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:7px; }
|
||||
.kpi { background:var(--bg-3); border-radius:8px; padding:10px 12px; border:1px solid var(--border-1); box-shadow:var(--tile-3d); }
|
||||
.kpi-lbl { font-size:9px; color:var(--ink-4); font-family:var(--font-terminal); letter-spacing:.06em; }
|
||||
.kpi-val { font-family:var(--font-mono); font-size:20px; font-weight:700; line-height:1.1; margin-top:2px; }
|
||||
.kpi-val .u { font-size:10px; color:var(--ink-3); font-weight:400; }
|
||||
.kpi-sub { font-size:10px; color:var(--ink-4); font-family:var(--font-mono); margin-top:2px; }
|
||||
.charts-grid { display:grid; grid-template-columns:1fr 1fr; gap:10px; }
|
||||
.chart-card { background:var(--bg-3); border-radius:8px; padding:10px 12px; border:1px solid var(--border-1); box-shadow:var(--tile-3d); }
|
||||
.chart-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:8px; }
|
||||
.chart-label { display:flex; align-items:center; gap:6px; font-size:10px; font-family:var(--font-terminal); letter-spacing:.06em; color:var(--ink-3); }
|
||||
.chart-cur { font-family:var(--font-mono); font-size:16px; font-weight:700; }
|
||||
.chart-svg { width:100%; height:52px; display:block; }
|
||||
.chart-axis { display:flex; justify-content:space-between; margin-top:2px; font-family:var(--font-terminal); font-size:9px; color:var(--ink-4); }
|
||||
.det-gauges { display:flex; flex-direction:column; gap:7px; }
|
||||
.dg-row { display:flex; align-items:center; gap:10px; }
|
||||
.dg-ico { width:22px; text-align:center; font-size:13px; cursor:help; }
|
||||
.dg-bar { flex:1; height:7px; border-radius:4px; background:var(--bg-1); overflow:hidden; }
|
||||
.dg-fill { height:100%; border-radius:4px; background:var(--ok); }
|
||||
.dg-fill.b { background:var(--blue); }
|
||||
.dg-val { font-family:var(--font-mono); font-size:12px; color:var(--ink-2); width:90px; text-align:right; }
|
||||
.meta-grid { display:grid; grid-template-columns:1fr 1fr; gap:6px; }
|
||||
.meta { background:var(--bg-3); border-radius:6px; padding:8px 10px; border:1px solid var(--border-1); }
|
||||
.meta-lbl { font-size:9px; color:var(--ink-4); font-family:var(--font-terminal); letter-spacing:.06em; }
|
||||
.meta-val { font-family:var(--font-mono); font-size:12px; color:var(--ink-2); margin-top:2px; }
|
||||
|
||||
/* badge protocoles dans meta */
|
||||
.proto-badges { display:flex; gap:5px; margin-top:4px; flex-wrap:wrap; }
|
||||
.proto-badge { display:inline-flex; align-items:center; gap:4px; padding:2px 7px; border-radius:999px; font-size:10px; font-family:var(--font-terminal); font-weight:600; }
|
||||
.proto-badge.udp { background:rgba(61,176,209,.15); color:var(--blue); border:1px solid rgba(61,176,209,.3); }
|
||||
.proto-badge.mqtt { background:rgba(200,130,200,.15); color:var(--purple); border:1px solid rgba(200,130,200,.3); }
|
||||
|
||||
/* footer popup détail — bouton config agent en bas à droite */
|
||||
.pop-foot { padding:10px 18px; border-top:1px solid var(--border-2); background:var(--bg-3); display:flex; align-items:center; gap:8px; }
|
||||
.pop-uptime { font-family:var(--font-terminal); font-size:11px; color:var(--ink-4); flex:1; }
|
||||
/* bouton config agent — icon service */
|
||||
.btn-agent-cfg {
|
||||
width:34px; height:34px; border-radius:8px; border:1px solid var(--border-2);
|
||||
background:var(--bg-4); color:var(--ink-3); font-size:15px;
|
||||
display:flex; align-items:center; justify-content:center;
|
||||
cursor:pointer; user-select:none;
|
||||
transition:background .12s,color .12s,transform .08s,box-shadow .08s;
|
||||
}
|
||||
.btn-agent-cfg:hover { background:var(--bg-5); color:var(--accent); }
|
||||
.btn-agent-cfg:active { transform:translateY(1px) scale(.93); box-shadow:var(--tile-press); }
|
||||
|
||||
/* ══ POPUP CONFIG AGENT ══ */
|
||||
#popup-agentcfg { width:480px; max-width:96vw; max-height:90vh; z-index:200; }
|
||||
.cfg-head { background:var(--bg-3); padding:14px 18px; border-bottom:1px solid var(--border-2); display:flex; align-items:center; gap:10px; }
|
||||
.cfg-head-icon { width:32px; height:32px; border-radius:8px; background:var(--bg-4); display:flex; align-items:center; justify-content:center; color:var(--accent); font-size:15px; }
|
||||
.cfg-head-info { flex:1; }
|
||||
.cfg-head-title { font-weight:700; font-size:14px; }
|
||||
.cfg-head-sub { font-size:11px; color:var(--ink-4); font-family:var(--font-terminal); }
|
||||
.cfg-body { padding:18px; display:flex; flex-direction:column; gap:18px; overflow-y:auto; max-height:60vh; }
|
||||
.cfg-section { display:flex; flex-direction:column; gap:10px; }
|
||||
.cfg-sec-title { font-size:9px; color:var(--ink-4); font-family:var(--font-terminal); letter-spacing:.08em; padding-bottom:6px; border-bottom:1px solid var(--border-1); }
|
||||
|
||||
/* checkboxes custom */
|
||||
.check-row { display:flex; align-items:center; gap:10px; padding:8px 10px; border-radius:8px; background:var(--bg-3); border:1px solid var(--border-1); cursor:pointer; transition:background .12s,border-color .12s; }
|
||||
.check-row:hover { background:var(--bg-4); border-color:var(--border-2); }
|
||||
.check-row.active { border-color:var(--accent-soft); background:rgba(254,128,25,.08); }
|
||||
.check-row.mqtt-active { border-color:rgba(200,130,200,.4); background:rgba(200,130,200,.07); }
|
||||
.chk-box { width:18px; height:18px; border-radius:4px; border:2px solid var(--border-3); background:var(--bg-1); display:flex; align-items:center; justify-content:center; font-size:11px; color:var(--accent); flex-shrink:0; transition:background .12s,border-color .12s; }
|
||||
.check-row.active .chk-box { background:var(--accent); border-color:var(--accent); color:var(--bg-0); }
|
||||
.check-row.mqtt-active .chk-box { background:var(--purple); border-color:var(--purple); color:var(--bg-0); }
|
||||
.chk-label { flex:1; }
|
||||
.chk-name { font-weight:600; font-size:13px; }
|
||||
.chk-desc { font-size:11px; color:var(--ink-4); font-family:var(--font-terminal); margin-top:1px; }
|
||||
.chk-badge { font-size:10px; font-family:var(--font-terminal); font-weight:700; padding:1px 7px; border-radius:999px; }
|
||||
.chk-badge.udp { background:rgba(61,176,209,.15); color:var(--blue); }
|
||||
.chk-badge.mqtt { background:rgba(200,130,200,.15); color:var(--purple); }
|
||||
|
||||
/* MQTT sub-options */
|
||||
.mqtt-opts { background:var(--bg-3); border-radius:8px; border:1px solid rgba(200,130,200,.2); padding:12px 14px; display:flex; flex-direction:column; gap:10px; }
|
||||
.mqtt-field { display:flex; align-items:center; gap:10px; }
|
||||
.mqtt-field label { font-size:11px; color:var(--ink-3); font-family:var(--font-terminal); width:90px; flex-shrink:0; }
|
||||
.mqtt-input { flex:1; background:var(--bg-1); border:1px solid var(--border-2); border-radius:6px; color:var(--ink-1); padding:6px 10px; font-size:12px; font-family:var(--font-mono); }
|
||||
.mqtt-input:focus { outline:none; border-color:var(--purple); }
|
||||
.mqtt-check-row { display:flex; align-items:center; justify-content:space-between; padding:4px 0; }
|
||||
.mqtt-check-row label { font-size:12px; color:var(--ink-2); display:flex; align-items:center; gap:7px; cursor:pointer; }
|
||||
.mqtt-check-row label i { color:var(--purple); font-size:11px; }
|
||||
/* toggle switch */
|
||||
.toggle { position:relative; width:34px; height:18px; flex-shrink:0; }
|
||||
.toggle input { opacity:0; width:0; height:0; }
|
||||
.toggle-slider { position:absolute; inset:0; border-radius:9px; background:var(--bg-4); border:1px solid var(--border-2); cursor:pointer; transition:background .2s; }
|
||||
.toggle-slider::before { content:''; position:absolute; width:12px; height:12px; border-radius:50%; background:var(--ink-4); top:2px; left:2px; transition:transform .2s, background .2s; }
|
||||
.toggle input:checked + .toggle-slider { background:rgba(200,130,200,.3); border-color:var(--purple); }
|
||||
.toggle input:checked + .toggle-slider::before { transform:translateX(16px); background:var(--purple); }
|
||||
|
||||
/* Métriques toggles */
|
||||
.metrics-grid { display:grid; grid-template-columns:1fr 1fr; gap:6px; }
|
||||
.metric-row { display:flex; align-items:center; gap:8px; padding:7px 10px; border-radius:7px; background:var(--bg-3); border:1px solid var(--border-1); }
|
||||
.metric-ico { width:22px; text-align:center; font-size:12px; color:var(--ink-3); }
|
||||
.metric-name { flex:1; font-size:12px; color:var(--ink-2); font-family:var(--font-terminal); }
|
||||
.tog-ok { }
|
||||
.tog-ok input:checked + .toggle-slider { background:rgba(77,187,38,.3); border-color:var(--ok); }
|
||||
.tog-ok input:checked + .toggle-slider::before { background:var(--ok); }
|
||||
|
||||
/* Commandes futures */
|
||||
.cmds-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:6px; }
|
||||
.cmd-btn { display:flex; flex-direction:column; align-items:center; gap:4px; padding:10px 8px; border-radius:8px; background:var(--bg-3); border:1px solid var(--border-1); cursor:not-allowed; opacity:.45; }
|
||||
.cmd-btn i { font-size:16px; color:var(--ink-3); }
|
||||
.cmd-btn span { font-size:10px; color:var(--ink-4); font-family:var(--font-terminal); }
|
||||
.cmd-btn.enabled { cursor:pointer; opacity:1; }
|
||||
.cmd-btn.enabled:hover { background:var(--bg-4); border-color:var(--border-2); }
|
||||
.cmd-btn.enabled:hover i { color:var(--accent); }
|
||||
.cmd-btn.danger-cmd { }
|
||||
.cmd-btn.danger-cmd.enabled:hover { background:rgba(251,73,52,.12); border-color:rgba(251,73,52,.3); }
|
||||
.cmd-btn.danger-cmd.enabled:hover i { color:var(--err); }
|
||||
.soon-tag { font-size:8px; color:var(--ink-4); font-family:var(--font-terminal); letter-spacing:.04em; }
|
||||
|
||||
/* cfg footer */
|
||||
.cfg-foot { padding:12px 18px; border-top:1px solid var(--border-2); background:var(--bg-3); display:flex; align-items:center; gap:8px; }
|
||||
.cfg-status { flex:1; display:flex; align-items:center; gap:6px; font-family:var(--font-terminal); font-size:11px; color:var(--ink-4); }
|
||||
.cfg-status .dot { width:6px; height:6px; border-radius:50%; background:var(--ok); }
|
||||
|
||||
::-webkit-scrollbar{width:5px}::-webkit-scrollbar-track{background:var(--bg-1)}::-webkit-scrollbar-thumb{background:var(--bg-4);border-radius:3px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="tooltip"></div>
|
||||
|
||||
<!-- HEADER -->
|
||||
<div class="header">
|
||||
<div class="logo"><div class="logo-led"></div><span class="logo-name">NANOMETRICS</span><span class="logo-ver">v1.0</span></div>
|
||||
<div class="h-sep"></div>
|
||||
<div class="h-stats">
|
||||
<div class="h-stat"><span class="lbl">AGENTS</span><span class="val c-n">8</span></div>
|
||||
<div class="h-stat"><span class="lbl">OK</span><span class="val c-ok">5</span></div>
|
||||
<div class="h-stat"><span class="lbl">WARN</span><span class="val c-warn">2</span></div>
|
||||
<div class="h-stat"><span class="lbl">ERR</span><span class="val c-err">1</span></div>
|
||||
</div>
|
||||
<div class="h-spacer"></div>
|
||||
<div class="hbtn" onclick="toggleTheme()" data-tip="Thème clair / sombre"><i class="fa-solid fa-moon" id="theme-icon"></i></div>
|
||||
<div class="hbtn" data-tip="Configuration interface"><i class="fa-solid fa-sliders"></i></div>
|
||||
</div>
|
||||
|
||||
<!-- GRID -->
|
||||
<div class="main">
|
||||
<div class="agents-grid">
|
||||
<div class="tile" onclick="showDetail()">
|
||||
<div class="tile-head"><div class="t-icon"><i class="fa-solid fa-server"></i></div><div class="t-names"><div class="t-host">srv-prod-01</div><div class="t-ip">10.0.0.11</div></div><div class="t-led s-ok"></div></div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row"><div class="g-ico" data-tip="Processeur (CPU)"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill" style="width:42%"></div></div><span class="g-val">42%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="Mémoire RAM"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill" style="width:58%"></div></div><span class="g-val">58%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="Espace disque"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill" style="width:31%"></div></div><span class="g-val">31%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>14j 6h</div>
|
||||
</div>
|
||||
<div class="tile t-warn">
|
||||
<div class="tile-head"><div class="t-icon"><i class="fa-solid fa-server"></i></div><div class="t-names"><div class="t-host">srv-backup-02</div><div class="t-ip">10.0.0.12</div></div><div class="t-led s-warn"></div></div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row"><div class="g-ico" data-tip="CPU — élevé" style="color:var(--warn)"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill w" style="width:78%"></div></div><span class="g-val" style="color:var(--warn)">78%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="RAM — élevée" style="color:var(--warn)"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill w" style="width:72%"></div></div><span class="g-val" style="color:var(--warn)">72%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="Espace disque"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill" style="width:20%"></div></div><span class="g-val">20%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>3j 14h</div>
|
||||
</div>
|
||||
<div class="tile t-err">
|
||||
<div class="tile-head"><div class="t-icon" style="color:var(--err)"><i class="fa-solid fa-microchip"></i></div><div class="t-names"><div class="t-host">rpi-sensor-03</div><div class="t-ip">10.0.0.23</div></div><div class="t-led s-err"></div></div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row"><div class="g-ico" data-tip="CPU"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill" style="width:12%"></div></div><span class="g-val">12%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="RAM"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill" style="width:38%"></div></div><span class="g-val">38%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="Disque critique !" style="color:var(--err)"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill e" style="width:94%"></div></div><span class="g-val" style="color:var(--err)">94%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>62j 1h</div>
|
||||
</div>
|
||||
<div class="tile"><div class="tile-head"><div class="t-icon"><i class="fa-solid fa-hard-drive"></i></div><div class="t-names"><div class="t-host">nas-storage-04</div><div class="t-ip">10.0.0.30</div></div><div class="t-led s-ok"></div></div><div class="tile-gauges"><div class="g-row"><div class="g-ico" data-tip="CPU"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill" style="width:8%"></div></div><span class="g-val">8%</span></div><div class="g-row"><div class="g-ico" data-tip="RAM"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill" style="width:45%"></div></div><span class="g-val">45%</span></div><div class="g-row"><div class="g-ico" data-tip="Disque"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill" style="width:66%"></div></div><span class="g-val">66%</span></div></div><div class="tile-foot"><i class="fa-solid fa-clock"></i>128j 3h</div></div>
|
||||
<div class="tile"><div class="tile-head"><div class="t-icon"><i class="fa-solid fa-database"></i></div><div class="t-names"><div class="t-host">db-primary-05</div><div class="t-ip">10.0.0.40</div></div><div class="t-led s-ok"></div></div><div class="tile-gauges"><div class="g-row"><div class="g-ico" data-tip="CPU"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill" style="width:25%"></div></div><span class="g-val">25%</span></div><div class="g-row"><div class="g-ico" data-tip="RAM"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill" style="width:61%"></div></div><span class="g-val">61%</span></div><div class="g-row"><div class="g-ico" data-tip="Disque"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill" style="width:48%"></div></div><span class="g-val">48%</span></div></div><div class="tile-foot"><i class="fa-solid fa-clock"></i>7j 22h</div></div>
|
||||
<div class="tile t-off"><div class="tile-head"><div class="t-icon" style="color:var(--ink-4)"><i class="fa-solid fa-server"></i></div><div class="t-names"><div class="t-host">srv-dev-07</div><div class="t-ip">10.0.0.60</div></div><div class="t-led s-off"></div></div><div class="tile-gauges"><div class="g-row"><div class="g-ico"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"></div><span class="g-val" style="color:var(--ink-4)">—</span></div><div class="g-row"><div class="g-ico"><i class="fa-solid fa-memory"></i></div><div class="g-bar"></div><span class="g-val" style="color:var(--ink-4)">—</span></div><div class="g-row"><div class="g-ico"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"></div><span class="g-val" style="color:var(--ink-4)">—</span></div></div><div class="tile-foot" style="color:var(--err)"><i class="fa-solid fa-circle-xmark"></i>Hors ligne · vu il y a 2h</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<div class="footer">
|
||||
<div class="f-mode">LIVE</div>
|
||||
<div class="f-cell"><i class="fa-solid fa-server" style="font-size:10px"></i><span>SERVEUR</span></div>
|
||||
<div class="f-cell"><i class="fa-solid fa-microchip" style="font-size:10px"></i><span class="f-val">18%</span><div class="f-minibar"><div class="f-minifill" style="width:18%"></div></div></div>
|
||||
<div class="f-cell"><i class="fa-solid fa-memory" style="font-size:10px"></i><span class="f-val w">71%</span><div class="f-minibar"><div class="f-minifill w" style="width:71%"></div></div></div>
|
||||
<div class="f-spacer"></div>
|
||||
<div class="f-right"><i class="fa-solid fa-rotate"></i><span>Actualisation : <span class="f-time">22:14:07</span></span></div>
|
||||
</div>
|
||||
|
||||
<!-- ══ POPUP DÉTAIL AGENT ══ -->
|
||||
<div class="overlay" id="overlay-detail" style="display:flex" onclick="if(event.target===this)this.style.display='none'">
|
||||
<div class="popup" id="popup-detail" onclick="event.stopPropagation()">
|
||||
<div class="pop-head">
|
||||
<div class="agent-icon-wrap" onclick="document.getElementById('icon-upload').click()" data-tip="Changer l'icône">
|
||||
<i class="fa-solid fa-server"></i>
|
||||
<div class="agent-icon-overlay"><i class="fa-solid fa-camera"></i><span>Changer</span></div>
|
||||
</div>
|
||||
<input type="file" id="icon-upload" accept=".svg,.jpg,.jpeg,.png,.webp">
|
||||
<div class="pop-head-info">
|
||||
<div class="pop-host">srv-prod-01</div>
|
||||
<div class="pop-ip">10.0.0.11</div>
|
||||
<div class="upload-hint">Cliquer sur l'icône pour personnaliser · SVG JPG PNG WEBP · max 128×128 px</div>
|
||||
</div>
|
||||
<div class="pop-led"></div>
|
||||
<div class="pop-close" onclick="document.getElementById('overlay-detail').style.display='none'" data-tip="Fermer"><i class="fa-solid fa-xmark"></i></div>
|
||||
</div>
|
||||
|
||||
<div class="pop-body">
|
||||
<div>
|
||||
<div class="sec-title">MÉTRIQUES ACTUELLES</div>
|
||||
<div class="kpi-grid">
|
||||
<div class="kpi"><div class="kpi-lbl">CPU</div><div class="kpi-val c-ok">42<span class="u">%</span></div><div class="kpi-sub">4 cœurs</div></div>
|
||||
<div class="kpi"><div class="kpi-lbl">MÉMOIRE</div><div class="kpi-val">3.7<span class="u">Go</span></div><div class="kpi-sub">/ 8 Go · 46%</div></div>
|
||||
<div class="kpi"><div class="kpi-lbl">DISQUE</div><div class="kpi-val">62<span class="u">Go</span></div><div class="kpi-sub">/ 200 Go · 31%</div></div>
|
||||
<div class="kpi"><div class="kpi-lbl">UPTIME</div><div class="kpi-val" style="font-size:15px;color:var(--ink-1)">14j 6h</div><div class="kpi-sub">depuis boot</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sec-title">HISTORIQUE — 30 DERNIÈRES MINUTES</div>
|
||||
<div class="charts-grid">
|
||||
<div class="chart-card">
|
||||
<div class="chart-header"><div class="chart-label" style="color:var(--accent)"><i class="fa-solid fa-microchip"></i>CPU</div><span class="chart-cur c-ok">42%</span></div>
|
||||
<svg class="chart-svg" viewBox="0 0 200 52" preserveAspectRatio="none" id="cpu-chart"></svg>
|
||||
<div class="chart-axis"><span>−30min</span><span>−15min</span><span>now</span></div>
|
||||
</div>
|
||||
<div class="chart-card">
|
||||
<div class="chart-header"><div class="chart-label" style="color:var(--blue)"><i class="fa-solid fa-memory"></i>RAM</div><span class="chart-cur" style="color:var(--blue)">46%</span></div>
|
||||
<svg class="chart-svg" viewBox="0 0 200 52" preserveAspectRatio="none" id="ram-chart"></svg>
|
||||
<div class="chart-axis"><span>−30min</span><span>−15min</span><span>now</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sec-title">STOCKAGE</div>
|
||||
<div class="det-gauges">
|
||||
<div class="dg-row"><div class="dg-ico" data-tip="Utilisé"><i class="fa-solid fa-hard-drive"></i></div><div class="dg-bar"><div class="dg-fill" style="width:31%"></div></div><span class="dg-val">62 / 200 Go</span></div>
|
||||
<div class="dg-row"><div class="dg-ico" data-tip="Libre" style="color:var(--blue)"><i class="fa-solid fa-floppy-disk"></i></div><div class="dg-bar"><div class="dg-fill b" style="width:69%"></div></div><span class="dg-val">138 Go libre</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sec-title">INFORMATIONS</div>
|
||||
<div class="meta-grid">
|
||||
<div class="meta"><div class="meta-lbl">HOSTNAME</div><div class="meta-val">srv-prod-01</div></div>
|
||||
<div class="meta"><div class="meta-lbl">ADRESSE IP</div><div class="meta-val">10.0.0.11</div></div>
|
||||
<div class="meta"><div class="meta-lbl">PROTOCOLES ACTIFS</div>
|
||||
<div class="proto-badges">
|
||||
<span class="proto-badge udp"><i class="fa-solid fa-arrow-up"></i>UDP</span>
|
||||
<span class="proto-badge mqtt"><i class="fa-brands fa-mqtt" style="font-family:var(--font-terminal)">M</i>MQTT</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="meta"><div class="meta-lbl">DERNIER CONTACT</div><div class="meta-val">22:14:07</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- footer — bouton config agent en bas à droite -->
|
||||
<div class="pop-foot">
|
||||
<span class="pop-uptime"><i class="fa-solid fa-clock" style="margin-right:4px"></i>En ligne depuis 14 jours 6 heures</span>
|
||||
<button class="btn"><i class="fa-solid fa-chart-line"></i> Historique</button>
|
||||
<div class="btn-agent-cfg" onclick="showAgentCfg()" data-tip="Configurer l'agent">
|
||||
<i class="fa-solid fa-gears"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══ POPUP CONFIG AGENT ══ -->
|
||||
<div class="overlay" id="overlay-agentcfg" style="display:none;z-index:200" onclick="if(event.target===this)this.style.display='none'">
|
||||
<div class="popup" id="popup-agentcfg" onclick="event.stopPropagation()">
|
||||
|
||||
<div class="cfg-head">
|
||||
<div class="cfg-head-icon"><i class="fa-solid fa-gears"></i></div>
|
||||
<div class="cfg-head-info">
|
||||
<div class="cfg-head-title">Configuration de l'agent</div>
|
||||
<div class="cfg-head-sub">srv-prod-01 · 10.0.0.11 · config récupérée à 22:14:05</div>
|
||||
</div>
|
||||
<div class="pop-close" onclick="document.getElementById('overlay-agentcfg').style.display='none'" data-tip="Fermer"><i class="fa-solid fa-xmark"></i></div>
|
||||
</div>
|
||||
|
||||
<div class="cfg-body">
|
||||
|
||||
<!-- PROTOCOLES -->
|
||||
<div class="cfg-section">
|
||||
<div class="cfg-sec-title">PROTOCOLES DE TRANSPORT</div>
|
||||
|
||||
<!-- UDP -->
|
||||
<div class="check-row active" id="row-udp" onclick="toggleProto('udp')">
|
||||
<div class="chk-box" id="chk-udp"><i class="fa-solid fa-check"></i></div>
|
||||
<div class="chk-label">
|
||||
<div class="chk-name">UDP <span class="chk-badge udp">UDP</span></div>
|
||||
<div class="chk-desc">Fire-and-forget · serveur 10.0.0.50 · port 9999</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MQTT -->
|
||||
<div class="check-row mqtt-active" id="row-mqtt" onclick="toggleProto('mqtt')">
|
||||
<div class="chk-box" id="chk-mqtt" style="background:var(--purple);border-color:var(--purple);color:var(--bg-0)"><i class="fa-solid fa-check"></i></div>
|
||||
<div class="chk-label">
|
||||
<div class="chk-name">MQTT <span class="chk-badge mqtt">MQTT</span></div>
|
||||
<div class="chk-desc">Bidirectionnel · broker 10.0.0.3 · port 1883</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MQTT sub-options -->
|
||||
<div class="mqtt-opts" id="mqtt-opts">
|
||||
<div class="mqtt-field">
|
||||
<label>Broker</label>
|
||||
<input class="mqtt-input" type="text" value="10.0.0.3">
|
||||
</div>
|
||||
<div class="mqtt-field">
|
||||
<label>Port</label>
|
||||
<input class="mqtt-input" type="number" value="1883" style="width:90px;flex:none">
|
||||
</div>
|
||||
<div class="mqtt-field">
|
||||
<label>Topic base</label>
|
||||
<input class="mqtt-input" type="text" value="nanometrics/agents">
|
||||
</div>
|
||||
<div style="border-top:1px solid var(--border-1);padding-top:8px;display:flex;flex-direction:column;gap:6px;">
|
||||
<div class="mqtt-check-row">
|
||||
<label><i class="fa-solid fa-satellite-dish"></i> Auto-discovery (Home Assistant)</label>
|
||||
<label class="toggle"><input type="checkbox" checked><span class="toggle-slider"></span></label>
|
||||
</div>
|
||||
<div class="mqtt-check-row">
|
||||
<label><i class="fa-solid fa-arrow-right-to-bracket"></i> Birth message (connexion)</label>
|
||||
<label class="toggle"><input type="checkbox" checked><span class="toggle-slider"></span></label>
|
||||
</div>
|
||||
<div class="mqtt-check-row">
|
||||
<label><i class="fa-solid fa-skull"></i> Last Will message (déconnexion)</label>
|
||||
<label class="toggle"><input type="checkbox" checked><span class="toggle-slider"></span></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MÉTRIQUES -->
|
||||
<div class="cfg-section">
|
||||
<div class="cfg-sec-title">MÉTRIQUES ACTIVES</div>
|
||||
<div class="metrics-grid">
|
||||
<div class="metric-row"><div class="metric-ico"><i class="fa-solid fa-microchip"></i></div><span class="metric-name">cpu</span><label class="toggle tog-ok"><input type="checkbox" checked><span class="toggle-slider"></span></label></div>
|
||||
<div class="metric-row"><div class="metric-ico"><i class="fa-solid fa-memory"></i></div><span class="metric-name">memory</span><label class="toggle tog-ok"><input type="checkbox" checked><span class="toggle-slider"></span></label></div>
|
||||
<div class="metric-row"><div class="metric-ico"><i class="fa-solid fa-hard-drive"></i></div><span class="metric-name">disk</span><label class="toggle tog-ok"><input type="checkbox" checked><span class="toggle-slider"></span></label></div>
|
||||
<div class="metric-row"><div class="metric-ico"><i class="fa-solid fa-network-wired"></i></div><span class="metric-name">network</span><label class="toggle tog-ok"><input type="checkbox"><span class="toggle-slider"></span></label></div>
|
||||
<div class="metric-row"><div class="metric-ico"><i class="fa-solid fa-clock"></i></div><span class="metric-name">uptime</span><label class="toggle tog-ok"><input type="checkbox" checked><span class="toggle-slider"></span></label></div>
|
||||
<div class="metric-row"><div class="metric-ico"><i class="fa-solid fa-thermometer-half"></i></div><span class="metric-name">temperature</span><label class="toggle tog-ok"><input type="checkbox"><span class="toggle-slider"></span></label></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- COMMANDES -->
|
||||
<div class="cfg-section">
|
||||
<div class="cfg-sec-title">COMMANDES DISTANTES <span style="color:var(--ink-4);font-size:8px;margin-left:6px">— BIENTÔT DISPONIBLE</span></div>
|
||||
<div class="cmds-grid">
|
||||
<div class="cmd-btn"><i class="fa-solid fa-rotate-right"></i><span>reboot</span><span class="soon-tag">bientôt</span></div>
|
||||
<div class="cmd-btn"><i class="fa-solid fa-power-off"></i><span>shutdown</span><span class="soon-tag">bientôt</span></div>
|
||||
<div class="cmd-btn"><i class="fa-solid fa-display"></i><span>screen off</span><span class="soon-tag">bientôt</span></div>
|
||||
<div class="cmd-btn"><i class="fa-solid fa-arrow-up-from-bracket"></i><span>update</span><span class="soon-tag">bientôt</span></div>
|
||||
<div class="cmd-btn"><i class="fa-solid fa-arrow-up-right-dots"></i><span>upgrade</span><span class="soon-tag">bientôt</span></div>
|
||||
<div class="cmd-btn"><i class="fa-solid fa-terminal"></i><span>shell cmd</span><span class="soon-tag">bientôt</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="cfg-foot">
|
||||
<div class="cfg-status">
|
||||
<div class="dot"></div>
|
||||
<span>Config synchronisée avec l'agent</span>
|
||||
</div>
|
||||
<button class="btn" onclick="document.getElementById('overlay-agentcfg').style.display='none'">Annuler</button>
|
||||
<button class="btn primary"><i class="fa-solid fa-paper-plane"></i> Envoyer à l'agent</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/* tooltip */
|
||||
const tip=document.getElementById('tooltip');let tt;
|
||||
document.addEventListener('mouseover',e=>{const el=e.target.closest('[data-tip]');if(!el)return;clearTimeout(tt);tt=setTimeout(()=>{tip.textContent=el.dataset.tip;tip.classList.add('show');mv(e);},120);});
|
||||
document.addEventListener('mousemove',e=>{if(tip.classList.contains('show'))mv(e);});
|
||||
document.addEventListener('mouseout',e=>{const el=e.target.closest('[data-tip]');if(!el)return;clearTimeout(tt);tip.classList.remove('show');});
|
||||
function mv(e){const w=tip.offsetWidth,h=tip.offsetHeight;let x=e.clientX-w/2,y=e.clientY-h-10;x=Math.max(6,Math.min(x,window.innerWidth-w-6));if(y<6)y=e.clientY+18;tip.style.left=x+'px';tip.style.top=y+'px';}
|
||||
|
||||
/* thème */
|
||||
function toggleTheme(){const h=document.documentElement;h.dataset.theme=h.dataset.theme==='dark'?'light':'dark';document.getElementById('theme-icon').className=h.dataset.theme==='dark'?'fa-solid fa-moon':'fa-solid fa-sun';drawCharts();}
|
||||
|
||||
/* proto toggle */
|
||||
function toggleProto(p){
|
||||
const row=document.getElementById('row-'+p);
|
||||
const chk=document.getElementById('chk-'+p);
|
||||
const active=row.classList.contains('active')||row.classList.contains('mqtt-active');
|
||||
if(p==='udp'){row.classList.toggle('active',!active);chk.innerHTML=active?'':'';}
|
||||
if(p==='mqtt'){
|
||||
row.classList.toggle('mqtt-active',!active);
|
||||
chk.innerHTML=active?'':'<i class="fa-solid fa-check"></i>';
|
||||
if(!active){chk.style.cssText='background:var(--purple);border-color:var(--purple);color:var(--bg-0)';}
|
||||
else{chk.style.cssText='background:var(--bg-1);border-color:var(--border-3);color:var(--accent)';}
|
||||
document.getElementById('mqtt-opts').style.display=active?'none':'flex';
|
||||
}
|
||||
}
|
||||
|
||||
/* popups */
|
||||
function showDetail(){document.getElementById('overlay-detail').style.display='flex';drawCharts();}
|
||||
function showAgentCfg(){document.getElementById('overlay-agentcfg').style.display='flex';}
|
||||
|
||||
/* courbes */
|
||||
function makeCurve(pts,stroke,fill,w,h){
|
||||
const xs=pts.map((_,i)=>(i/(pts.length-1))*w);
|
||||
const ys=pts.map(v=>h-(v/100)*(h-6)-3);
|
||||
const wy=h-(70/100)*(h-6)-3;
|
||||
let d=`M${xs[0]} ${ys[0]}`;
|
||||
for(let i=1;i<pts.length;i++){const cx=(xs[i-1]+xs[i])/2;d+=` C${cx} ${ys[i-1]},${cx} ${ys[i]},${xs[i]} ${ys[i]}`;}
|
||||
const uid=Math.random().toString(36).slice(2);
|
||||
return `<defs><linearGradient id="g${uid}" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="${fill}" stop-opacity=".4"/><stop offset="100%" stop-color="${fill}" stop-opacity=".02"/></linearGradient></defs>
|
||||
<line x1="0" y1="${wy}" x2="${w}" y2="${wy}" stroke="var(--warn)" stroke-width=".8" stroke-dasharray="3,3" opacity=".5"/>
|
||||
<path d="${d} L${xs.at(-1)} ${h} L${xs[0]} ${h}Z" fill="url(#g${uid})"/>
|
||||
<path d="${d}" fill="none" stroke="${stroke}" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<circle cx="${xs.at(-1)}" cy="${ys.at(-1)}" r="2.5" fill="${stroke}"/>`;
|
||||
}
|
||||
function drawCharts(){
|
||||
const cpu=[38,41,35,40,44,42,39,45,50,48,43,42,44,41,40,43,47,45,42,44,41,43,42,40,43,41,44,43,41,42];
|
||||
const ram=[44,44,45,45,46,46,47,47,46,46,45,46,46,47,47,46,46,45,46,46,46,46,46,46,46,46,46,46,46,46];
|
||||
const cs=getComputedStyle(document.documentElement);
|
||||
const ac=cs.getPropertyValue('--accent').trim(),bl=cs.getPropertyValue('--blue').trim();
|
||||
document.getElementById('cpu-chart').innerHTML=makeCurve(cpu,ac,ac,200,52);
|
||||
document.getElementById('ram-chart').innerHTML=makeCurve(ram,bl,bl,200,52);
|
||||
}
|
||||
window.addEventListener('load',()=>{drawCharts();});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,634 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Nanometrics — v7</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Share+Tech+Mono:wght@400&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<script src="/helper.js"></script>
|
||||
<style>
|
||||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
||||
:root[data-theme="dark"]{
|
||||
--accent:#fe8019;--accent-soft:#d65d0e;--accent-glow:rgba(254,128,25,.28);
|
||||
--bg-0:#1d1813;--bg-1:#2a231d;--bg-2:#32291f;--bg-3:#3c332a;--bg-4:#4a3f33;--bg-5:#57493c;
|
||||
--ink-1:#f2e5c7;--ink-2:#d5c4a1;--ink-3:#a89984;--ink-4:#7c6f64;
|
||||
--ok:#4dbb26;--warn:#fabd2f;--err:#fb4934;--blue:#3db0d1;--purple:#c882c8;
|
||||
--border-1:rgba(255,255,255,.06);--border-2:rgba(255,255,255,.12);--border-3:rgba(255,255,255,.26);
|
||||
--tile-3d:0 1px 0 rgba(255,255,255,.08) inset,0 -1px 0 rgba(0,0,0,.3) inset,0 6px 20px rgba(0,0,0,.5);
|
||||
--tile-press:inset 0 2px 8px rgba(0,0,0,.5),inset 0 1px 3px rgba(0,0,0,.4);
|
||||
--hover-glow:0 0 0 1px var(--accent-soft),0 0 24px var(--accent-glow),0 6px 20px rgba(0,0,0,.5);
|
||||
--font-ui:'Inter',system-ui,sans-serif;--font-mono:'JetBrains Mono',monospace;--font-terminal:'Share Tech Mono',monospace;
|
||||
}
|
||||
:root[data-theme="light"]{
|
||||
--accent:#af3a03;--accent-soft:#d65d0e;--accent-glow:rgba(175,58,3,.18);
|
||||
--bg-0:#d5c4a1;--bg-1:#ebdbb2;--bg-2:#d5c4a1;--bg-3:#bdae93;--bg-4:#a89984;--bg-5:#928374;
|
||||
--ink-1:#3c3836;--ink-2:#504945;--ink-3:#665c54;--ink-4:#7c6f64;
|
||||
--ok:#3c911c;--warn:#b57614;--err:#9d0006;--blue:#2d82a3;--purple:#8c468c;
|
||||
--border-1:rgba(0,0,0,.08);--border-2:rgba(0,0,0,.15);--border-3:rgba(0,0,0,.3);
|
||||
--tile-3d:0 1px 0 rgba(255,255,255,.55) inset,0 -1px 0 rgba(0,0,0,.08) inset,0 4px 14px rgba(0,0,0,.13);
|
||||
--tile-press:inset 0 2px 6px rgba(0,0,0,.2);
|
||||
--hover-glow:0 0 0 1px var(--accent-soft),0 0 18px var(--accent-glow),0 4px 14px rgba(0,0,0,.13);
|
||||
--font-ui:'Inter',system-ui,sans-serif;--font-mono:'JetBrains Mono',monospace;--font-terminal:'Share Tech Mono',monospace;
|
||||
}
|
||||
body{background:var(--bg-1);color:var(--ink-1);font-family:var(--font-ui);font-size:13px;height:100vh;display:flex;flex-direction:column;overflow:hidden;transition:background .2s,color .2s}
|
||||
|
||||
/* TOOLTIP */
|
||||
#tooltip{position:fixed;z-index:9999;pointer-events:none;background:var(--bg-0);color:var(--ink-1);border:1px solid var(--border-3);border-radius:5px;padding:4px 9px;font-size:11px;font-family:var(--font-ui);white-space:nowrap;opacity:0;transition:opacity .12s;box-shadow:0 4px 12px rgba(0,0,0,.4)}
|
||||
#tooltip.show{opacity:1}
|
||||
|
||||
/* HEADER */
|
||||
.header{background:var(--bg-2);border-bottom:1px solid var(--border-2);padding:0 20px;height:48px;display:flex;align-items:center;gap:12px;flex-shrink:0}
|
||||
.logo{display:flex;align-items:center;gap:8px}
|
||||
.logo-led{width:9px;height:9px;border-radius:50%;background:var(--accent);box-shadow:0 0 8px var(--accent-glow);animation:blink 2s infinite}
|
||||
@keyframes blink{0%,100%{opacity:1}50%{opacity:.4}}
|
||||
.logo-name{font-weight:700;font-size:14px;letter-spacing:.05em;font-family:var(--font-terminal)}
|
||||
.logo-ver{font-size:10px;color:var(--ink-4);font-family:var(--font-terminal)}
|
||||
.h-sep{width:1px;height:24px;background:var(--border-2)}
|
||||
.h-spacer{flex:1}
|
||||
.h-stats{display:flex;gap:14px}
|
||||
.h-stat{display:flex;align-items:center;gap:5px}
|
||||
.h-stat .lbl{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.06em}
|
||||
.h-stat .val{font-family:var(--font-mono);font-weight:700;font-size:13px}
|
||||
.c-ok{color:var(--ok)}.c-warn{color:var(--warn)}.c-err{color:var(--err)}.c-n{color:var(--ink-2)}
|
||||
.hbtn{width:34px;height:34px;border-radius:8px;border:1px solid var(--border-2);background:var(--bg-3);color:var(--ink-2);font-size:14px;display:flex;align-items:center;justify-content:center;cursor:pointer;user-select:none;transition:background .12s,color .12s,transform .08s,box-shadow .08s}
|
||||
.hbtn:hover{background:var(--bg-4);color:var(--accent)}
|
||||
.hbtn:active{transform:translateY(1px) scale(.96);box-shadow:var(--tile-press)}
|
||||
|
||||
/* GRID */
|
||||
.main{flex:1;padding:14px 16px;overflow-y:auto}
|
||||
.agents-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:10px}
|
||||
.tile{background:var(--bg-3);border-radius:10px;padding:12px 14px;border:1px solid var(--border-1);box-shadow:var(--tile-3d);cursor:pointer;user-select:none;display:flex;flex-direction:column;gap:9px;transition:box-shadow .15s,transform .08s,border-color .15s}
|
||||
.tile:hover{box-shadow:var(--hover-glow);border-color:var(--accent-soft)}
|
||||
.tile:active{transform:translateY(2px) scale(.99);box-shadow:var(--tile-press)}
|
||||
.tile.t-warn{border-color:rgba(250,189,47,.3)}.tile.t-warn:hover{border-color:var(--warn);box-shadow:0 0 0 1px var(--warn),0 0 22px rgba(250,189,47,.22),0 6px 20px rgba(0,0,0,.5)}
|
||||
.tile.t-err{border-color:rgba(251,73,52,.35)}.tile.t-err:hover{border-color:var(--err);box-shadow:0 0 0 1px var(--err),0 0 22px rgba(251,73,52,.25),0 6px 20px rgba(0,0,0,.5)}
|
||||
.tile.t-off{opacity:.5;cursor:default}.tile.t-off:hover,.tile.t-off:active{box-shadow:var(--tile-3d);border-color:var(--border-1);transform:none}
|
||||
.tile-head{display:flex;align-items:center;gap:8px}
|
||||
.t-icon{width:28px;height:28px;border-radius:7px;background:var(--bg-4);display:flex;align-items:center;justify-content:center;color:var(--accent);font-size:13px;flex-shrink:0;overflow:hidden}
|
||||
.t-names{flex:1;min-width:0}
|
||||
.t-host{font-weight:600;font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||
.t-ip{font-family:var(--font-mono);font-size:10px;color:var(--ink-4)}
|
||||
.t-led{width:8px;height:8px;border-radius:50%;flex-shrink:0}
|
||||
.s-ok{background:var(--ok);box-shadow:0 0 6px var(--ok)}.s-warn{background:var(--warn);box-shadow:0 0 6px var(--warn);animation:blink 1.5s infinite}.s-err{background:var(--err);box-shadow:0 0 8px var(--err);animation:blink 1s infinite}.s-off{background:var(--ink-4)}
|
||||
.tile-gauges{display:flex;flex-direction:column;gap:5px}
|
||||
.g-row{display:flex;align-items:center;gap:7px}
|
||||
.g-ico{width:18px;height:18px;display:flex;align-items:center;justify-content:center;font-size:11px;color:var(--ink-3);flex-shrink:0;cursor:help}
|
||||
.g-bar{flex:1;height:5px;border-radius:3px;background:var(--bg-1);overflow:hidden}
|
||||
.g-fill{height:100%;border-radius:3px;background:var(--ok)}
|
||||
.g-fill.w{background:var(--warn)}.g-fill.e{background:var(--err)}
|
||||
.g-val{font-family:var(--font-mono);font-size:11px;color:var(--ink-2);width:34px;text-align:right}
|
||||
.tile-foot{font-family:var(--font-terminal);font-size:10px;color:var(--ink-4);display:flex;align-items:center;gap:5px}
|
||||
.tile-foot i{font-size:9px}
|
||||
|
||||
/* FOOTER */
|
||||
.footer{background:var(--bg-0);border-top:1px solid var(--border-2);height:26px;display:flex;align-items:center;font-family:var(--font-terminal);font-size:11px;color:var(--ink-4);flex-shrink:0}
|
||||
.f-mode{background:var(--accent);color:var(--bg-0);padding:0 12px;height:100%;display:flex;align-items:center;font-weight:700;letter-spacing:.04em}
|
||||
.f-cell{padding:0 12px;border-right:1px solid var(--border-1);display:flex;align-items:center;gap:5px;height:100%}
|
||||
.f-val{font-family:var(--font-mono);color:var(--ink-2)}.f-val.w{color:var(--warn)}
|
||||
.f-minibar{width:36px;height:4px;border-radius:2px;background:var(--bg-3);overflow:hidden}
|
||||
.f-minifill{height:100%;border-radius:2px;background:var(--ok)}.f-minifill.w{background:var(--warn)}
|
||||
.f-spacer{flex:1}.f-right{padding:0 12px;display:flex;align-items:center;gap:6px;color:var(--ink-3)}
|
||||
.f-time{font-family:var(--font-mono);color:var(--ink-2)}
|
||||
|
||||
/* OVERLAY */
|
||||
.overlay{position:fixed;inset:0;background:rgba(0,0,0,.65);z-index:100;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(2px)}
|
||||
.popup{background:var(--bg-2);border:1px solid var(--border-3);border-radius:12px;box-shadow:0 24px 64px rgba(0,0,0,.7);display:flex;flex-direction:column;overflow:hidden}
|
||||
|
||||
/* POPUP DETAIL — redimensionnable */
|
||||
#popup-detail{
|
||||
width:540px;max-width:96vw;max-height:92vh;
|
||||
/* redimensionnable par l'utilisateur */
|
||||
resize:both;overflow:hidden;
|
||||
min-width:380px;min-height:300px;
|
||||
}
|
||||
/* on met overflow:auto sur le body pour que resize fonctionne */
|
||||
#popup-detail .pop-body{overflow-y:auto;flex:1}
|
||||
|
||||
.pop-head{background:var(--bg-3);padding:14px 18px;border-bottom:1px solid var(--border-2);display:flex;align-items:center;gap:12px;flex-shrink:0}
|
||||
.agent-icon-wrap{position:relative;width:44px;height:44px;border-radius:10px;flex-shrink:0;cursor:pointer;overflow:hidden;background:var(--bg-4);display:flex;align-items:center;justify-content:center;color:var(--accent);font-size:18px;border:2px solid var(--border-2);transition:border-color .15s}
|
||||
.agent-icon-wrap:hover{border-color:var(--accent)}
|
||||
.agent-icon-overlay{position:absolute;inset:0;background:rgba(0,0,0,.6);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;opacity:0;transition:opacity .15s;font-size:10px;color:#fff}
|
||||
.agent-icon-overlay i{font-size:14px}
|
||||
.agent-icon-wrap:hover .agent-icon-overlay{opacity:1}
|
||||
#icon-upload{display:none}
|
||||
.pop-head-info{flex:1}
|
||||
.pop-host{font-weight:700;font-size:15px}
|
||||
.pop-ip{font-family:var(--font-mono);font-size:11px;color:var(--ink-4)}
|
||||
.upload-hint{font-size:10px;color:var(--ink-4);font-family:var(--font-terminal);margin-top:2px}
|
||||
.pop-led{width:10px;height:10px;border-radius:50%;background:var(--ok);box-shadow:0 0 8px var(--ok);flex-shrink:0}
|
||||
.pop-close{width:28px;height:28px;border-radius:6px;background:var(--bg-5);color:var(--ink-3);display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:12px;border:1px solid var(--border-1);transition:background .12s,color .12s,transform .08s;user-select:none}
|
||||
.pop-close:hover{background:var(--err);color:#fff}
|
||||
.pop-close:active{transform:translateY(1px) scale(.93);box-shadow:var(--tile-press)}
|
||||
|
||||
.pop-body{padding:16px 18px;display:flex;flex-direction:column;gap:14px}
|
||||
.sec-title{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.08em;margin-bottom:8px}
|
||||
.kpi-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:7px}
|
||||
.kpi{background:var(--bg-3);border-radius:8px;padding:10px 12px;border:1px solid var(--border-1);box-shadow:var(--tile-3d)}
|
||||
.kpi-lbl{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.06em}
|
||||
.kpi-val{font-family:var(--font-mono);font-size:20px;font-weight:700;line-height:1.1;margin-top:2px}
|
||||
.kpi-val .u{font-size:10px;color:var(--ink-3);font-weight:400}
|
||||
.kpi-sub{font-size:10px;color:var(--ink-4);font-family:var(--font-mono);margin-top:2px}
|
||||
.charts-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}
|
||||
.chart-card{background:var(--bg-3);border-radius:8px;padding:10px 12px;border:1px solid var(--border-1);box-shadow:var(--tile-3d)}
|
||||
.chart-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}
|
||||
.chart-label{display:flex;align-items:center;gap:6px;font-size:10px;font-family:var(--font-terminal);letter-spacing:.06em;color:var(--ink-3)}
|
||||
.chart-cur{font-family:var(--font-mono);font-size:16px;font-weight:700}
|
||||
.chart-svg{width:100%;height:52px;display:block}
|
||||
.chart-axis{display:flex;justify-content:space-between;margin-top:2px;font-family:var(--font-terminal);font-size:9px;color:var(--ink-4)}
|
||||
|
||||
/* STOCKAGE + SMART */
|
||||
.storage-block{display:flex;flex-direction:column;gap:8px}
|
||||
.det-gauges{display:flex;flex-direction:column;gap:7px}
|
||||
.dg-row{display:flex;align-items:center;gap:10px}
|
||||
.dg-ico{width:22px;text-align:center;font-size:13px;cursor:help}
|
||||
.dg-bar{flex:1;height:7px;border-radius:4px;background:var(--bg-1);overflow:hidden}
|
||||
.dg-fill{height:100%;border-radius:4px;background:var(--ok)}
|
||||
.dg-fill.b{background:var(--blue)}
|
||||
.dg-val{font-family:var(--font-mono);font-size:12px;color:var(--ink-2);width:90px;text-align:right}
|
||||
|
||||
/* bouton SMART */
|
||||
.smart-btn{
|
||||
display:inline-flex;align-items:center;gap:8px;padding:7px 12px;border-radius:8px;
|
||||
border:1px solid var(--border-2);background:var(--bg-3);cursor:pointer;user-select:none;
|
||||
transition:background .12s,border-color .12s,transform .08s,box-shadow .08s;
|
||||
font-family:var(--font-terminal);font-size:11px;
|
||||
}
|
||||
.smart-btn:hover{background:var(--bg-4);border-color:var(--border-3)}
|
||||
.smart-btn:active{transform:translateY(1px);box-shadow:var(--tile-press)}
|
||||
.smart-btn.ok{border-color:rgba(77,187,38,.3);color:var(--ok)}
|
||||
.smart-btn.ok .smart-dot{background:var(--ok);box-shadow:0 0 5px var(--ok)}
|
||||
.smart-btn.warn{border-color:rgba(250,189,47,.3);color:var(--warn)}
|
||||
.smart-btn.warn .smart-dot{background:var(--warn);box-shadow:0 0 5px var(--warn)}
|
||||
.smart-btn.err{border-color:rgba(251,73,52,.3);color:var(--err)}
|
||||
.smart-btn.err .smart-dot{background:var(--err);box-shadow:0 0 5px var(--err)}
|
||||
.smart-dot{width:7px;height:7px;border-radius:50%}
|
||||
.smart-label{font-weight:600;letter-spacing:.04em}
|
||||
.smart-temp{font-family:var(--font-mono);font-size:10px;color:var(--ink-3);margin-left:4px}
|
||||
.smart-chevron{font-size:10px;color:var(--ink-4);margin-left:auto}
|
||||
|
||||
.meta-grid{display:grid;grid-template-columns:1fr 1fr;gap:6px}
|
||||
.meta{background:var(--bg-3);border-radius:6px;padding:8px 10px;border:1px solid var(--border-1)}
|
||||
.meta-lbl{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.06em}
|
||||
.meta-val{font-family:var(--font-mono);font-size:12px;color:var(--ink-2);margin-top:2px}
|
||||
.proto-badges{display:flex;gap:5px;margin-top:4px;flex-wrap:wrap}
|
||||
.proto-badge{display:inline-flex;align-items:center;gap:4px;padding:2px 7px;border-radius:999px;font-size:10px;font-family:var(--font-terminal);font-weight:600}
|
||||
.proto-badge.udp{background:rgba(61,176,209,.15);color:var(--blue);border:1px solid rgba(61,176,209,.3)}
|
||||
.proto-badge.mqtt{background:rgba(200,130,200,.15);color:var(--purple);border:1px solid rgba(200,130,200,.3)}
|
||||
|
||||
.pop-foot{padding:10px 18px;border-top:1px solid var(--border-2);background:var(--bg-3);display:flex;align-items:center;gap:8px;flex-shrink:0}
|
||||
.pop-uptime{font-family:var(--font-terminal);font-size:11px;color:var(--ink-4);flex:1}
|
||||
.resize-hint{font-family:var(--font-terminal);font-size:9px;color:var(--ink-4);display:flex;align-items:center;gap:4px}
|
||||
|
||||
.btn{padding:6px 14px;border-radius:8px;border:1px solid var(--border-2);background:var(--bg-4);color:var(--ink-2);font-size:12px;font-family:var(--font-ui);cursor:pointer;display:flex;align-items:center;gap:6px;user-select:none;transition:background .1s,transform .08s,box-shadow .08s}
|
||||
.btn:hover{background:var(--bg-5)}.btn:active{transform:translateY(1px) scale(.97);box-shadow:var(--tile-press)}
|
||||
.btn.primary{background:var(--accent);color:var(--bg-0);border-color:var(--accent-soft);font-weight:600}.btn.primary:hover{background:var(--accent-soft)}
|
||||
.btn-agent-cfg{width:34px;height:34px;border-radius:8px;border:1px solid var(--border-2);background:var(--bg-4);color:var(--ink-3);font-size:15px;display:flex;align-items:center;justify-content:center;cursor:pointer;user-select:none;transition:background .12s,color .12s,transform .08s,box-shadow .08s}
|
||||
.btn-agent-cfg:hover{background:var(--bg-5);color:var(--accent)}.btn-agent-cfg:active{transform:translateY(1px) scale(.93);box-shadow:var(--tile-press)}
|
||||
|
||||
/* ══ POPUP SMART ══ */
|
||||
#overlay-smart{z-index:300}
|
||||
#popup-smart{width:500px;max-width:96vw;max-height:88vh}
|
||||
.smart-head{background:var(--bg-3);padding:14px 18px;border-bottom:1px solid var(--border-2);display:flex;align-items:center;gap:10px}
|
||||
.smart-head-icon{width:32px;height:32px;border-radius:8px;background:var(--bg-4);display:flex;align-items:center;justify-content:center;color:var(--ok);font-size:15px}
|
||||
.smart-head-info{flex:1}
|
||||
.smart-head-title{font-weight:700;font-size:14px}
|
||||
.smart-head-sub{font-size:11px;color:var(--ink-4);font-family:var(--font-terminal)}
|
||||
.smart-body{padding:18px;display:flex;flex-direction:column;gap:16px;overflow-y:auto}
|
||||
|
||||
/* verdict global */
|
||||
.smart-verdict{display:flex;align-items:center;gap:14px;background:rgba(77,187,38,.1);border:1px solid rgba(77,187,38,.3);border-radius:10px;padding:14px 18px}
|
||||
.verdict-icon{font-size:28px;color:var(--ok)}
|
||||
.verdict-text .v-title{font-size:16px;font-weight:700;color:var(--ok)}
|
||||
.verdict-text .v-sub{font-size:12px;color:var(--ink-3);margin-top:3px}
|
||||
|
||||
/* indicateurs simplifiés */
|
||||
.smart-indicators{display:grid;grid-template-columns:1fr 1fr;gap:8px}
|
||||
.smart-ind{background:var(--bg-3);border-radius:8px;padding:12px 14px;border:1px solid var(--border-1);box-shadow:var(--tile-3d)}
|
||||
.si-header{display:flex;align-items:center;gap:8px;margin-bottom:6px}
|
||||
.si-icon{font-size:14px;width:22px;text-align:center}
|
||||
.si-title{font-weight:600;font-size:12px;flex:1}
|
||||
.si-status{font-size:10px;font-family:var(--font-terminal);font-weight:700;padding:1px 7px;border-radius:999px}
|
||||
.si-status.ok{background:rgba(77,187,38,.15);color:var(--ok)}
|
||||
.si-status.warn{background:rgba(250,189,47,.15);color:var(--warn)}
|
||||
.si-val{font-family:var(--font-mono);font-size:18px;font-weight:700;color:var(--ink-1)}
|
||||
.si-val .u{font-size:11px;color:var(--ink-3);font-weight:400}
|
||||
.si-desc{font-size:11px;color:var(--ink-3);margin-top:4px;line-height:1.4}
|
||||
|
||||
/* attributs détail */
|
||||
.smart-attrs{display:flex;flex-direction:column;gap:5px}
|
||||
.attr-row{display:flex;align-items:center;gap:10px;padding:6px 10px;border-radius:6px;background:var(--bg-3);border:1px solid var(--border-1)}
|
||||
.attr-id{font-family:var(--font-mono);font-size:10px;color:var(--ink-4);width:28px;flex-shrink:0}
|
||||
.attr-name{flex:1;font-size:11px;color:var(--ink-2)}
|
||||
.attr-val{font-family:var(--font-mono);font-size:11px;font-weight:600;width:40px;text-align:right}
|
||||
.attr-explain{font-size:10px;color:var(--ink-4);width:130px;text-align:right;font-family:var(--font-terminal)}
|
||||
.attr-ok{color:var(--ok)}.attr-warn{color:var(--warn)}.attr-err{color:var(--err)}
|
||||
|
||||
.smart-footer{padding:12px 18px;border-top:1px solid var(--border-2);background:var(--bg-3);display:flex;align-items:center;gap:8px}
|
||||
.smart-footer-note{flex:1;font-size:10px;color:var(--ink-4);font-family:var(--font-terminal)}
|
||||
|
||||
/* CONFIG AGENT */
|
||||
#overlay-agentcfg{z-index:200}
|
||||
#popup-agentcfg{width:480px;max-width:96vw;max-height:90vh}
|
||||
.cfg-head{background:var(--bg-3);padding:14px 18px;border-bottom:1px solid var(--border-2);display:flex;align-items:center;gap:10px}
|
||||
.cfg-head-icon{width:32px;height:32px;border-radius:8px;background:var(--bg-4);display:flex;align-items:center;justify-content:center;color:var(--accent);font-size:15px}
|
||||
.cfg-head-info{flex:1}.cfg-head-title{font-weight:700;font-size:14px}.cfg-head-sub{font-size:11px;color:var(--ink-4);font-family:var(--font-terminal)}
|
||||
.cfg-body{padding:18px;display:flex;flex-direction:column;gap:16px;overflow-y:auto;max-height:58vh}
|
||||
.cfg-section{display:flex;flex-direction:column;gap:8px}
|
||||
.cfg-sec-title{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.08em;padding-bottom:6px;border-bottom:1px solid var(--border-1)}
|
||||
.check-row{display:flex;align-items:center;gap:10px;padding:8px 10px;border-radius:8px;background:var(--bg-3);border:1px solid var(--border-1);cursor:pointer;transition:background .12s,border-color .12s}
|
||||
.check-row.active{border-color:var(--accent-soft);background:rgba(254,128,25,.08)}
|
||||
.check-row.mqtt-active{border-color:rgba(200,130,200,.4);background:rgba(200,130,200,.07)}
|
||||
.chk-box{width:18px;height:18px;border-radius:4px;border:2px solid var(--border-3);background:var(--bg-1);display:flex;align-items:center;justify-content:center;font-size:11px;flex-shrink:0}
|
||||
.check-row.active .chk-box{background:var(--accent);border-color:var(--accent);color:var(--bg-0)}
|
||||
.check-row.mqtt-active .chk-box{background:var(--purple);border-color:var(--purple);color:var(--bg-0)}
|
||||
.chk-label{flex:1}.chk-name{font-weight:600;font-size:13px}.chk-desc{font-size:11px;color:var(--ink-4);font-family:var(--font-terminal);margin-top:1px}
|
||||
.chk-badge{font-size:10px;font-family:var(--font-terminal);font-weight:700;padding:1px 7px;border-radius:999px}
|
||||
.chk-badge.udp{background:rgba(61,176,209,.15);color:var(--blue)}.chk-badge.mqtt{background:rgba(200,130,200,.15);color:var(--purple)}
|
||||
.mqtt-opts{background:var(--bg-3);border-radius:8px;border:1px solid rgba(200,130,200,.2);padding:12px 14px;display:flex;flex-direction:column;gap:10px}
|
||||
.mqtt-field{display:flex;align-items:center;gap:10px}
|
||||
.mqtt-field label{font-size:11px;color:var(--ink-3);font-family:var(--font-terminal);width:90px;flex-shrink:0}
|
||||
.mqtt-input{flex:1;background:var(--bg-1);border:1px solid var(--border-2);border-radius:6px;color:var(--ink-1);padding:6px 10px;font-size:12px;font-family:var(--font-mono)}
|
||||
.mqtt-input:focus{outline:none;border-color:var(--purple)}
|
||||
.mqtt-check-row{display:flex;align-items:center;justify-content:space-between;padding:4px 0}
|
||||
.mqtt-check-row label{font-size:12px;color:var(--ink-2);display:flex;align-items:center;gap:7px;cursor:pointer}
|
||||
.mqtt-check-row label i{color:var(--purple);font-size:11px}
|
||||
.toggle{position:relative;width:34px;height:18px;flex-shrink:0}
|
||||
.toggle input{opacity:0;width:0;height:0}
|
||||
.toggle-slider{position:absolute;inset:0;border-radius:9px;background:var(--bg-4);border:1px solid var(--border-2);cursor:pointer;transition:background .2s}
|
||||
.toggle-slider::before{content:'';position:absolute;width:12px;height:12px;border-radius:50%;background:var(--ink-4);top:2px;left:2px;transition:transform .2s,background .2s}
|
||||
.toggle input:checked+.toggle-slider{background:rgba(200,130,200,.3);border-color:var(--purple)}
|
||||
.toggle input:checked+.toggle-slider::before{transform:translateX(16px);background:var(--purple)}
|
||||
.metrics-grid{display:grid;grid-template-columns:1fr 1fr;gap:6px}
|
||||
.metric-row{display:flex;align-items:center;gap:8px;padding:7px 10px;border-radius:7px;background:var(--bg-3);border:1px solid var(--border-1)}
|
||||
.metric-ico{width:22px;text-align:center;font-size:12px;color:var(--ink-3)}
|
||||
.metric-name{flex:1;font-size:12px;color:var(--ink-2);font-family:var(--font-terminal)}
|
||||
.tog-ok input:checked+.toggle-slider{background:rgba(77,187,38,.3);border-color:var(--ok)}
|
||||
.tog-ok input:checked+.toggle-slider::before{background:var(--ok)}
|
||||
.cmds-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:6px}
|
||||
.cmd-btn{display:flex;flex-direction:column;align-items:center;gap:4px;padding:10px 8px;border-radius:8px;background:var(--bg-3);border:1px solid var(--border-1);cursor:not-allowed;opacity:.4}
|
||||
.cmd-btn i{font-size:16px;color:var(--ink-3)}.cmd-btn span{font-size:10px;color:var(--ink-4);font-family:var(--font-terminal)}
|
||||
.soon-tag{font-size:8px;color:var(--ink-4);font-family:var(--font-terminal)}
|
||||
.cfg-foot{padding:12px 18px;border-top:1px solid var(--border-2);background:var(--bg-3);display:flex;align-items:center;gap:8px}
|
||||
.cfg-status{flex:1;display:flex;align-items:center;gap:6px;font-family:var(--font-terminal);font-size:11px;color:var(--ink-4)}
|
||||
.cfg-status .dot{width:6px;height:6px;border-radius:50%;background:var(--ok)}
|
||||
|
||||
::-webkit-scrollbar{width:5px}::-webkit-scrollbar-track{background:var(--bg-1)}::-webkit-scrollbar-thumb{background:var(--bg-4);border-radius:3px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="tooltip"></div>
|
||||
|
||||
<!-- HEADER -->
|
||||
<div class="header">
|
||||
<div class="logo"><div class="logo-led"></div><span class="logo-name">NANOMETRICS</span><span class="logo-ver">v1.0</span></div>
|
||||
<div class="h-sep"></div>
|
||||
<div class="h-stats">
|
||||
<div class="h-stat"><span class="lbl">AGENTS</span><span class="val c-n">8</span></div>
|
||||
<div class="h-stat"><span class="lbl">OK</span><span class="val c-ok">5</span></div>
|
||||
<div class="h-stat"><span class="lbl">WARN</span><span class="val c-warn">2</span></div>
|
||||
<div class="h-stat"><span class="lbl">ERR</span><span class="val c-err">1</span></div>
|
||||
</div>
|
||||
<div class="h-spacer"></div>
|
||||
<div class="hbtn" onclick="toggleTheme()" data-tip="Thème clair / sombre"><i class="fa-solid fa-moon" id="theme-icon"></i></div>
|
||||
<div class="hbtn" data-tip="Configuration interface"><i class="fa-solid fa-sliders"></i></div>
|
||||
</div>
|
||||
|
||||
<!-- GRID -->
|
||||
<div class="main">
|
||||
<div class="agents-grid">
|
||||
<div class="tile" onclick="showDetail()">
|
||||
<div class="tile-head"><div class="t-icon"><i class="fa-solid fa-server"></i></div><div class="t-names"><div class="t-host">srv-prod-01</div><div class="t-ip">10.0.0.11</div></div><div class="t-led s-ok"></div></div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row"><div class="g-ico" data-tip="CPU"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill" style="width:42%"></div></div><span class="g-val">42%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="RAM"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill" style="width:58%"></div></div><span class="g-val">58%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="Disque"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill" style="width:31%"></div></div><span class="g-val">31%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>14j 6h</div>
|
||||
</div>
|
||||
<div class="tile t-warn"><div class="tile-head"><div class="t-icon"><i class="fa-solid fa-server"></i></div><div class="t-names"><div class="t-host">srv-backup-02</div><div class="t-ip">10.0.0.12</div></div><div class="t-led s-warn"></div></div><div class="tile-gauges"><div class="g-row"><div class="g-ico" data-tip="CPU élevé" style="color:var(--warn)"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill w" style="width:78%"></div></div><span class="g-val" style="color:var(--warn)">78%</span></div><div class="g-row"><div class="g-ico" data-tip="RAM élevée" style="color:var(--warn)"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill w" style="width:72%"></div></div><span class="g-val" style="color:var(--warn)">72%</span></div><div class="g-row"><div class="g-ico" data-tip="Disque"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill" style="width:20%"></div></div><span class="g-val">20%</span></div></div><div class="tile-foot"><i class="fa-solid fa-clock"></i>3j 14h</div></div>
|
||||
<div class="tile t-err"><div class="tile-head"><div class="t-icon" style="color:var(--err)"><i class="fa-solid fa-microchip"></i></div><div class="t-names"><div class="t-host">rpi-sensor-03</div><div class="t-ip">10.0.0.23</div></div><div class="t-led s-err"></div></div><div class="tile-gauges"><div class="g-row"><div class="g-ico" data-tip="CPU"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill" style="width:12%"></div></div><span class="g-val">12%</span></div><div class="g-row"><div class="g-ico" data-tip="RAM"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill" style="width:38%"></div></div><span class="g-val">38%</span></div><div class="g-row"><div class="g-ico" data-tip="Disque critique !" style="color:var(--err)"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill e" style="width:94%"></div></div><span class="g-val" style="color:var(--err)">94%</span></div></div><div class="tile-foot"><i class="fa-solid fa-clock"></i>62j 1h</div></div>
|
||||
<div class="tile"><div class="tile-head"><div class="t-icon"><i class="fa-solid fa-hard-drive"></i></div><div class="t-names"><div class="t-host">nas-storage-04</div><div class="t-ip">10.0.0.30</div></div><div class="t-led s-ok"></div></div><div class="tile-gauges"><div class="g-row"><div class="g-ico" data-tip="CPU"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill" style="width:8%"></div></div><span class="g-val">8%</span></div><div class="g-row"><div class="g-ico" data-tip="RAM"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill" style="width:45%"></div></div><span class="g-val">45%</span></div><div class="g-row"><div class="g-ico" data-tip="Disque"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill" style="width:66%"></div></div><span class="g-val">66%</span></div></div><div class="tile-foot"><i class="fa-solid fa-clock"></i>128j 3h</div></div>
|
||||
<div class="tile t-off"><div class="tile-head"><div class="t-icon" style="color:var(--ink-4)"><i class="fa-solid fa-server"></i></div><div class="t-names"><div class="t-host">srv-dev-07</div><div class="t-ip">10.0.0.60</div></div><div class="t-led s-off"></div></div><div class="tile-gauges"><div class="g-row"><div class="g-ico"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"></div><span class="g-val" style="color:var(--ink-4)">—</span></div><div class="g-row"><div class="g-ico"><i class="fa-solid fa-memory"></i></div><div class="g-bar"></div><span class="g-val" style="color:var(--ink-4)">—</span></div><div class="g-row"><div class="g-ico"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"></div><span class="g-val" style="color:var(--ink-4)">—</span></div></div><div class="tile-foot" style="color:var(--err)"><i class="fa-solid fa-circle-xmark"></i>Hors ligne · vu il y a 2h</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<div class="footer">
|
||||
<div class="f-mode">LIVE</div>
|
||||
<div class="f-cell"><i class="fa-solid fa-server" style="font-size:10px"></i><span>SERVEUR</span></div>
|
||||
<div class="f-cell"><i class="fa-solid fa-microchip" style="font-size:10px"></i><span class="f-val">18%</span><div class="f-minibar"><div class="f-minifill" style="width:18%"></div></div></div>
|
||||
<div class="f-cell"><i class="fa-solid fa-memory" style="font-size:10px"></i><span class="f-val w">71%</span><div class="f-minibar"><div class="f-minifill w" style="width:71%"></div></div></div>
|
||||
<div class="f-spacer"></div>
|
||||
<div class="f-right"><i class="fa-solid fa-rotate"></i><span>Actualisation : <span class="f-time">22:14:07</span></span></div>
|
||||
</div>
|
||||
|
||||
<!-- ══ POPUP DÉTAIL — redimensionnable ══ -->
|
||||
<div class="overlay" id="overlay-detail" style="display:flex" onclick="if(event.target===this)this.style.display='none'">
|
||||
<div class="popup" id="popup-detail" onclick="event.stopPropagation()">
|
||||
<div class="pop-head">
|
||||
<div class="agent-icon-wrap" onclick="document.getElementById('icon-upload').click()" data-tip="Changer l'icône">
|
||||
<i class="fa-solid fa-server"></i>
|
||||
<div class="agent-icon-overlay"><i class="fa-solid fa-camera"></i><span>Changer</span></div>
|
||||
</div>
|
||||
<input type="file" id="icon-upload" accept=".svg,.jpg,.jpeg,.png,.webp">
|
||||
<div class="pop-head-info">
|
||||
<div class="pop-host">srv-prod-01</div>
|
||||
<div class="pop-ip">10.0.0.11</div>
|
||||
<div class="upload-hint">Cliquer sur l'icône pour personnaliser · SVG JPG PNG WEBP · max 128×128 px</div>
|
||||
</div>
|
||||
<div class="pop-led"></div>
|
||||
<div class="pop-close" onclick="document.getElementById('overlay-detail').style.display='none'" data-tip="Fermer"><i class="fa-solid fa-xmark"></i></div>
|
||||
</div>
|
||||
|
||||
<div class="pop-body">
|
||||
<div>
|
||||
<div class="sec-title">MÉTRIQUES ACTUELLES</div>
|
||||
<div class="kpi-grid">
|
||||
<div class="kpi"><div class="kpi-lbl">CPU</div><div class="kpi-val c-ok">42<span class="u">%</span></div><div class="kpi-sub">4 cœurs</div></div>
|
||||
<div class="kpi"><div class="kpi-lbl">MÉMOIRE</div><div class="kpi-val">3.7<span class="u">Go</span></div><div class="kpi-sub">/ 8 Go · 46%</div></div>
|
||||
<div class="kpi"><div class="kpi-lbl">DISQUE</div><div class="kpi-val">62<span class="u">Go</span></div><div class="kpi-sub">/ 200 Go · 31%</div></div>
|
||||
<div class="kpi"><div class="kpi-lbl">UPTIME</div><div class="kpi-val" style="font-size:15px;color:var(--ink-1)">14j 6h</div><div class="kpi-sub">depuis boot</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sec-title">HISTORIQUE — 30 MIN</div>
|
||||
<div class="charts-grid">
|
||||
<div class="chart-card">
|
||||
<div class="chart-header"><div class="chart-label" style="color:var(--accent)"><i class="fa-solid fa-microchip"></i>CPU</div><span class="chart-cur c-ok">42%</span></div>
|
||||
<svg class="chart-svg" viewBox="0 0 200 52" preserveAspectRatio="none" id="cpu-chart"></svg>
|
||||
<div class="chart-axis"><span>−30min</span><span>−15min</span><span>now</span></div>
|
||||
</div>
|
||||
<div class="chart-card">
|
||||
<div class="chart-header"><div class="chart-label" style="color:var(--blue)"><i class="fa-solid fa-memory"></i>RAM</div><span class="chart-cur" style="color:var(--blue)">46%</span></div>
|
||||
<svg class="chart-svg" viewBox="0 0 200 52" preserveAspectRatio="none" id="ram-chart"></svg>
|
||||
<div class="chart-axis"><span>−30min</span><span>−15min</span><span>now</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sec-title">STOCKAGE</div>
|
||||
<div class="storage-block">
|
||||
<div class="det-gauges">
|
||||
<div class="dg-row"><div class="dg-ico" data-tip="Utilisé"><i class="fa-solid fa-hard-drive"></i></div><div class="dg-bar"><div class="dg-fill" style="width:31%"></div></div><span class="dg-val">62 / 200 Go</span></div>
|
||||
<div class="dg-row"><div class="dg-ico" data-tip="Libre" style="color:var(--blue)"><i class="fa-solid fa-floppy-disk"></i></div><div class="dg-bar"><div class="dg-fill b" style="width:69%"></div></div><span class="dg-val">138 Go libre</span></div>
|
||||
</div>
|
||||
<!-- Bouton SMART — affiché si smartctl disponible -->
|
||||
<div class="smart-btn ok" onclick="showSmart();event.stopPropagation()" data-tip="Voir l'état de santé complet du disque">
|
||||
<div class="smart-dot"></div>
|
||||
<span class="smart-label">SMART</span>
|
||||
<span style="font-size:10px;color:var(--ink-3)">·</span>
|
||||
<span style="font-size:11px;color:var(--ok);font-family:var(--font-terminal)">PASSED</span>
|
||||
<span class="smart-temp"><i class="fa-solid fa-temperature-half"></i> 34°C</span>
|
||||
<i class="fa-solid fa-chevron-right smart-chevron"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sec-title">INFORMATIONS</div>
|
||||
<div class="meta-grid">
|
||||
<div class="meta"><div class="meta-lbl">HOSTNAME</div><div class="meta-val">srv-prod-01</div></div>
|
||||
<div class="meta"><div class="meta-lbl">ADRESSE IP</div><div class="meta-val">10.0.0.11</div></div>
|
||||
<div class="meta"><div class="meta-lbl">PROTOCOLES ACTIFS</div><div class="proto-badges"><span class="proto-badge udp"><i class="fa-solid fa-arrow-up"></i>UDP</span><span class="proto-badge mqtt">M MQTT</span></div></div>
|
||||
<div class="meta"><div class="meta-lbl">DERNIER CONTACT</div><div class="meta-val">22:14:07</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pop-foot">
|
||||
<span class="pop-uptime"><i class="fa-solid fa-clock" style="margin-right:4px"></i>En ligne depuis 14 jours 6 heures</span>
|
||||
<span class="resize-hint"><i class="fa-solid fa-up-right-and-down-left-from-center"></i>Redimensionnable</span>
|
||||
<div class="btn-agent-cfg" onclick="showAgentCfg()" data-tip="Configurer l'agent"><i class="fa-solid fa-gears"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══ POPUP SMART ══ -->
|
||||
<div class="overlay" id="overlay-smart" style="display:none" onclick="if(event.target===this)this.style.display='none'">
|
||||
<div class="popup" id="popup-smart" onclick="event.stopPropagation()">
|
||||
<div class="smart-head">
|
||||
<div class="smart-head-icon"><i class="fa-solid fa-shield-heart"></i></div>
|
||||
<div class="smart-head-info">
|
||||
<div class="smart-head-title">Santé du disque dur</div>
|
||||
<div class="smart-head-sub">srv-prod-01 · /dev/sda · Samsung SSD 870 EVO 250GB</div>
|
||||
</div>
|
||||
<div class="pop-close" onclick="document.getElementById('overlay-smart').style.display='none'" data-tip="Fermer"><i class="fa-solid fa-xmark"></i></div>
|
||||
</div>
|
||||
|
||||
<div class="smart-body">
|
||||
<!-- Verdict global en français, compréhensible par tous -->
|
||||
<div class="smart-verdict">
|
||||
<div class="verdict-icon"><i class="fa-solid fa-circle-check"></i></div>
|
||||
<div class="verdict-text">
|
||||
<div class="v-title">Disque en bonne santé</div>
|
||||
<div class="v-sub">Aucun problème détecté. Le disque fonctionne normalement et peut être utilisé en toute confiance.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Indicateurs simplifiés -->
|
||||
<div>
|
||||
<div class="sec-title">POINTS DE CONTRÔLE PRINCIPAUX</div>
|
||||
<div class="smart-indicators">
|
||||
<div class="smart-ind">
|
||||
<div class="si-header">
|
||||
<div class="si-icon" style="color:var(--warn)"><i class="fa-solid fa-temperature-half"></i></div>
|
||||
<span class="si-title">Température</span>
|
||||
<span class="si-status ok">Normale</span>
|
||||
</div>
|
||||
<div class="si-val">34<span class="u">°C</span></div>
|
||||
<div class="si-desc">La température idéale est entre 20°C et 50°C. Au-delà de 60°C le disque risque de s'abîmer prématurément.</div>
|
||||
</div>
|
||||
<div class="smart-ind">
|
||||
<div class="si-header">
|
||||
<div class="si-icon" style="color:var(--ok)"><i class="fa-solid fa-circle-check"></i></div>
|
||||
<span class="si-title">Secteurs défectueux</span>
|
||||
<span class="si-status ok">Aucun</span>
|
||||
</div>
|
||||
<div class="si-val">0<span class="u"> sect.</span></div>
|
||||
<div class="si-desc">Les secteurs défectueux sont des zones endommagées sur le disque. S'ils apparaissent en grand nombre, une panne est imminente.</div>
|
||||
</div>
|
||||
<div class="smart-ind">
|
||||
<div class="si-header">
|
||||
<div class="si-icon" style="color:var(--blue)"><i class="fa-solid fa-clock-rotate-left"></i></div>
|
||||
<span class="si-title">Heures de fonctionnement</span>
|
||||
<span class="si-status ok">Jeune</span>
|
||||
</div>
|
||||
<div class="si-val">4<span class="u">213 h</span></div>
|
||||
<div class="si-desc">Environ 175 jours de fonctionnement. Un disque dur de bureau dure en moyenne 3 à 5 ans (26 000 – 43 000 heures).</div>
|
||||
</div>
|
||||
<div class="smart-ind">
|
||||
<div class="si-header">
|
||||
<div class="si-icon" style="color:var(--ok)"><i class="fa-solid fa-battery-full"></i></div>
|
||||
<span class="si-title">Durée de vie SSD</span>
|
||||
<span class="si-status ok">Excellente</span>
|
||||
</div>
|
||||
<div class="si-val">98<span class="u">%</span></div>
|
||||
<div class="si-desc">Indique la durée de vie restante des cellules flash (spécifique aux SSD). 100% = neuf, 0% = fin de vie recommandée.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Attributs détaillés -->
|
||||
<div>
|
||||
<div class="sec-title">ATTRIBUTS SMART DÉTAILLÉS</div>
|
||||
<div class="smart-attrs">
|
||||
<div class="attr-row">
|
||||
<span class="attr-id">001</span>
|
||||
<span class="attr-name">Taux d'erreurs de lecture brutes</span>
|
||||
<span class="attr-val attr-ok">0</span>
|
||||
<span class="attr-explain attr-ok">✓ Parfait</span>
|
||||
</div>
|
||||
<div class="attr-row">
|
||||
<span class="attr-id">005</span>
|
||||
<span class="attr-name">Secteurs réalloués (remplacés)</span>
|
||||
<span class="attr-val attr-ok">0</span>
|
||||
<span class="attr-explain attr-ok">✓ Aucun dommage</span>
|
||||
</div>
|
||||
<div class="attr-row">
|
||||
<span class="attr-id">009</span>
|
||||
<span class="attr-name">Heures de fonctionnement</span>
|
||||
<span class="attr-val attr-ok">4213</span>
|
||||
<span class="attr-explain">175 jours</span>
|
||||
</div>
|
||||
<div class="attr-row">
|
||||
<span class="attr-id">012</span>
|
||||
<span class="attr-name">Nombre de démarrages</span>
|
||||
<span class="attr-val attr-ok">312</span>
|
||||
<span class="attr-explain">Normale</span>
|
||||
</div>
|
||||
<div class="attr-row">
|
||||
<span class="attr-id">177</span>
|
||||
<span class="attr-name">Usure des cellules d'écriture (SSD)</span>
|
||||
<span class="attr-val attr-ok">2</span>
|
||||
<span class="attr-explain attr-ok">✓ Très faible</span>
|
||||
</div>
|
||||
<div class="attr-row">
|
||||
<span class="attr-id">190</span>
|
||||
<span class="attr-name">Température du disque</span>
|
||||
<span class="attr-val attr-ok">34°C</span>
|
||||
<span class="attr-explain attr-ok">✓ Normale</span>
|
||||
</div>
|
||||
<div class="attr-row">
|
||||
<span class="attr-id">197</span>
|
||||
<span class="attr-name">Secteurs instables en attente</span>
|
||||
<span class="attr-val attr-ok">0</span>
|
||||
<span class="attr-explain attr-ok">✓ Aucun</span>
|
||||
</div>
|
||||
<div class="attr-row">
|
||||
<span class="attr-id">198</span>
|
||||
<span class="attr-name">Secteurs non corrigeables</span>
|
||||
<span class="attr-val attr-ok">0</span>
|
||||
<span class="attr-explain attr-ok">✓ Aucun</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="smart-footer">
|
||||
<span class="smart-footer-note"><i class="fa-solid fa-circle-info" style="margin-right:4px"></i>Données collectées via smartctl · mise à jour à chaque actualisation de l'agent</span>
|
||||
<button class="btn primary" onclick="document.getElementById('overlay-smart').style.display='none'"><i class="fa-solid fa-xmark"></i> Fermer</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CONFIG AGENT (inchangé) -->
|
||||
<div class="overlay" id="overlay-agentcfg" style="display:none;z-index:200" onclick="if(event.target===this)this.style.display='none'">
|
||||
<div class="popup" id="popup-agentcfg" onclick="event.stopPropagation()">
|
||||
<div class="cfg-head">
|
||||
<div class="cfg-head-icon"><i class="fa-solid fa-gears"></i></div>
|
||||
<div class="cfg-head-info"><div class="cfg-head-title">Configuration de l'agent</div><div class="cfg-head-sub">srv-prod-01 · 10.0.0.11 · config récupérée à 22:14:05</div></div>
|
||||
<div class="pop-close" onclick="document.getElementById('overlay-agentcfg').style.display='none'" data-tip="Fermer"><i class="fa-solid fa-xmark"></i></div>
|
||||
</div>
|
||||
<div class="cfg-body">
|
||||
<div class="cfg-section">
|
||||
<div class="cfg-sec-title">PROTOCOLES DE TRANSPORT</div>
|
||||
<div class="check-row active"><div class="chk-box"><i class="fa-solid fa-check"></i></div><div class="chk-label"><div class="chk-name">UDP <span class="chk-badge udp">UDP</span></div><div class="chk-desc">Fire-and-forget · serveur 10.0.0.50 · port 9999</div></div></div>
|
||||
<div class="check-row mqtt-active"><div class="chk-box" style="background:var(--purple);border-color:var(--purple);color:var(--bg-0)"><i class="fa-solid fa-check"></i></div><div class="chk-label"><div class="chk-name">MQTT <span class="chk-badge mqtt">MQTT</span></div><div class="chk-desc">Bidirectionnel · broker 10.0.0.3 · port 1883</div></div></div>
|
||||
<div class="mqtt-opts">
|
||||
<div class="mqtt-field"><label>Broker</label><input class="mqtt-input" type="text" value="10.0.0.3"></div>
|
||||
<div class="mqtt-field"><label>Port</label><input class="mqtt-input" type="number" value="1883" style="width:90px;flex:none"></div>
|
||||
<div class="mqtt-field"><label>Topic base</label><input class="mqtt-input" type="text" value="nanometrics/agents"></div>
|
||||
<div style="border-top:1px solid var(--border-1);padding-top:8px;display:flex;flex-direction:column;gap:6px">
|
||||
<div class="mqtt-check-row"><label><i class="fa-solid fa-satellite-dish"></i> Auto-discovery (Home Assistant)</label><label class="toggle"><input type="checkbox" checked><span class="toggle-slider"></span></label></div>
|
||||
<div class="mqtt-check-row"><label><i class="fa-solid fa-arrow-right-to-bracket"></i> Birth message (connexion)</label><label class="toggle"><input type="checkbox" checked><span class="toggle-slider"></span></label></div>
|
||||
<div class="mqtt-check-row"><label><i class="fa-solid fa-skull"></i> Last Will message (déconnexion)</label><label class="toggle"><input type="checkbox" checked><span class="toggle-slider"></span></label></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cfg-section">
|
||||
<div class="cfg-sec-title">MÉTRIQUES ACTIVES</div>
|
||||
<div class="metrics-grid">
|
||||
<div class="metric-row"><div class="metric-ico"><i class="fa-solid fa-microchip"></i></div><span class="metric-name">cpu</span><label class="toggle tog-ok"><input type="checkbox" checked><span class="toggle-slider"></span></label></div>
|
||||
<div class="metric-row"><div class="metric-ico"><i class="fa-solid fa-memory"></i></div><span class="metric-name">memory</span><label class="toggle tog-ok"><input type="checkbox" checked><span class="toggle-slider"></span></label></div>
|
||||
<div class="metric-row"><div class="metric-ico"><i class="fa-solid fa-hard-drive"></i></div><span class="metric-name">disk</span><label class="toggle tog-ok"><input type="checkbox" checked><span class="toggle-slider"></span></label></div>
|
||||
<div class="metric-row"><div class="metric-ico"><i class="fa-solid fa-shield-heart"></i></div><span class="metric-name">smart</span><label class="toggle tog-ok"><input type="checkbox" checked><span class="toggle-slider"></span></label></div>
|
||||
<div class="metric-row"><div class="metric-ico"><i class="fa-solid fa-network-wired"></i></div><span class="metric-name">network</span><label class="toggle tog-ok"><input type="checkbox"><span class="toggle-slider"></span></label></div>
|
||||
<div class="metric-row"><div class="metric-ico"><i class="fa-solid fa-thermometer-half"></i></div><span class="metric-name">temperature</span><label class="toggle tog-ok"><input type="checkbox"><span class="toggle-slider"></span></label></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cfg-section">
|
||||
<div class="cfg-sec-title">COMMANDES DISTANTES <span style="color:var(--ink-4);font-size:8px;margin-left:6px">— BIENTÔT</span></div>
|
||||
<div class="cmds-grid">
|
||||
<div class="cmd-btn"><i class="fa-solid fa-rotate-right"></i><span>reboot</span><span class="soon-tag">bientôt</span></div>
|
||||
<div class="cmd-btn"><i class="fa-solid fa-power-off"></i><span>shutdown</span><span class="soon-tag">bientôt</span></div>
|
||||
<div class="cmd-btn"><i class="fa-solid fa-display"></i><span>screen off</span><span class="soon-tag">bientôt</span></div>
|
||||
<div class="cmd-btn"><i class="fa-solid fa-arrow-up-from-bracket"></i><span>update</span><span class="soon-tag">bientôt</span></div>
|
||||
<div class="cmd-btn"><i class="fa-solid fa-arrow-up-right-dots"></i><span>upgrade</span><span class="soon-tag">bientôt</span></div>
|
||||
<div class="cmd-btn"><i class="fa-solid fa-terminal"></i><span>shell cmd</span><span class="soon-tag">bientôt</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cfg-foot">
|
||||
<div class="cfg-status"><div class="dot"></div><span>Config synchronisée</span></div>
|
||||
<button class="btn" onclick="document.getElementById('overlay-agentcfg').style.display='none'">Annuler</button>
|
||||
<button class="btn primary"><i class="fa-solid fa-paper-plane"></i> Envoyer à l'agent</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const tip=document.getElementById('tooltip');let tt;
|
||||
document.addEventListener('mouseover',e=>{const el=e.target.closest('[data-tip]');if(!el)return;clearTimeout(tt);tt=setTimeout(()=>{tip.textContent=el.dataset.tip;tip.classList.add('show');mv(e);},120);});
|
||||
document.addEventListener('mousemove',e=>{if(tip.classList.contains('show'))mv(e);});
|
||||
document.addEventListener('mouseout',e=>{const el=e.target.closest('[data-tip]');if(!el)return;clearTimeout(tt);tip.classList.remove('show');});
|
||||
function mv(e){const w=tip.offsetWidth,h=tip.offsetHeight;let x=e.clientX-w/2,y=e.clientY-h-10;x=Math.max(6,Math.min(x,window.innerWidth-w-6));if(y<6)y=e.clientY+18;tip.style.left=x+'px';tip.style.top=y+'px';}
|
||||
|
||||
function toggleTheme(){const h=document.documentElement;h.dataset.theme=h.dataset.theme==='dark'?'light':'dark';document.getElementById('theme-icon').className=h.dataset.theme==='dark'?'fa-solid fa-moon':'fa-solid fa-sun';drawCharts();}
|
||||
function showDetail(){document.getElementById('overlay-detail').style.display='flex';drawCharts();}
|
||||
function showAgentCfg(){document.getElementById('overlay-agentcfg').style.display='flex';}
|
||||
function showSmart(){document.getElementById('overlay-smart').style.display='flex';}
|
||||
|
||||
/* Sauvegarde taille popup au resize */
|
||||
const pd=document.getElementById('popup-detail');
|
||||
const ro=new ResizeObserver(()=>{
|
||||
localStorage.setItem('popup-detail-size',JSON.stringify({w:pd.offsetWidth,h:pd.offsetHeight}));
|
||||
});
|
||||
ro.observe(pd);
|
||||
const saved=JSON.parse(localStorage.getItem('popup-detail-size')||'null');
|
||||
if(saved){pd.style.width=saved.w+'px';pd.style.height=saved.h+'px';}
|
||||
|
||||
function makeCurve(pts,stroke,fill,w,h){
|
||||
const xs=pts.map((_,i)=>(i/(pts.length-1))*w);
|
||||
const ys=pts.map(v=>h-(v/100)*(h-6)-3);
|
||||
const wy=h-(70/100)*(h-6)-3;
|
||||
let d=`M${xs[0]} ${ys[0]}`;
|
||||
for(let i=1;i<pts.length;i++){const cx=(xs[i-1]+xs[i])/2;d+=` C${cx} ${ys[i-1]},${cx} ${ys[i]},${xs[i]} ${ys[i]}`;}
|
||||
const uid=Math.random().toString(36).slice(2);
|
||||
return `<defs><linearGradient id="g${uid}" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="${fill}" stop-opacity=".4"/><stop offset="100%" stop-color="${fill}" stop-opacity=".02"/></linearGradient></defs>
|
||||
<line x1="0" y1="${wy}" x2="${w}" y2="${wy}" stroke="var(--warn)" stroke-width=".8" stroke-dasharray="3,3" opacity=".5"/>
|
||||
<path d="${d} L${xs.at(-1)} ${h} L${xs[0]} ${h}Z" fill="url(#g${uid})"/>
|
||||
<path d="${d}" fill="none" stroke="${stroke}" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<circle cx="${xs.at(-1)}" cy="${ys.at(-1)}" r="2.5" fill="${stroke}"/>`;
|
||||
}
|
||||
function drawCharts(){
|
||||
const cpu=[38,41,35,40,44,42,39,45,50,48,43,42,44,41,40,43,47,45,42,44,41,43,42,40,43,41,44,43,41,42];
|
||||
const ram=[44,44,45,45,46,46,47,47,46,46,45,46,46,47,47,46,46,45,46,46,46,46,46,46,46,46,46,46,46,46];
|
||||
const cs=getComputedStyle(document.documentElement);
|
||||
const ac=cs.getPropertyValue('--accent').trim(),bl=cs.getPropertyValue('--blue').trim();
|
||||
document.getElementById('cpu-chart').innerHTML=makeCurve(cpu,ac,ac,200,52);
|
||||
document.getElementById('ram-chart').innerHTML=makeCurve(ram,bl,bl,200,52);
|
||||
}
|
||||
window.addEventListener('load',drawCharts);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,672 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Nanometrics — v8</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Share+Tech+Mono:wght@400&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<script src="/helper.js"></script>
|
||||
<style>
|
||||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
||||
:root[data-theme="dark"]{
|
||||
--accent:#fe8019;--accent-soft:#d65d0e;--accent-glow:rgba(254,128,25,.28);
|
||||
--bg-0:#1d1813;--bg-1:#2a231d;--bg-2:#32291f;--bg-3:#3c332a;--bg-4:#4a3f33;--bg-5:#57493c;
|
||||
--ink-1:#f2e5c7;--ink-2:#d5c4a1;--ink-3:#a89984;--ink-4:#7c6f64;
|
||||
--ok:#4dbb26;--warn:#fabd2f;--err:#fb4934;--blue:#3db0d1;--purple:#c882c8;
|
||||
--border-1:rgba(255,255,255,.06);--border-2:rgba(255,255,255,.12);--border-3:rgba(255,255,255,.26);
|
||||
--tile-3d:0 1px 0 rgba(255,255,255,.08) inset,0 -1px 0 rgba(0,0,0,.3) inset,0 6px 20px rgba(0,0,0,.5);
|
||||
--tile-press:inset 0 2px 8px rgba(0,0,0,.5),inset 0 1px 3px rgba(0,0,0,.4);
|
||||
--hover-glow:0 0 0 1px var(--accent-soft),0 0 24px var(--accent-glow),0 6px 20px rgba(0,0,0,.5);
|
||||
--font-ui:'Inter',system-ui,sans-serif;--font-mono:'JetBrains Mono',monospace;--font-terminal:'Share Tech Mono',monospace;
|
||||
}
|
||||
:root[data-theme="light"]{
|
||||
--accent:#af3a03;--accent-soft:#d65d0e;--accent-glow:rgba(175,58,3,.18);
|
||||
--bg-0:#d5c4a1;--bg-1:#ebdbb2;--bg-2:#d5c4a1;--bg-3:#bdae93;--bg-4:#a89984;--bg-5:#928374;
|
||||
--ink-1:#3c3836;--ink-2:#504945;--ink-3:#665c54;--ink-4:#7c6f64;
|
||||
--ok:#3c911c;--warn:#b57614;--err:#9d0006;--blue:#2d82a3;--purple:#8c468c;
|
||||
--border-1:rgba(0,0,0,.08);--border-2:rgba(0,0,0,.15);--border-3:rgba(0,0,0,.3);
|
||||
--tile-3d:0 1px 0 rgba(255,255,255,.55) inset,0 -1px 0 rgba(0,0,0,.08) inset,0 4px 14px rgba(0,0,0,.13);
|
||||
--tile-press:inset 0 2px 6px rgba(0,0,0,.2);
|
||||
--hover-glow:0 0 0 1px var(--accent-soft),0 0 18px var(--accent-glow),0 4px 14px rgba(0,0,0,.13);
|
||||
--font-ui:'Inter',system-ui,sans-serif;--font-mono:'JetBrains Mono',monospace;--font-terminal:'Share Tech Mono',monospace;
|
||||
}
|
||||
body{background:var(--bg-1);color:var(--ink-1);font-family:var(--font-ui);font-size:13px;height:100vh;display:flex;flex-direction:column;overflow:hidden;transition:background .2s,color .2s}
|
||||
#tooltip{position:fixed;z-index:9999;pointer-events:none;background:var(--bg-0);color:var(--ink-1);border:1px solid var(--border-3);border-radius:5px;padding:4px 9px;font-size:11px;font-family:var(--font-ui);white-space:nowrap;opacity:0;transition:opacity .12s;box-shadow:0 4px 12px rgba(0,0,0,.4)}
|
||||
#tooltip.show{opacity:1}
|
||||
|
||||
/* HEADER */
|
||||
.header{background:var(--bg-2);border-bottom:1px solid var(--border-2);padding:0 20px;height:48px;display:flex;align-items:center;gap:12px;flex-shrink:0}
|
||||
.logo{display:flex;align-items:center;gap:8px}
|
||||
.logo-led{width:9px;height:9px;border-radius:50%;background:var(--accent);box-shadow:0 0 8px var(--accent-glow);animation:blink 2s infinite}
|
||||
@keyframes blink{0%,100%{opacity:1}50%{opacity:.4}}
|
||||
.logo-name{font-weight:700;font-size:14px;letter-spacing:.05em;font-family:var(--font-terminal)}
|
||||
.logo-ver{font-size:10px;color:var(--ink-4);font-family:var(--font-terminal)}
|
||||
.h-sep{width:1px;height:24px;background:var(--border-2)}
|
||||
.h-spacer{flex:1}
|
||||
.h-stats{display:flex;gap:14px}
|
||||
.h-stat{display:flex;align-items:center;gap:5px}
|
||||
.h-stat .lbl{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.06em}
|
||||
.h-stat .val{font-family:var(--font-mono);font-weight:700;font-size:13px}
|
||||
.c-ok{color:var(--ok)}.c-warn{color:var(--warn)}.c-err{color:var(--err)}.c-n{color:var(--ink-2)}
|
||||
.hbtn{width:34px;height:34px;border-radius:8px;border:1px solid var(--border-2);background:var(--bg-3);color:var(--ink-2);font-size:14px;display:flex;align-items:center;justify-content:center;cursor:pointer;user-select:none;transition:background .12s,color .12s,transform .08s,box-shadow .08s}
|
||||
.hbtn:hover{background:var(--bg-4);color:var(--accent)}
|
||||
.hbtn:active{transform:translateY(1px) scale(.96);box-shadow:var(--tile-press)}
|
||||
.hbtn.active-btn{background:var(--accent);color:var(--bg-0);border-color:var(--accent-soft)}
|
||||
|
||||
/* GRID */
|
||||
.main{flex:1;padding:14px 16px;overflow-y:auto}
|
||||
.agents-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:10px}
|
||||
.tile{background:var(--bg-3);border-radius:10px;padding:12px 14px;border:1px solid var(--border-1);box-shadow:var(--tile-3d);cursor:pointer;user-select:none;display:flex;flex-direction:column;gap:9px;transition:box-shadow .15s,transform .08s,border-color .15s}
|
||||
.tile:hover{box-shadow:var(--hover-glow);border-color:var(--accent-soft)}
|
||||
.tile:active{transform:translateY(2px) scale(.99);box-shadow:var(--tile-press)}
|
||||
.tile.t-warn{border-color:rgba(250,189,47,.3)}.tile.t-warn:hover{border-color:var(--warn);box-shadow:0 0 0 1px var(--warn),0 0 22px rgba(250,189,47,.22),0 6px 20px rgba(0,0,0,.5)}
|
||||
.tile.t-err{border-color:rgba(251,73,52,.35)}.tile.t-err:hover{border-color:var(--err);box-shadow:0 0 0 1px var(--err),0 0 22px rgba(251,73,52,.25),0 6px 20px rgba(0,0,0,.5)}
|
||||
.tile.t-off{opacity:.5;cursor:default}.tile.t-off:hover,.tile.t-off:active{box-shadow:var(--tile-3d);border-color:var(--border-1);transform:none}
|
||||
.tile-head{display:flex;align-items:center;gap:8px}
|
||||
.t-icon{width:28px;height:28px;border-radius:7px;background:var(--bg-4);display:flex;align-items:center;justify-content:center;color:var(--accent);font-size:13px;flex-shrink:0;overflow:hidden}
|
||||
.t-names{flex:1;min-width:0}
|
||||
.t-host{font-weight:600;font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||
.t-ip{font-family:var(--font-mono);font-size:10px;color:var(--ink-4)}
|
||||
.t-led{width:8px;height:8px;border-radius:50%;flex-shrink:0}
|
||||
.s-ok{background:var(--ok);box-shadow:0 0 6px var(--ok)}.s-warn{background:var(--warn);box-shadow:0 0 6px var(--warn);animation:blink 1.5s infinite}.s-err{background:var(--err);box-shadow:0 0 8px var(--err);animation:blink 1s infinite}.s-off{background:var(--ink-4)}
|
||||
.tile-gauges{display:flex;flex-direction:column;gap:5px}
|
||||
.g-row{display:flex;align-items:center;gap:7px}
|
||||
.g-ico{width:18px;height:18px;display:flex;align-items:center;justify-content:center;font-size:11px;color:var(--ink-3);flex-shrink:0;cursor:help}
|
||||
.g-bar{flex:1;height:5px;border-radius:3px;background:var(--bg-1);overflow:hidden}
|
||||
.g-fill{height:100%;border-radius:3px;background:var(--ok)}
|
||||
.g-fill.w{background:var(--warn)}.g-fill.e{background:var(--err)}
|
||||
.g-val{font-family:var(--font-mono);font-size:11px;color:var(--ink-2);width:34px;text-align:right}
|
||||
.tile-foot{font-family:var(--font-terminal);font-size:10px;color:var(--ink-4);display:flex;align-items:center;gap:5px}
|
||||
.tile-foot i{font-size:9px}
|
||||
|
||||
/* FOOTER */
|
||||
.footer{background:var(--bg-0);border-top:1px solid var(--border-2);height:26px;display:flex;align-items:center;font-family:var(--font-terminal);font-size:11px;color:var(--ink-4);flex-shrink:0}
|
||||
.f-mode{background:var(--accent);color:var(--bg-0);padding:0 12px;height:100%;display:flex;align-items:center;font-weight:700;letter-spacing:.04em}
|
||||
.f-cell{padding:0 12px;border-right:1px solid var(--border-1);display:flex;align-items:center;gap:5px;height:100%}
|
||||
.f-val{font-family:var(--font-mono);color:var(--ink-2)}.f-val.w{color:var(--warn)}
|
||||
.f-minibar{width:36px;height:4px;border-radius:2px;background:var(--bg-3);overflow:hidden}
|
||||
.f-minifill{height:100%;border-radius:2px;background:var(--ok)}.f-minifill.w{background:var(--warn)}
|
||||
.f-spacer{flex:1}.f-right{padding:0 12px;display:flex;align-items:center;gap:6px;color:var(--ink-3)}
|
||||
.f-time{font-family:var(--font-mono);color:var(--ink-2)}
|
||||
|
||||
/* OVERLAY */
|
||||
.overlay{position:fixed;inset:0;background:rgba(0,0,0,.65);z-index:100;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(2px)}
|
||||
|
||||
/* POPUP BASE */
|
||||
.popup{background:var(--bg-2);border:1px solid var(--border-3);border-radius:12px;box-shadow:0 24px 64px rgba(0,0,0,.7);display:flex;flex-direction:column;overflow:hidden}
|
||||
.pop-close{width:28px;height:28px;border-radius:6px;background:var(--bg-5);color:var(--ink-3);display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:12px;border:1px solid var(--border-1);transition:background .12s,color .12s,transform .08s;user-select:none}
|
||||
.pop-close:hover{background:var(--err);color:#fff}
|
||||
.pop-close:active{transform:translateY(1px) scale(.93);box-shadow:var(--tile-press)}
|
||||
.sec-title{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.08em;margin-bottom:8px}
|
||||
.btn{padding:6px 14px;border-radius:8px;border:1px solid var(--border-2);background:var(--bg-4);color:var(--ink-2);font-size:12px;font-family:var(--font-ui);cursor:pointer;display:flex;align-items:center;gap:6px;user-select:none;transition:background .1s,transform .08s,box-shadow .08s}
|
||||
.btn:hover{background:var(--bg-5)}.btn:active{transform:translateY(1px) scale(.97);box-shadow:var(--tile-press)}
|
||||
.btn.primary{background:var(--accent);color:var(--bg-0);border-color:var(--accent-soft);font-weight:600}.btn.primary:hover{background:var(--accent-soft)}
|
||||
|
||||
/* ══ CONFIG SERVEUR (header) ══ */
|
||||
#overlay-srvcfg{z-index:150}
|
||||
#popup-srvcfg{width:400px;max-width:96vw;max-height:88vh}
|
||||
.scfg-head{background:var(--bg-3);padding:14px 18px;border-bottom:1px solid var(--border-2);display:flex;align-items:center;gap:10px}
|
||||
.scfg-icon{width:32px;height:32px;border-radius:8px;background:var(--accent);display:flex;align-items:center;justify-content:center;color:var(--bg-0);font-size:15px}
|
||||
.scfg-title{flex:1;font-weight:700;font-size:14px}
|
||||
.scfg-body{padding:18px;display:flex;flex-direction:column;gap:14px;overflow-y:auto}
|
||||
.scfg-section{display:flex;flex-direction:column;gap:8px}
|
||||
.scfg-sec-title{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.08em;padding-bottom:6px;border-bottom:1px solid var(--border-1)}
|
||||
.scfg-row{display:flex;align-items:center;gap:10px}
|
||||
.scfg-row > label{font-size:12px;color:var(--ink-3);width:110px;flex-shrink:0;font-family:var(--font-terminal)}
|
||||
.scfg-slider{flex:1;accent-color:var(--accent)}
|
||||
.scfg-val{font-family:var(--font-mono);font-size:12px;color:var(--ink-2);width:48px;text-align:right}
|
||||
.scfg-select{flex:1;background:var(--bg-3);border:1px solid var(--border-2);border-radius:6px;color:var(--ink-1);padding:6px 10px;font-size:12px;font-family:var(--font-ui)}
|
||||
.scfg-toggle-row{display:flex;align-items:center;justify-content:space-between;padding:8px 10px;background:var(--bg-3);border-radius:7px;border:1px solid var(--border-1)}
|
||||
.scfg-toggle-row label{font-size:12px;color:var(--ink-2);display:flex;align-items:center;gap:7px;cursor:pointer}
|
||||
.scfg-toggle-row label i{color:var(--accent);font-size:12px}
|
||||
.scfg-foot{padding:12px 18px;border-top:1px solid var(--border-2);background:var(--bg-3);display:flex;gap:8px;justify-content:flex-end}
|
||||
.toggle{position:relative;width:34px;height:18px;flex-shrink:0}
|
||||
.toggle input{opacity:0;width:0;height:0}
|
||||
.toggle-slider{position:absolute;inset:0;border-radius:9px;background:var(--bg-4);border:1px solid var(--border-2);cursor:pointer;transition:background .2s}
|
||||
.toggle-slider::before{content:'';position:absolute;width:12px;height:12px;border-radius:50%;background:var(--ink-4);top:2px;left:2px;transition:transform .2s,background .2s}
|
||||
.toggle input:checked+.toggle-slider{background:rgba(254,128,25,.3);border-color:var(--accent)}
|
||||
.toggle input:checked+.toggle-slider::before{transform:translateX(16px);background:var(--accent)}
|
||||
.tog-ok input:checked+.toggle-slider{background:rgba(77,187,38,.3);border-color:var(--ok)}
|
||||
.tog-ok input:checked+.toggle-slider::before{background:var(--ok)}
|
||||
|
||||
/* ══ POPUP DÉTAIL AGENT — redimensionnable ══ */
|
||||
#popup-detail{width:560px;max-width:96vw;max-height:92vh;resize:both;overflow:hidden;min-width:400px;min-height:320px}
|
||||
#popup-detail .pop-body{overflow-y:auto;flex:1}
|
||||
.pop-head{background:var(--bg-3);padding:14px 18px;border-bottom:1px solid var(--border-2);display:flex;align-items:center;gap:12px;flex-shrink:0}
|
||||
.agent-icon-wrap{position:relative;width:44px;height:44px;border-radius:10px;flex-shrink:0;cursor:pointer;overflow:hidden;background:var(--bg-4);display:flex;align-items:center;justify-content:center;color:var(--accent);font-size:18px;border:2px solid var(--border-2);transition:border-color .15s}
|
||||
.agent-icon-wrap:hover{border-color:var(--accent)}
|
||||
.agent-icon-overlay{position:absolute;inset:0;background:rgba(0,0,0,.6);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;opacity:0;transition:opacity .15s;font-size:10px;color:#fff}
|
||||
.agent-icon-overlay i{font-size:14px}
|
||||
.agent-icon-wrap:hover .agent-icon-overlay{opacity:1}
|
||||
#icon-upload{display:none}
|
||||
.pop-head-info{flex:1}
|
||||
.pop-host{font-weight:700;font-size:15px}
|
||||
.pop-ip{font-family:var(--font-mono);font-size:11px;color:var(--ink-4)}
|
||||
.upload-hint{font-size:10px;color:var(--ink-4);font-family:var(--font-terminal);margin-top:2px}
|
||||
.pop-led{width:10px;height:10px;border-radius:50%;background:var(--ok);box-shadow:0 0 8px var(--ok);flex-shrink:0}
|
||||
.pop-body{padding:16px 18px;display:flex;flex-direction:column;gap:14px}
|
||||
.kpi-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:7px}
|
||||
.kpi{background:var(--bg-3);border-radius:8px;padding:10px 12px;border:1px solid var(--border-1);box-shadow:var(--tile-3d)}
|
||||
.kpi-lbl{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.06em}
|
||||
.kpi-val{font-family:var(--font-mono);font-size:20px;font-weight:700;line-height:1.1;margin-top:2px}
|
||||
.kpi-val .u{font-size:10px;color:var(--ink-3);font-weight:400}
|
||||
.kpi-sub{font-size:10px;color:var(--ink-4);font-family:var(--font-mono);margin-top:2px}
|
||||
.charts-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}
|
||||
.chart-card{background:var(--bg-3);border-radius:8px;padding:10px 12px;border:1px solid var(--border-1);box-shadow:var(--tile-3d)}
|
||||
.chart-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}
|
||||
.chart-label{display:flex;align-items:center;gap:6px;font-size:10px;font-family:var(--font-terminal);letter-spacing:.06em;color:var(--ink-3)}
|
||||
.chart-cur{font-family:var(--font-mono);font-size:16px;font-weight:700}
|
||||
.chart-svg{width:100%;height:52px;display:block}
|
||||
.chart-axis{display:flex;justify-content:space-between;margin-top:2px;font-family:var(--font-terminal);font-size:9px;color:var(--ink-4)}
|
||||
.storage-block{display:flex;flex-direction:column;gap:8px}
|
||||
.det-gauges{display:flex;flex-direction:column;gap:7px}
|
||||
.dg-row{display:flex;align-items:center;gap:10px}
|
||||
.dg-ico{width:22px;text-align:center;font-size:13px;cursor:help}
|
||||
.dg-bar{flex:1;height:7px;border-radius:4px;background:var(--bg-1);overflow:hidden}
|
||||
.dg-fill{height:100%;border-radius:4px;background:var(--ok)}.dg-fill.b{background:var(--blue)}
|
||||
.dg-val{font-family:var(--font-mono);font-size:12px;color:var(--ink-2);width:90px;text-align:right}
|
||||
.smart-btn{display:inline-flex;align-items:center;gap:8px;padding:7px 12px;border-radius:8px;border:1px solid var(--border-2);background:var(--bg-3);cursor:pointer;user-select:none;transition:background .12s,border-color .12s,transform .08s;font-family:var(--font-terminal);font-size:11px}
|
||||
.smart-btn:hover{background:var(--bg-4);border-color:var(--border-3)}.smart-btn:active{transform:translateY(1px);box-shadow:var(--tile-press)}
|
||||
.smart-btn.ok{border-color:rgba(77,187,38,.3);color:var(--ok)}.smart-dot{width:7px;height:7px;border-radius:50%;background:var(--ok);box-shadow:0 0 5px var(--ok)}
|
||||
.meta-grid{display:grid;grid-template-columns:1fr 1fr;gap:6px}
|
||||
.meta{background:var(--bg-3);border-radius:6px;padding:8px 10px;border:1px solid var(--border-1)}
|
||||
.meta-lbl{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.06em}
|
||||
.meta-val{font-family:var(--font-mono);font-size:12px;color:var(--ink-2);margin-top:2px}
|
||||
.proto-badges{display:flex;gap:5px;margin-top:4px}
|
||||
.proto-badge{display:inline-flex;align-items:center;gap:4px;padding:2px 7px;border-radius:999px;font-size:10px;font-family:var(--font-terminal);font-weight:600}
|
||||
.proto-badge.udp{background:rgba(61,176,209,.15);color:var(--blue);border:1px solid rgba(61,176,209,.3)}
|
||||
.proto-badge.mqtt{background:rgba(200,130,200,.15);color:var(--purple);border:1px solid rgba(200,130,200,.3)}
|
||||
.pop-foot{padding:10px 18px;border-top:1px solid var(--border-2);background:var(--bg-3);display:flex;align-items:center;gap:8px;flex-shrink:0}
|
||||
.pop-uptime{font-family:var(--font-terminal);font-size:11px;color:var(--ink-4);flex:1}
|
||||
.resize-hint{font-family:var(--font-terminal);font-size:9px;color:var(--ink-4);display:flex;align-items:center;gap:4px}
|
||||
.btn-agent-cfg{width:34px;height:34px;border-radius:8px;border:1px solid var(--border-2);background:var(--bg-4);color:var(--ink-3);font-size:15px;display:flex;align-items:center;justify-content:center;cursor:pointer;user-select:none;transition:background .12s,color .12s,transform .08s,box-shadow .08s}
|
||||
.btn-agent-cfg:hover{background:var(--bg-5);color:var(--accent)}.btn-agent-cfg:active{transform:translateY(1px) scale(.93);box-shadow:var(--tile-press)}
|
||||
|
||||
/* ══ CONFIG AGENT ══ */
|
||||
#overlay-agentcfg{z-index:200}
|
||||
#popup-agentcfg{width:520px;max-width:96vw;max-height:90vh}
|
||||
.cfg-head{background:var(--bg-3);padding:14px 18px;border-bottom:1px solid var(--border-2);display:flex;align-items:center;gap:10px}
|
||||
.cfg-head-icon{width:32px;height:32px;border-radius:8px;background:var(--bg-4);display:flex;align-items:center;justify-content:center;color:var(--accent);font-size:15px}
|
||||
.cfg-head-info{flex:1}.cfg-head-title{font-weight:700;font-size:14px}.cfg-head-sub{font-size:11px;color:var(--ink-4);font-family:var(--font-terminal)}
|
||||
.cfg-body{padding:18px;display:flex;flex-direction:column;gap:16px;overflow-y:auto;max-height:60vh}
|
||||
.cfg-section{display:flex;flex-direction:column;gap:8px}
|
||||
.cfg-sec-title{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.08em;padding-bottom:6px;border-bottom:1px solid var(--border-1)}
|
||||
/* protocoles */
|
||||
.check-row{display:flex;align-items:center;gap:10px;padding:8px 10px;border-radius:8px;background:var(--bg-3);border:1px solid var(--border-1);cursor:pointer;transition:background .12s,border-color .12s}
|
||||
.check-row.active{border-color:var(--accent-soft);background:rgba(254,128,25,.08)}
|
||||
.check-row.mqtt-active{border-color:rgba(200,130,200,.4);background:rgba(200,130,200,.07)}
|
||||
.chk-box{width:18px;height:18px;border-radius:4px;border:2px solid var(--border-3);background:var(--bg-1);display:flex;align-items:center;justify-content:center;font-size:11px;flex-shrink:0}
|
||||
.check-row.active .chk-box{background:var(--accent);border-color:var(--accent);color:var(--bg-0)}
|
||||
.check-row.mqtt-active .chk-box{background:var(--purple);border-color:var(--purple);color:var(--bg-0)}
|
||||
.chk-label{flex:1}.chk-name{font-weight:600;font-size:13px}.chk-desc{font-size:11px;color:var(--ink-4);font-family:var(--font-terminal);margin-top:1px}
|
||||
.chk-badge{font-size:10px;font-family:var(--font-terminal);font-weight:700;padding:1px 7px;border-radius:999px}
|
||||
.chk-badge.udp{background:rgba(61,176,209,.15);color:var(--blue)}.chk-badge.mqtt{background:rgba(200,130,200,.15);color:var(--purple)}
|
||||
.mqtt-opts{background:var(--bg-3);border-radius:8px;border:1px solid rgba(200,130,200,.2);padding:12px 14px;display:flex;flex-direction:column;gap:10px}
|
||||
.mqtt-field{display:flex;align-items:center;gap:10px}
|
||||
.mqtt-field label{font-size:11px;color:var(--ink-3);font-family:var(--font-terminal);width:90px;flex-shrink:0}
|
||||
.mqtt-input{flex:1;background:var(--bg-1);border:1px solid var(--border-2);border-radius:6px;color:var(--ink-1);padding:6px 10px;font-size:12px;font-family:var(--font-mono)}
|
||||
.mqtt-input:focus{outline:none;border-color:var(--purple)}
|
||||
.mqtt-check-row{display:flex;align-items:center;justify-content:space-between;padding:3px 0}
|
||||
.mqtt-check-row label{font-size:12px;color:var(--ink-2);display:flex;align-items:center;gap:7px;cursor:pointer}
|
||||
.mqtt-check-row label i{color:var(--purple);font-size:11px}
|
||||
.tog-mqtt input:checked+.toggle-slider{background:rgba(200,130,200,.3);border-color:var(--purple)}
|
||||
.tog-mqtt input:checked+.toggle-slider::before{background:var(--purple)}
|
||||
|
||||
/* ══ MÉTRIQUES — 2 cases par métrique (UDP + MQTT) ══ */
|
||||
.metrics-table{display:flex;flex-direction:column;gap:0;border:1px solid var(--border-1);border-radius:8px;overflow:hidden}
|
||||
.metrics-header{display:grid;grid-template-columns:1fr 60px 60px;background:var(--bg-4);padding:7px 12px;gap:8px;align-items:center}
|
||||
.mh-label{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.06em}
|
||||
.mh-proto{font-size:9px;font-family:var(--font-terminal);font-weight:700;letter-spacing:.04em;text-align:center}
|
||||
.mh-proto.udp{color:var(--blue)}.mh-proto.mqtt{color:var(--purple)}
|
||||
.metric-row{display:grid;grid-template-columns:1fr 60px 60px;padding:7px 12px;gap:8px;align-items:center;background:var(--bg-3);border-top:1px solid var(--border-1);transition:background .1s}
|
||||
.metric-row:hover{background:var(--bg-4)}
|
||||
.metric-cell{display:flex;align-items:center;gap:7px}
|
||||
.metric-ico{font-size:12px;color:var(--ink-3);width:16px;text-align:center}
|
||||
.metric-name{font-size:12px;color:var(--ink-2);font-family:var(--font-terminal)}
|
||||
.metric-chk{display:flex;justify-content:center}
|
||||
/* checkbox custom compact */
|
||||
.cbox{width:18px;height:18px;border-radius:4px;border:2px solid var(--border-2);background:var(--bg-1);display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:10px;color:transparent;transition:background .12s,border-color .12s,color .12s;user-select:none;flex-shrink:0}
|
||||
.cbox.udp-on{background:rgba(61,176,209,.2);border-color:var(--blue);color:var(--blue)}
|
||||
.cbox.mqtt-on{background:rgba(200,130,200,.2);border-color:var(--purple);color:var(--purple)}
|
||||
|
||||
/* commandes */
|
||||
.cmds-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:6px}
|
||||
.cmd-btn{display:flex;flex-direction:column;align-items:center;gap:4px;padding:10px 8px;border-radius:8px;background:var(--bg-3);border:1px solid var(--border-1);cursor:not-allowed;opacity:.4}
|
||||
.cmd-btn i{font-size:16px;color:var(--ink-3)}.cmd-btn span{font-size:10px;color:var(--ink-4);font-family:var(--font-terminal)}
|
||||
.soon-tag{font-size:8px;color:var(--ink-4);font-family:var(--font-terminal)}
|
||||
.cfg-foot{padding:12px 18px;border-top:1px solid var(--border-2);background:var(--bg-3);display:flex;align-items:center;gap:8px}
|
||||
.cfg-status{flex:1;display:flex;align-items:center;gap:6px;font-family:var(--font-terminal);font-size:11px;color:var(--ink-4)}
|
||||
.cfg-status .dot{width:6px;height:6px;border-radius:50%;background:var(--ok)}
|
||||
|
||||
/* SMART */
|
||||
#overlay-smart{z-index:300}
|
||||
#popup-smart{width:500px;max-width:96vw;max-height:88vh}
|
||||
.smart-head{background:var(--bg-3);padding:14px 18px;border-bottom:1px solid var(--border-2);display:flex;align-items:center;gap:10px}
|
||||
.smart-head-icon{width:32px;height:32px;border-radius:8px;background:var(--bg-4);display:flex;align-items:center;justify-content:center;color:var(--ok);font-size:15px}
|
||||
.smart-head-info{flex:1}.smart-head-title{font-weight:700;font-size:14px}.smart-head-sub{font-size:11px;color:var(--ink-4);font-family:var(--font-terminal)}
|
||||
.smart-body{padding:18px;display:flex;flex-direction:column;gap:16px;overflow-y:auto;max-height:70vh}
|
||||
.smart-verdict{display:flex;align-items:center;gap:14px;background:rgba(77,187,38,.1);border:1px solid rgba(77,187,38,.3);border-radius:10px;padding:14px 18px}
|
||||
.verdict-icon{font-size:28px;color:var(--ok)}
|
||||
.verdict-text .v-title{font-size:16px;font-weight:700;color:var(--ok)}
|
||||
.verdict-text .v-sub{font-size:12px;color:var(--ink-3);margin-top:3px}
|
||||
.smart-indicators{display:grid;grid-template-columns:1fr 1fr;gap:8px}
|
||||
.smart-ind{background:var(--bg-3);border-radius:8px;padding:12px 14px;border:1px solid var(--border-1);box-shadow:var(--tile-3d)}
|
||||
.si-header{display:flex;align-items:center;gap:8px;margin-bottom:6px}
|
||||
.si-icon{font-size:14px;width:22px;text-align:center}
|
||||
.si-title{font-weight:600;font-size:12px;flex:1}
|
||||
.si-status{font-size:10px;font-family:var(--font-terminal);font-weight:700;padding:1px 7px;border-radius:999px}
|
||||
.si-status.ok{background:rgba(77,187,38,.15);color:var(--ok)}
|
||||
.si-val{font-family:var(--font-mono);font-size:18px;font-weight:700;color:var(--ink-1)}
|
||||
.si-val .u{font-size:11px;color:var(--ink-3);font-weight:400}
|
||||
.si-desc{font-size:11px;color:var(--ink-3);margin-top:4px;line-height:1.4}
|
||||
.smart-attrs{display:flex;flex-direction:column;gap:5px}
|
||||
.attr-row{display:flex;align-items:center;gap:10px;padding:6px 10px;border-radius:6px;background:var(--bg-3);border:1px solid var(--border-1)}
|
||||
.attr-id{font-family:var(--font-mono);font-size:10px;color:var(--ink-4);width:28px;flex-shrink:0}
|
||||
.attr-name{flex:1;font-size:11px;color:var(--ink-2)}
|
||||
.attr-val{font-family:var(--font-mono);font-size:11px;font-weight:600;width:40px;text-align:right}
|
||||
.attr-explain{font-size:10px;color:var(--ink-4);width:130px;text-align:right;font-family:var(--font-terminal)}
|
||||
.attr-ok{color:var(--ok)}
|
||||
.smart-footer{padding:12px 18px;border-top:1px solid var(--border-2);background:var(--bg-3);display:flex;align-items:center;gap:8px}
|
||||
.smart-footer-note{flex:1;font-size:10px;color:var(--ink-4);font-family:var(--font-terminal)}
|
||||
|
||||
::-webkit-scrollbar{width:5px}::-webkit-scrollbar-track{background:var(--bg-1)}::-webkit-scrollbar-thumb{background:var(--bg-4);border-radius:3px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="tooltip"></div>
|
||||
|
||||
<!-- HEADER -->
|
||||
<div class="header">
|
||||
<div class="logo"><div class="logo-led"></div><span class="logo-name">NANOMETRICS</span><span class="logo-ver">v1.0</span></div>
|
||||
<div class="h-sep"></div>
|
||||
<div class="h-stats">
|
||||
<div class="h-stat"><span class="lbl">AGENTS</span><span class="val c-n">8</span></div>
|
||||
<div class="h-stat"><span class="lbl">OK</span><span class="val c-ok">5</span></div>
|
||||
<div class="h-stat"><span class="lbl">WARN</span><span class="val c-warn">2</span></div>
|
||||
<div class="h-stat"><span class="lbl">ERR</span><span class="val c-err">1</span></div>
|
||||
</div>
|
||||
<div class="h-spacer"></div>
|
||||
<div class="hbtn" onclick="toggleTheme()" data-tip="Thème clair / sombre"><i class="fa-solid fa-moon" id="theme-icon"></i></div>
|
||||
<!-- ← bouton config serveur fonctionnel -->
|
||||
<div class="hbtn" id="btn-srvcfg" onclick="showSrvCfg()" data-tip="Configuration serveur / interface"><i class="fa-solid fa-sliders"></i></div>
|
||||
</div>
|
||||
|
||||
<!-- GRID -->
|
||||
<div class="main">
|
||||
<div class="agents-grid">
|
||||
<div class="tile" onclick="showDetail()">
|
||||
<div class="tile-head"><div class="t-icon"><i class="fa-solid fa-server"></i></div><div class="t-names"><div class="t-host">srv-prod-01</div><div class="t-ip">10.0.0.11</div></div><div class="t-led s-ok"></div></div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row"><div class="g-ico" data-tip="CPU"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill" style="width:42%"></div></div><span class="g-val">42%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="RAM"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill" style="width:58%"></div></div><span class="g-val">58%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="Disque"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill" style="width:31%"></div></div><span class="g-val">31%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>14j 6h</div>
|
||||
</div>
|
||||
<div class="tile t-warn"><div class="tile-head"><div class="t-icon"><i class="fa-solid fa-server"></i></div><div class="t-names"><div class="t-host">srv-backup-02</div><div class="t-ip">10.0.0.12</div></div><div class="t-led s-warn"></div></div><div class="tile-gauges"><div class="g-row"><div class="g-ico" data-tip="CPU élevé" style="color:var(--warn)"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill w" style="width:78%"></div></div><span class="g-val" style="color:var(--warn)">78%</span></div><div class="g-row"><div class="g-ico" data-tip="RAM élevée" style="color:var(--warn)"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill w" style="width:72%"></div></div><span class="g-val" style="color:var(--warn)">72%</span></div><div class="g-row"><div class="g-ico" data-tip="Disque"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill" style="width:20%"></div></div><span class="g-val">20%</span></div></div><div class="tile-foot"><i class="fa-solid fa-clock"></i>3j 14h</div></div>
|
||||
<div class="tile t-err"><div class="tile-head"><div class="t-icon" style="color:var(--err)"><i class="fa-solid fa-microchip"></i></div><div class="t-names"><div class="t-host">rpi-sensor-03</div><div class="t-ip">10.0.0.23</div></div><div class="t-led s-err"></div></div><div class="tile-gauges"><div class="g-row"><div class="g-ico" data-tip="CPU"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill" style="width:12%"></div></div><span class="g-val">12%</span></div><div class="g-row"><div class="g-ico" data-tip="RAM"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill" style="width:38%"></div></div><span class="g-val">38%</span></div><div class="g-row"><div class="g-ico" data-tip="Disque critique !" style="color:var(--err)"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill e" style="width:94%"></div></div><span class="g-val" style="color:var(--err)">94%</span></div></div><div class="tile-foot"><i class="fa-solid fa-clock"></i>62j 1h</div></div>
|
||||
<div class="tile"><div class="tile-head"><div class="t-icon"><i class="fa-solid fa-hard-drive"></i></div><div class="t-names"><div class="t-host">nas-storage-04</div><div class="t-ip">10.0.0.30</div></div><div class="t-led s-ok"></div></div><div class="tile-gauges"><div class="g-row"><div class="g-ico" data-tip="CPU"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill" style="width:8%"></div></div><span class="g-val">8%</span></div><div class="g-row"><div class="g-ico" data-tip="RAM"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill" style="width:45%"></div></div><span class="g-val">45%</span></div><div class="g-row"><div class="g-ico" data-tip="Disque"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill" style="width:66%"></div></div><span class="g-val">66%</span></div></div><div class="tile-foot"><i class="fa-solid fa-clock"></i>128j 3h</div></div>
|
||||
<div class="tile t-off"><div class="tile-head"><div class="t-icon" style="color:var(--ink-4)"><i class="fa-solid fa-server"></i></div><div class="t-names"><div class="t-host">srv-dev-07</div><div class="t-ip">10.0.0.60</div></div><div class="t-led s-off"></div></div><div class="tile-gauges"><div class="g-row"><div class="g-ico"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"></div><span class="g-val" style="color:var(--ink-4)">—</span></div><div class="g-row"><div class="g-ico"><i class="fa-solid fa-memory"></i></div><div class="g-bar"></div><span class="g-val" style="color:var(--ink-4)">—</span></div><div class="g-row"><div class="g-ico"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"></div><span class="g-val" style="color:var(--ink-4)">—</span></div></div><div class="tile-foot" style="color:var(--err)"><i class="fa-solid fa-circle-xmark"></i>Hors ligne · vu il y a 2h</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<div class="footer">
|
||||
<div class="f-mode">LIVE</div>
|
||||
<div class="f-cell"><i class="fa-solid fa-server" style="font-size:10px"></i><span>SERVEUR</span></div>
|
||||
<div class="f-cell"><i class="fa-solid fa-microchip" style="font-size:10px"></i><span class="f-val">18%</span><div class="f-minibar"><div class="f-minifill" style="width:18%"></div></div></div>
|
||||
<div class="f-cell"><i class="fa-solid fa-memory" style="font-size:10px"></i><span class="f-val w">71%</span><div class="f-minibar"><div class="f-minifill w" style="width:71%"></div></div></div>
|
||||
<div class="f-spacer"></div>
|
||||
<div class="f-right"><i class="fa-solid fa-rotate"></i><span>Actualisation : <span class="f-time">22:14:07</span></span></div>
|
||||
</div>
|
||||
|
||||
<!-- ══ CONFIG SERVEUR (header sliders) ══ -->
|
||||
<div class="overlay" id="overlay-srvcfg" style="display:none" onclick="if(event.target===this)hideSrvCfg()">
|
||||
<div class="popup" id="popup-srvcfg" onclick="event.stopPropagation()">
|
||||
<div class="scfg-head">
|
||||
<div class="scfg-icon"><i class="fa-solid fa-sliders"></i></div>
|
||||
<span class="scfg-title">Configuration interface</span>
|
||||
<div class="pop-close" onclick="hideSrvCfg()" data-tip="Fermer"><i class="fa-solid fa-xmark"></i></div>
|
||||
</div>
|
||||
<div class="scfg-body">
|
||||
<div class="scfg-section">
|
||||
<div class="scfg-sec-title">AFFICHAGE DES TUILES</div>
|
||||
<div class="scfg-row">
|
||||
<label>Largeur min.</label>
|
||||
<input type="range" class="scfg-slider" min="160" max="420" value="220" oninput="this.nextElementSibling.textContent=this.value+'px'">
|
||||
<span class="scfg-val">220px</span>
|
||||
</div>
|
||||
<div class="scfg-row">
|
||||
<label>Taille du texte</label>
|
||||
<input type="range" class="scfg-slider" min="10" max="18" value="13" oninput="this.nextElementSibling.textContent=this.value+'px'">
|
||||
<span class="scfg-val">13px</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scfg-section">
|
||||
<div class="scfg-sec-title">SEUILS D'ALERTE PAR DÉFAUT</div>
|
||||
<div class="scfg-row">
|
||||
<label>Warning CPU/RAM</label>
|
||||
<input type="range" class="scfg-slider" min="50" max="95" value="70" oninput="this.nextElementSibling.textContent=this.value+'%'">
|
||||
<span class="scfg-val">70%</span>
|
||||
</div>
|
||||
<div class="scfg-row">
|
||||
<label>Erreur CPU/RAM</label>
|
||||
<input type="range" class="scfg-slider" min="60" max="100" value="85" oninput="this.nextElementSibling.textContent=this.value+'%'">
|
||||
<span class="scfg-val">85%</span>
|
||||
</div>
|
||||
<div class="scfg-row">
|
||||
<label>Warning Disque</label>
|
||||
<input type="range" class="scfg-slider" min="50" max="95" value="75" oninput="this.nextElementSibling.textContent=this.value+'%'">
|
||||
<span class="scfg-val">75%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scfg-section">
|
||||
<div class="scfg-sec-title">DONNÉES & RÉTENTION</div>
|
||||
<div class="scfg-row">
|
||||
<label>Historique</label>
|
||||
<select class="scfg-select"><option>7 jours</option><option selected>30 jours</option><option>90 jours</option><option>1 an</option></select>
|
||||
</div>
|
||||
<div class="scfg-row">
|
||||
<label>Courbes (durée)</label>
|
||||
<select class="scfg-select"><option>15 min</option><option selected>30 min</option><option>1 heure</option><option>6 heures</option></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scfg-section">
|
||||
<div class="scfg-sec-title">COMPORTEMENT</div>
|
||||
<div class="scfg-toggle-row">
|
||||
<label><i class="fa-solid fa-eye-slash"></i> Masquer les agents hors ligne</label>
|
||||
<label class="toggle"><input type="checkbox"><span class="toggle-slider"></span></label>
|
||||
</div>
|
||||
<div class="scfg-toggle-row">
|
||||
<label><i class="fa-solid fa-bell"></i> Notifications navigateur</label>
|
||||
<label class="toggle"><input type="checkbox" checked><span class="toggle-slider"></span></label>
|
||||
</div>
|
||||
<div class="scfg-toggle-row">
|
||||
<label><i class="fa-solid fa-up-right-and-down-left-from-center"></i> Mémoriser taille des popups</label>
|
||||
<label class="toggle"><input type="checkbox" checked><span class="toggle-slider"></span></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scfg-foot">
|
||||
<button class="btn" onclick="hideSrvCfg()">Annuler</button>
|
||||
<button class="btn primary" onclick="hideSrvCfg()"><i class="fa-solid fa-floppy-disk"></i> Sauvegarder</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══ POPUP DÉTAIL AGENT ══ -->
|
||||
<div class="overlay" id="overlay-detail" style="display:flex" onclick="if(event.target===this)this.style.display='none'">
|
||||
<div class="popup" id="popup-detail" onclick="event.stopPropagation()">
|
||||
<div class="pop-head">
|
||||
<div class="agent-icon-wrap" onclick="document.getElementById('icon-upload').click()" data-tip="Changer l'icône">
|
||||
<i class="fa-solid fa-server"></i>
|
||||
<div class="agent-icon-overlay"><i class="fa-solid fa-camera"></i><span>Changer</span></div>
|
||||
</div>
|
||||
<input type="file" id="icon-upload" accept=".svg,.jpg,.jpeg,.png,.webp">
|
||||
<div class="pop-head-info">
|
||||
<div class="pop-host">srv-prod-01</div>
|
||||
<div class="pop-ip">10.0.0.11</div>
|
||||
<div class="upload-hint">Cliquer sur l'icône pour personnaliser · SVG JPG PNG WEBP · max 128×128 px</div>
|
||||
</div>
|
||||
<div class="pop-led"></div>
|
||||
<div class="pop-close" onclick="document.getElementById('overlay-detail').style.display='none'" data-tip="Fermer"><i class="fa-solid fa-xmark"></i></div>
|
||||
</div>
|
||||
<div class="pop-body">
|
||||
<div>
|
||||
<div class="sec-title">MÉTRIQUES ACTUELLES</div>
|
||||
<div class="kpi-grid">
|
||||
<div class="kpi"><div class="kpi-lbl">CPU</div><div class="kpi-val c-ok">42<span class="u">%</span></div><div class="kpi-sub">4 cœurs</div></div>
|
||||
<div class="kpi"><div class="kpi-lbl">MÉMOIRE</div><div class="kpi-val">3.7<span class="u">Go</span></div><div class="kpi-sub">/ 8 Go · 46%</div></div>
|
||||
<div class="kpi"><div class="kpi-lbl">DISQUE</div><div class="kpi-val">62<span class="u">Go</span></div><div class="kpi-sub">/ 200 Go · 31%</div></div>
|
||||
<div class="kpi"><div class="kpi-lbl">UPTIME</div><div class="kpi-val" style="font-size:15px;color:var(--ink-1)">14j 6h</div><div class="kpi-sub">depuis boot</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sec-title">HISTORIQUE — 30 MIN</div>
|
||||
<div class="charts-grid">
|
||||
<div class="chart-card">
|
||||
<div class="chart-header"><div class="chart-label" style="color:var(--accent)"><i class="fa-solid fa-microchip"></i>CPU</div><span class="chart-cur c-ok">42%</span></div>
|
||||
<svg class="chart-svg" viewBox="0 0 200 52" preserveAspectRatio="none" id="cpu-chart"></svg>
|
||||
<div class="chart-axis"><span>−30min</span><span>−15min</span><span>now</span></div>
|
||||
</div>
|
||||
<div class="chart-card">
|
||||
<div class="chart-header"><div class="chart-label" style="color:var(--blue)"><i class="fa-solid fa-memory"></i>RAM</div><span class="chart-cur" style="color:var(--blue)">46%</span></div>
|
||||
<svg class="chart-svg" viewBox="0 0 200 52" preserveAspectRatio="none" id="ram-chart"></svg>
|
||||
<div class="chart-axis"><span>−30min</span><span>−15min</span><span>now</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sec-title">STOCKAGE</div>
|
||||
<div class="storage-block">
|
||||
<div class="det-gauges">
|
||||
<div class="dg-row"><div class="dg-ico" data-tip="Utilisé"><i class="fa-solid fa-hard-drive"></i></div><div class="dg-bar"><div class="dg-fill" style="width:31%"></div></div><span class="dg-val">62 / 200 Go</span></div>
|
||||
<div class="dg-row"><div class="dg-ico" data-tip="Libre" style="color:var(--blue)"><i class="fa-solid fa-floppy-disk"></i></div><div class="dg-bar"><div class="dg-fill b" style="width:69%"></div></div><span class="dg-val">138 Go libre</span></div>
|
||||
</div>
|
||||
<div class="smart-btn ok" onclick="showSmart();event.stopPropagation()" data-tip="Voir la santé complète du disque">
|
||||
<div class="smart-dot"></div>
|
||||
<span style="font-weight:600;letter-spacing:.04em">SMART</span>
|
||||
<span style="font-size:10px;color:var(--ink-3)">·</span>
|
||||
<span style="font-size:11px;font-family:var(--font-terminal)">PASSED</span>
|
||||
<span style="font-family:var(--font-mono);font-size:10px;color:var(--ink-3);margin-left:4px"><i class="fa-solid fa-temperature-half"></i> 34°C</span>
|
||||
<i class="fa-solid fa-chevron-right" style="font-size:10px;color:var(--ink-4);margin-left:auto"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sec-title">INFORMATIONS</div>
|
||||
<div class="meta-grid">
|
||||
<div class="meta"><div class="meta-lbl">HOSTNAME</div><div class="meta-val">srv-prod-01</div></div>
|
||||
<div class="meta"><div class="meta-lbl">ADRESSE IP</div><div class="meta-val">10.0.0.11</div></div>
|
||||
<div class="meta"><div class="meta-lbl">PROTOCOLES ACTIFS</div><div class="proto-badges"><span class="proto-badge udp"><i class="fa-solid fa-arrow-up"></i>UDP</span><span class="proto-badge mqtt">M MQTT</span></div></div>
|
||||
<div class="meta"><div class="meta-lbl">DERNIER CONTACT</div><div class="meta-val">22:14:07</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pop-foot">
|
||||
<span class="pop-uptime"><i class="fa-solid fa-clock" style="margin-right:4px"></i>En ligne depuis 14 jours 6 heures</span>
|
||||
<span class="resize-hint" data-tip="Taille sauvegardée sur le serveur"><i class="fa-solid fa-up-right-and-down-left-from-center"></i>Redimensionnable</span>
|
||||
<div class="btn-agent-cfg" onclick="showAgentCfg()" data-tip="Configurer l'agent"><i class="fa-solid fa-gears"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══ CONFIG AGENT ══ -->
|
||||
<div class="overlay" id="overlay-agentcfg" style="display:none;z-index:200" onclick="if(event.target===this)this.style.display='none'">
|
||||
<div class="popup" id="popup-agentcfg" onclick="event.stopPropagation()">
|
||||
<div class="cfg-head">
|
||||
<div class="cfg-head-icon"><i class="fa-solid fa-gears"></i></div>
|
||||
<div class="cfg-head-info"><div class="cfg-head-title">Configuration de l'agent</div><div class="cfg-head-sub">srv-prod-01 · 10.0.0.11 · config récupérée à 22:14:05</div></div>
|
||||
<div class="pop-close" onclick="this.closest('.overlay').style.display='none'" data-tip="Fermer"><i class="fa-solid fa-xmark"></i></div>
|
||||
</div>
|
||||
<div class="cfg-body">
|
||||
|
||||
<!-- PROTOCOLES -->
|
||||
<div class="cfg-section">
|
||||
<div class="cfg-sec-title">PROTOCOLES DE TRANSPORT</div>
|
||||
<div class="check-row active"><div class="chk-box"><i class="fa-solid fa-check"></i></div><div class="chk-label"><div class="chk-name">UDP <span class="chk-badge udp">UDP</span></div><div class="chk-desc">Fire-and-forget · 10.0.0.50:9999</div></div></div>
|
||||
<div class="check-row mqtt-active"><div class="chk-box" style="background:var(--purple);border-color:var(--purple);color:var(--bg-0)"><i class="fa-solid fa-check"></i></div><div class="chk-label"><div class="chk-name">MQTT <span class="chk-badge mqtt">MQTT</span></div><div class="chk-desc">Bidirectionnel · 10.0.0.3:1883</div></div></div>
|
||||
<div class="mqtt-opts">
|
||||
<div class="mqtt-field"><label>Broker</label><input class="mqtt-input" type="text" value="10.0.0.3"></div>
|
||||
<div class="mqtt-field"><label>Port</label><input class="mqtt-input" type="number" value="1883" style="width:90px;flex:none"></div>
|
||||
<div class="mqtt-field"><label>Topic base</label><input class="mqtt-input" type="text" value="nanometrics/agents"></div>
|
||||
<div style="border-top:1px solid var(--border-1);padding-top:8px;display:flex;flex-direction:column;gap:5px">
|
||||
<div class="mqtt-check-row"><label><i class="fa-solid fa-satellite-dish"></i> Auto-discovery (Home Assistant)</label><label class="toggle tog-mqtt"><input type="checkbox" checked><span class="toggle-slider"></span></label></div>
|
||||
<div class="mqtt-check-row"><label><i class="fa-solid fa-arrow-right-to-bracket"></i> Birth message</label><label class="toggle tog-mqtt"><input type="checkbox" checked><span class="toggle-slider"></span></label></div>
|
||||
<div class="mqtt-check-row"><label><i class="fa-solid fa-skull"></i> Last Will message</label><label class="toggle tog-mqtt"><input type="checkbox" checked><span class="toggle-slider"></span></label></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MÉTRIQUES — 2 cases par ligne : UDP + MQTT -->
|
||||
<div class="cfg-section">
|
||||
<div class="cfg-sec-title">MÉTRIQUES ACTIVES PAR PROTOCOLE</div>
|
||||
<div class="metrics-table">
|
||||
<div class="metrics-header">
|
||||
<span class="mh-label">MÉTRIQUE</span>
|
||||
<span class="mh-proto udp"><i class="fa-solid fa-arrow-up"></i> UDP</span>
|
||||
<span class="mh-proto mqtt">M MQTT</span>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<div class="metric-cell"><div class="metric-ico"><i class="fa-solid fa-microchip"></i></div><span class="metric-name">cpu</span></div>
|
||||
<div class="metric-chk"><div class="cbox udp-on" onclick="toggleCbox(this,'udp')" data-tip="CPU via UDP"><i class="fa-solid fa-check"></i></div></div>
|
||||
<div class="metric-chk"><div class="cbox mqtt-on" onclick="toggleCbox(this,'mqtt')" data-tip="CPU via MQTT"><i class="fa-solid fa-check"></i></div></div>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<div class="metric-cell"><div class="metric-ico"><i class="fa-solid fa-memory"></i></div><span class="metric-name">memory</span></div>
|
||||
<div class="metric-chk"><div class="cbox udp-on" onclick="toggleCbox(this,'udp')" data-tip="RAM via UDP"><i class="fa-solid fa-check"></i></div></div>
|
||||
<div class="metric-chk"><div class="cbox mqtt-on" onclick="toggleCbox(this,'mqtt')" data-tip="RAM via MQTT"><i class="fa-solid fa-check"></i></div></div>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<div class="metric-cell"><div class="metric-ico"><i class="fa-solid fa-hard-drive"></i></div><span class="metric-name">disk</span></div>
|
||||
<div class="metric-chk"><div class="cbox udp-on" onclick="toggleCbox(this,'udp')" data-tip="Disque via UDP"><i class="fa-solid fa-check"></i></div></div>
|
||||
<div class="metric-chk"><div class="cbox mqtt-on" onclick="toggleCbox(this,'mqtt')" data-tip="Disque via MQTT"><i class="fa-solid fa-check"></i></div></div>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<div class="metric-cell"><div class="metric-ico"><i class="fa-solid fa-shield-heart"></i></div><span class="metric-name">smart</span></div>
|
||||
<div class="metric-chk"><div class="cbox udp-on" onclick="toggleCbox(this,'udp')" data-tip="SMART via UDP"><i class="fa-solid fa-check"></i></div></div>
|
||||
<div class="metric-chk"><div class="cbox" onclick="toggleCbox(this,'mqtt')" data-tip="SMART via MQTT"><i class="fa-solid fa-check"></i></div></div>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<div class="metric-cell"><div class="metric-ico"><i class="fa-solid fa-clock"></i></div><span class="metric-name">uptime</span></div>
|
||||
<div class="metric-chk"><div class="cbox udp-on" onclick="toggleCbox(this,'udp')" data-tip="Uptime via UDP"><i class="fa-solid fa-check"></i></div></div>
|
||||
<div class="metric-chk"><div class="cbox mqtt-on" onclick="toggleCbox(this,'mqtt')" data-tip="Uptime via MQTT"><i class="fa-solid fa-check"></i></div></div>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<div class="metric-cell"><div class="metric-ico"><i class="fa-solid fa-network-wired"></i></div><span class="metric-name">network</span></div>
|
||||
<div class="metric-chk"><div class="cbox" onclick="toggleCbox(this,'udp')" data-tip="Réseau via UDP"><i class="fa-solid fa-check"></i></div></div>
|
||||
<div class="metric-chk"><div class="cbox" onclick="toggleCbox(this,'mqtt')" data-tip="Réseau via MQTT"><i class="fa-solid fa-check"></i></div></div>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<div class="metric-cell"><div class="metric-ico"><i class="fa-solid fa-thermometer-half"></i></div><span class="metric-name">temperature</span></div>
|
||||
<div class="metric-chk"><div class="cbox" onclick="toggleCbox(this,'udp')" data-tip="Température via UDP"><i class="fa-solid fa-check"></i></div></div>
|
||||
<div class="metric-chk"><div class="cbox mqtt-on" onclick="toggleCbox(this,'mqtt')" data-tip="Température via MQTT"><i class="fa-solid fa-check"></i></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- COMMANDES -->
|
||||
<div class="cfg-section">
|
||||
<div class="cfg-sec-title">COMMANDES DISTANTES <span style="color:var(--ink-4);font-size:8px;margin-left:6px">— BIENTÔT</span></div>
|
||||
<div class="cmds-grid">
|
||||
<div class="cmd-btn"><i class="fa-solid fa-rotate-right"></i><span>reboot</span><span class="soon-tag">bientôt</span></div>
|
||||
<div class="cmd-btn"><i class="fa-solid fa-power-off"></i><span>shutdown</span><span class="soon-tag">bientôt</span></div>
|
||||
<div class="cmd-btn"><i class="fa-solid fa-display"></i><span>screen off</span><span class="soon-tag">bientôt</span></div>
|
||||
<div class="cmd-btn"><i class="fa-solid fa-arrow-up-from-bracket"></i><span>update</span><span class="soon-tag">bientôt</span></div>
|
||||
<div class="cmd-btn"><i class="fa-solid fa-arrow-up-right-dots"></i><span>upgrade</span><span class="soon-tag">bientôt</span></div>
|
||||
<div class="cmd-btn"><i class="fa-solid fa-terminal"></i><span>shell cmd</span><span class="soon-tag">bientôt</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cfg-foot">
|
||||
<div class="cfg-status"><div class="dot"></div><span>Config synchronisée avec l'agent</span></div>
|
||||
<button class="btn" onclick="this.closest('.overlay').style.display='none'">Annuler</button>
|
||||
<button class="btn primary"><i class="fa-solid fa-paper-plane"></i> Envoyer à l'agent</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SMART -->
|
||||
<div class="overlay" id="overlay-smart" style="display:none;z-index:300" onclick="if(event.target===this)this.style.display='none'">
|
||||
<div class="popup" id="popup-smart" onclick="event.stopPropagation()">
|
||||
<div class="smart-head">
|
||||
<div class="smart-head-icon"><i class="fa-solid fa-shield-heart"></i></div>
|
||||
<div class="smart-head-info"><div class="smart-head-title">Santé du disque dur</div><div class="smart-head-sub">srv-prod-01 · /dev/sda · Samsung SSD 870 EVO 250GB</div></div>
|
||||
<div class="pop-close" onclick="this.closest('.overlay').style.display='none'" data-tip="Fermer"><i class="fa-solid fa-xmark"></i></div>
|
||||
</div>
|
||||
<div class="smart-body">
|
||||
<div class="smart-verdict">
|
||||
<div class="verdict-icon"><i class="fa-solid fa-circle-check"></i></div>
|
||||
<div class="verdict-text"><div class="v-title">Disque en bonne santé</div><div class="v-sub">Aucun problème détecté. Le disque fonctionne normalement et peut être utilisé en toute confiance.</div></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sec-title">POINTS DE CONTRÔLE</div>
|
||||
<div class="smart-indicators">
|
||||
<div class="smart-ind"><div class="si-header"><div class="si-icon" style="color:var(--warn)"><i class="fa-solid fa-temperature-half"></i></div><span class="si-title">Température</span><span class="si-status ok">Normale</span></div><div class="si-val">34<span class="u">°C</span></div><div class="si-desc">Idéal : 20-50°C. Au-delà de 60°C le disque risque de s'abîmer prématurément.</div></div>
|
||||
<div class="smart-ind"><div class="si-header"><div class="si-icon" style="color:var(--ok)"><i class="fa-solid fa-circle-check"></i></div><span class="si-title">Secteurs défectueux</span><span class="si-status ok">Aucun</span></div><div class="si-val">0<span class="u"> sect.</span></div><div class="si-desc">S'ils apparaissent en grand nombre, une panne est imminente.</div></div>
|
||||
<div class="smart-ind"><div class="si-header"><div class="si-icon" style="color:var(--blue)"><i class="fa-solid fa-clock-rotate-left"></i></div><span class="si-title">Heures de fonctionnement</span><span class="si-status ok">Jeune</span></div><div class="si-val">4<span class="u">213h</span></div><div class="si-desc">≈175 jours. Un disque dure en moyenne 3 à 5 ans.</div></div>
|
||||
<div class="smart-ind"><div class="si-header"><div class="si-icon" style="color:var(--ok)"><i class="fa-solid fa-battery-full"></i></div><span class="si-title">Durée de vie SSD</span><span class="si-status ok">Excellente</span></div><div class="si-val">98<span class="u">%</span></div><div class="si-desc">100% = neuf · 0% = fin de vie recommandée.</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sec-title">ATTRIBUTS DÉTAILLÉS</div>
|
||||
<div class="smart-attrs">
|
||||
<div class="attr-row"><span class="attr-id">001</span><span class="attr-name">Taux d'erreurs de lecture</span><span class="attr-val attr-ok">0</span><span class="attr-explain attr-ok">✓ Parfait</span></div>
|
||||
<div class="attr-row"><span class="attr-id">005</span><span class="attr-name">Secteurs réalloués</span><span class="attr-val attr-ok">0</span><span class="attr-explain attr-ok">✓ Aucun dommage</span></div>
|
||||
<div class="attr-row"><span class="attr-id">009</span><span class="attr-name">Heures de fonctionnement</span><span class="attr-val attr-ok">4213</span><span class="attr-explain">175 jours</span></div>
|
||||
<div class="attr-row"><span class="attr-id">177</span><span class="attr-name">Usure cellules SSD</span><span class="attr-val attr-ok">2</span><span class="attr-explain attr-ok">✓ Très faible</span></div>
|
||||
<div class="attr-row"><span class="attr-id">190</span><span class="attr-name">Température</span><span class="attr-val attr-ok">34°C</span><span class="attr-explain attr-ok">✓ Normale</span></div>
|
||||
<div class="attr-row"><span class="attr-id">197</span><span class="attr-name">Secteurs instables</span><span class="attr-val attr-ok">0</span><span class="attr-explain attr-ok">✓ Aucun</span></div>
|
||||
<div class="attr-row"><span class="attr-id">198</span><span class="attr-name">Secteurs non corrigeables</span><span class="attr-val attr-ok">0</span><span class="attr-explain attr-ok">✓ Aucun</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="smart-footer">
|
||||
<span class="smart-footer-note"><i class="fa-solid fa-circle-info" style="margin-right:4px"></i>Données via smartctl · actualisées à chaque cycle de l'agent</span>
|
||||
<button class="btn primary" onclick="this.closest('.overlay').style.display='none'"><i class="fa-solid fa-xmark"></i> Fermer</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/* tooltip */
|
||||
const tip=document.getElementById('tooltip');let tt;
|
||||
document.addEventListener('mouseover',e=>{const el=e.target.closest('[data-tip]');if(!el)return;clearTimeout(tt);tt=setTimeout(()=>{tip.textContent=el.dataset.tip;tip.classList.add('show');mv(e);},120);});
|
||||
document.addEventListener('mousemove',e=>{if(tip.classList.contains('show'))mv(e);});
|
||||
document.addEventListener('mouseout',e=>{const el=e.target.closest('[data-tip]');if(!el)return;clearTimeout(tt);tip.classList.remove('show');});
|
||||
function mv(e){const w=tip.offsetWidth,h=tip.offsetHeight;let x=e.clientX-w/2,y=e.clientY-h-10;x=Math.max(6,Math.min(x,window.innerWidth-w-6));if(y<6)y=e.clientY+18;tip.style.left=x+'px';tip.style.top=y+'px';}
|
||||
|
||||
/* thème */
|
||||
function toggleTheme(){const h=document.documentElement;h.dataset.theme=h.dataset.theme==='dark'?'light':'dark';document.getElementById('theme-icon').className=h.dataset.theme==='dark'?'fa-solid fa-moon':'fa-solid fa-sun';drawCharts();}
|
||||
|
||||
/* config serveur */
|
||||
function showSrvCfg(){document.getElementById('overlay-srvcfg').style.display='flex';document.getElementById('btn-srvcfg').classList.add('active-btn');}
|
||||
function hideSrvCfg(){document.getElementById('overlay-srvcfg').style.display='none';document.getElementById('btn-srvcfg').classList.remove('active-btn');}
|
||||
|
||||
/* popups */
|
||||
function showDetail(){document.getElementById('overlay-detail').style.display='flex';drawCharts();}
|
||||
function showAgentCfg(){document.getElementById('overlay-agentcfg').style.display='flex';}
|
||||
function showSmart(){document.getElementById('overlay-smart').style.display='flex';}
|
||||
|
||||
/* checkbox métrique par protocole */
|
||||
function toggleCbox(el,proto){
|
||||
const isOn=el.classList.contains(proto+'-on');
|
||||
el.classList.toggle(proto+'-on',!isOn);
|
||||
el.style.color=isOn?'transparent':'';
|
||||
}
|
||||
|
||||
/* courbes */
|
||||
function makeCurve(pts,stroke,fill,w,h){
|
||||
const xs=pts.map((_,i)=>(i/(pts.length-1))*w);
|
||||
const ys=pts.map(v=>h-(v/100)*(h-6)-3);
|
||||
const wy=h-(70/100)*(h-6)-3;
|
||||
let d=`M${xs[0]} ${ys[0]}`;
|
||||
for(let i=1;i<pts.length;i++){const cx=(xs[i-1]+xs[i])/2;d+=` C${cx} ${ys[i-1]},${cx} ${ys[i]},${xs[i]} ${ys[i]}`;}
|
||||
const uid=Math.random().toString(36).slice(2);
|
||||
return `<defs><linearGradient id="g${uid}" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="${fill}" stop-opacity=".4"/><stop offset="100%" stop-color="${fill}" stop-opacity=".02"/></linearGradient></defs>
|
||||
<line x1="0" y1="${wy}" x2="${w}" y2="${wy}" stroke="var(--warn)" stroke-width=".8" stroke-dasharray="3,3" opacity=".5"/>
|
||||
<path d="${d} L${xs.at(-1)} ${h} L${xs[0]} ${h}Z" fill="url(#g${uid})"/>
|
||||
<path d="${d}" fill="none" stroke="${stroke}" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<circle cx="${xs.at(-1)}" cy="${ys.at(-1)}" r="2.5" fill="${stroke}"/>`;
|
||||
}
|
||||
function drawCharts(){
|
||||
const cpu=[38,41,35,40,44,42,39,45,50,48,43,42,44,41,40,43,47,45,42,44,41,43,42,40,43,41,44,43,41,42];
|
||||
const ram=[44,44,45,45,46,46,47,47,46,46,45,46,46,47,47,46,46,45,46,46,46,46,46,46,46,46,46,46,46,46];
|
||||
const cs=getComputedStyle(document.documentElement);
|
||||
const ac=cs.getPropertyValue('--accent').trim(),bl=cs.getPropertyValue('--blue').trim();
|
||||
document.getElementById('cpu-chart').innerHTML=makeCurve(cpu,ac,ac,200,52);
|
||||
document.getElementById('ram-chart').innerHTML=makeCurve(ram,bl,bl,200,52);
|
||||
}
|
||||
|
||||
/* resize popup → sauvegarde serveur */
|
||||
const pd=document.getElementById('popup-detail');
|
||||
new ResizeObserver(()=>{
|
||||
/* en prod : fetch('/api/config', {method:'PUT', body: JSON.stringify({popup_detail_size:{w:pd.offsetWidth,h:pd.offsetHeight}})}) */
|
||||
console.log('Taille sauvegardée sur serveur:',pd.offsetWidth,'x',pd.offsetHeight);
|
||||
}).observe(pd);
|
||||
|
||||
window.addEventListener('load',drawCharts);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,673 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Nanometrics — v9</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Share+Tech+Mono:wght@400&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<script src="/helper.js"></script>
|
||||
<style>
|
||||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
||||
:root[data-theme="dark"]{
|
||||
--accent:#fe8019;--accent-soft:#d65d0e;--accent-glow:rgba(254,128,25,.28);
|
||||
--bg-0:#1d1813;--bg-1:#2a231d;--bg-2:#32291f;--bg-3:#3c332a;--bg-4:#4a3f33;--bg-5:#57493c;
|
||||
--ink-1:#f2e5c7;--ink-2:#d5c4a1;--ink-3:#a89984;--ink-4:#7c6f64;
|
||||
--ok:#4dbb26;--warn:#fabd2f;--err:#fb4934;--blue:#3db0d1;--purple:#c882c8;
|
||||
--border-1:rgba(255,255,255,.06);--border-2:rgba(255,255,255,.12);--border-3:rgba(255,255,255,.26);
|
||||
--tile-3d:0 1px 0 rgba(255,255,255,.08) inset,0 -1px 0 rgba(0,0,0,.3) inset,0 6px 20px rgba(0,0,0,.5);
|
||||
--tile-press:inset 0 2px 8px rgba(0,0,0,.5),inset 0 1px 3px rgba(0,0,0,.4);
|
||||
--hover-glow:0 0 0 1px var(--accent-soft),0 0 24px var(--accent-glow),0 6px 20px rgba(0,0,0,.5);
|
||||
--font-ui:'Inter',system-ui,sans-serif;--font-mono:'JetBrains Mono',monospace;--font-terminal:'Share Tech Mono',monospace;
|
||||
}
|
||||
:root[data-theme="light"]{
|
||||
--accent:#af3a03;--accent-soft:#d65d0e;--accent-glow:rgba(175,58,3,.18);
|
||||
--bg-0:#d5c4a1;--bg-1:#ebdbb2;--bg-2:#d5c4a1;--bg-3:#bdae93;--bg-4:#a89984;--bg-5:#928374;
|
||||
--ink-1:#3c3836;--ink-2:#504945;--ink-3:#665c54;--ink-4:#7c6f64;
|
||||
--ok:#3c911c;--warn:#b57614;--err:#9d0006;--blue:#2d82a3;--purple:#8c468c;
|
||||
--border-1:rgba(0,0,0,.08);--border-2:rgba(0,0,0,.15);--border-3:rgba(0,0,0,.3);
|
||||
--tile-3d:0 1px 0 rgba(255,255,255,.55) inset,0 -1px 0 rgba(0,0,0,.08) inset,0 4px 14px rgba(0,0,0,.13);
|
||||
--tile-press:inset 0 2px 6px rgba(0,0,0,.2);
|
||||
--hover-glow:0 0 0 1px var(--accent-soft),0 0 18px var(--accent-glow),0 4px 14px rgba(0,0,0,.13);
|
||||
--font-ui:'Inter',system-ui,sans-serif;--font-mono:'JetBrains Mono',monospace;--font-terminal:'Share Tech Mono',monospace;
|
||||
}
|
||||
body{background:var(--bg-1);color:var(--ink-1);font-family:var(--font-ui);font-size:13px;height:100vh;display:flex;flex-direction:column;overflow:hidden;transition:background .2s,color .2s}
|
||||
#tooltip{position:fixed;z-index:9999;pointer-events:none;background:var(--bg-0);color:var(--ink-1);border:1px solid var(--border-3);border-radius:5px;padding:4px 9px;font-size:11px;font-family:var(--font-ui);white-space:nowrap;opacity:0;transition:opacity .12s;box-shadow:0 4px 12px rgba(0,0,0,.4)}
|
||||
#tooltip.show{opacity:1}
|
||||
|
||||
/* HEADER */
|
||||
.header{background:var(--bg-2);border-bottom:1px solid var(--border-2);padding:0 20px;height:48px;display:flex;align-items:center;gap:12px;flex-shrink:0}
|
||||
.logo{display:flex;align-items:center;gap:8px}
|
||||
.logo-led{width:9px;height:9px;border-radius:50%;background:var(--accent);box-shadow:0 0 8px var(--accent-glow);animation:blink 2s infinite}
|
||||
@keyframes blink{0%,100%{opacity:1}50%{opacity:.4}}
|
||||
.logo-name{font-weight:700;font-size:14px;letter-spacing:.05em;font-family:var(--font-terminal)}
|
||||
.logo-ver{font-size:10px;color:var(--ink-4);font-family:var(--font-terminal)}
|
||||
.h-sep{width:1px;height:24px;background:var(--border-2)}
|
||||
.h-spacer{flex:1}
|
||||
.h-stats{display:flex;gap:14px}
|
||||
.h-stat{display:flex;align-items:center;gap:5px}
|
||||
.h-stat .lbl{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.06em}
|
||||
.h-stat .val{font-family:var(--font-mono);font-weight:700;font-size:13px}
|
||||
.c-ok{color:var(--ok)}.c-warn{color:var(--warn)}.c-err{color:var(--err)}.c-n{color:var(--ink-2)}
|
||||
/* user-select:none uniquement sur les éléments interactifs qui ne doivent pas être sélectionnés */
|
||||
.hbtn{width:34px;height:34px;border-radius:8px;border:1px solid var(--border-2);background:var(--bg-3);color:var(--ink-2);font-size:14px;display:flex;align-items:center;justify-content:center;cursor:pointer;user-select:none;transition:background .12s,color .12s,transform .08s,box-shadow .08s}
|
||||
.hbtn:hover{background:var(--bg-4);color:var(--accent)}
|
||||
.hbtn:active{transform:translateY(1px) scale(.96);box-shadow:var(--tile-press)}
|
||||
.hbtn.active-btn{background:var(--accent);color:var(--bg-0);border-color:var(--accent-soft)}
|
||||
|
||||
/* GRID */
|
||||
.main{flex:1;padding:14px 16px;overflow-y:auto}
|
||||
.agents-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:10px}
|
||||
/* user-select retiré du .tile — le texte (hostname, IP, valeurs) doit rester sélectionnable */
|
||||
.tile{background:var(--bg-3);border-radius:10px;padding:12px 14px;border:1px solid var(--border-1);box-shadow:var(--tile-3d);cursor:pointer;display:flex;flex-direction:column;gap:9px;transition:box-shadow .15s,transform .08s,border-color .15s}
|
||||
.tile:hover{box-shadow:var(--hover-glow);border-color:var(--accent-soft)}
|
||||
.tile:active{transform:translateY(2px) scale(.99);box-shadow:var(--tile-press)}
|
||||
.tile.t-warn{border-color:rgba(250,189,47,.3)}.tile.t-warn:hover{border-color:var(--warn);box-shadow:0 0 0 1px var(--warn),0 0 22px rgba(250,189,47,.22),0 6px 20px rgba(0,0,0,.5)}
|
||||
.tile.t-err{border-color:rgba(251,73,52,.35)}.tile.t-err:hover{border-color:var(--err);box-shadow:0 0 0 1px var(--err),0 0 22px rgba(251,73,52,.25),0 6px 20px rgba(0,0,0,.5)}
|
||||
.tile.t-off{opacity:.5;cursor:default}.tile.t-off:hover,.tile.t-off:active{box-shadow:var(--tile-3d);border-color:var(--border-1);transform:none}
|
||||
.tile-head{display:flex;align-items:center;gap:8px;user-select:none}
|
||||
.t-icon{width:28px;height:28px;border-radius:7px;background:var(--bg-4);display:flex;align-items:center;justify-content:center;color:var(--accent);font-size:13px;flex-shrink:0;overflow:hidden}
|
||||
.t-names{flex:1;min-width:0}
|
||||
.t-host{font-weight:600;font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||
.t-ip{font-family:var(--font-mono);font-size:10px;color:var(--ink-4)}
|
||||
.t-led{width:8px;height:8px;border-radius:50%;flex-shrink:0}
|
||||
.s-ok{background:var(--ok);box-shadow:0 0 6px var(--ok)}.s-warn{background:var(--warn);box-shadow:0 0 6px var(--warn);animation:blink 1.5s infinite}.s-err{background:var(--err);box-shadow:0 0 8px var(--err);animation:blink 1s infinite}.s-off{background:var(--ink-4)}
|
||||
.tile-gauges{display:flex;flex-direction:column;gap:5px}
|
||||
.g-row{display:flex;align-items:center;gap:7px}
|
||||
.g-ico{width:18px;height:18px;display:flex;align-items:center;justify-content:center;font-size:11px;color:var(--ink-3);flex-shrink:0;cursor:help}
|
||||
.g-bar{flex:1;height:5px;border-radius:3px;background:var(--bg-1);overflow:hidden}
|
||||
.g-fill{height:100%;border-radius:3px;background:var(--ok)}
|
||||
.g-fill.w{background:var(--warn)}.g-fill.e{background:var(--err)}
|
||||
.g-val{font-family:var(--font-mono);font-size:11px;color:var(--ink-2);width:34px;text-align:right}
|
||||
.tile-foot{font-family:var(--font-terminal);font-size:10px;color:var(--ink-4);display:flex;align-items:center;gap:5px;user-select:none}
|
||||
.tile-foot i{font-size:9px}
|
||||
|
||||
/* FOOTER */
|
||||
.footer{background:var(--bg-0);border-top:1px solid var(--border-2);height:26px;display:flex;align-items:center;font-family:var(--font-terminal);font-size:11px;color:var(--ink-4);flex-shrink:0}
|
||||
.f-mode{background:var(--accent);color:var(--bg-0);padding:0 12px;height:100%;display:flex;align-items:center;font-weight:700;letter-spacing:.04em;user-select:none}
|
||||
.f-cell{padding:0 12px;border-right:1px solid var(--border-1);display:flex;align-items:center;gap:5px;height:100%}
|
||||
.f-val{font-family:var(--font-mono);color:var(--ink-2)}.f-val.w{color:var(--warn)}
|
||||
.f-minibar{width:36px;height:4px;border-radius:2px;background:var(--bg-3);overflow:hidden}
|
||||
.f-minifill{height:100%;border-radius:2px;background:var(--ok)}.f-minifill.w{background:var(--warn)}
|
||||
.f-spacer{flex:1}.f-right{padding:0 12px;display:flex;align-items:center;gap:6px;color:var(--ink-3)}
|
||||
.f-time{font-family:var(--font-mono);color:var(--ink-2)}
|
||||
|
||||
/* OVERLAY */
|
||||
.overlay{position:fixed;inset:0;background:rgba(0,0,0,.65);z-index:100;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(2px)}
|
||||
|
||||
/* POPUP BASE */
|
||||
.popup{background:var(--bg-2);border:1px solid var(--border-3);border-radius:12px;box-shadow:0 24px 64px rgba(0,0,0,.7);display:flex;flex-direction:column;overflow:hidden}
|
||||
.pop-close{width:28px;height:28px;border-radius:6px;background:var(--bg-5);color:var(--ink-3);display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:12px;border:1px solid var(--border-1);user-select:none;transition:background .12s,color .12s,transform .08s}
|
||||
.pop-close:hover{background:var(--err);color:#fff}
|
||||
.pop-close:active{transform:translateY(1px) scale(.93);box-shadow:var(--tile-press)}
|
||||
.sec-title{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.08em;margin-bottom:8px}
|
||||
.btn{padding:6px 14px;border-radius:8px;border:1px solid var(--border-2);background:var(--bg-4);color:var(--ink-2);font-size:12px;font-family:var(--font-ui);cursor:pointer;display:flex;align-items:center;gap:6px;user-select:none;transition:background .1s,transform .08s,box-shadow .08s}
|
||||
.btn:hover{background:var(--bg-5)}.btn:active{transform:translateY(1px) scale(.97);box-shadow:var(--tile-press)}
|
||||
.btn.primary{background:var(--accent);color:var(--bg-0);border-color:var(--accent-soft);font-weight:600}.btn.primary:hover{background:var(--accent-soft)}
|
||||
|
||||
/* ══ CONFIG SERVEUR (header) ══ */
|
||||
#overlay-srvcfg{z-index:150}
|
||||
#popup-srvcfg{width:400px;max-width:96vw;max-height:88vh}
|
||||
.scfg-head{background:var(--bg-3);padding:14px 18px;border-bottom:1px solid var(--border-2);display:flex;align-items:center;gap:10px}
|
||||
.scfg-icon{width:32px;height:32px;border-radius:8px;background:var(--accent);display:flex;align-items:center;justify-content:center;color:var(--bg-0);font-size:15px}
|
||||
.scfg-title{flex:1;font-weight:700;font-size:14px}
|
||||
.scfg-body{padding:18px;display:flex;flex-direction:column;gap:14px;overflow-y:auto}
|
||||
.scfg-section{display:flex;flex-direction:column;gap:8px}
|
||||
.scfg-sec-title{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.08em;padding-bottom:6px;border-bottom:1px solid var(--border-1)}
|
||||
.scfg-row{display:flex;align-items:center;gap:10px}
|
||||
.scfg-row > label{font-size:12px;color:var(--ink-3);width:110px;flex-shrink:0;font-family:var(--font-terminal)}
|
||||
.scfg-slider{flex:1;accent-color:var(--accent)}
|
||||
.scfg-val{font-family:var(--font-mono);font-size:12px;color:var(--ink-2);width:48px;text-align:right}
|
||||
.scfg-select{flex:1;background:var(--bg-3);border:1px solid var(--border-2);border-radius:6px;color:var(--ink-1);padding:6px 10px;font-size:12px;font-family:var(--font-ui)}
|
||||
.scfg-toggle-row{display:flex;align-items:center;justify-content:space-between;padding:8px 10px;background:var(--bg-3);border-radius:7px;border:1px solid var(--border-1)}
|
||||
.scfg-toggle-row label{font-size:12px;color:var(--ink-2);display:flex;align-items:center;gap:7px;cursor:pointer}
|
||||
.scfg-toggle-row label i{color:var(--accent);font-size:12px}
|
||||
.scfg-foot{padding:12px 18px;border-top:1px solid var(--border-2);background:var(--bg-3);display:flex;gap:8px;justify-content:flex-end}
|
||||
.toggle{position:relative;width:34px;height:18px;flex-shrink:0}
|
||||
.toggle input{opacity:0;width:0;height:0}
|
||||
.toggle-slider{position:absolute;inset:0;border-radius:9px;background:var(--bg-4);border:1px solid var(--border-2);cursor:pointer;transition:background .2s}
|
||||
.toggle-slider::before{content:'';position:absolute;width:12px;height:12px;border-radius:50%;background:var(--ink-4);top:2px;left:2px;transition:transform .2s,background .2s}
|
||||
.toggle input:checked+.toggle-slider{background:rgba(254,128,25,.3);border-color:var(--accent)}
|
||||
.toggle input:checked+.toggle-slider::before{transform:translateX(16px);background:var(--accent)}
|
||||
.tog-ok input:checked+.toggle-slider{background:rgba(77,187,38,.3);border-color:var(--ok)}
|
||||
.tog-ok input:checked+.toggle-slider::before{background:var(--ok)}
|
||||
|
||||
/* ══ POPUP DÉTAIL AGENT — redimensionnable ══ */
|
||||
#popup-detail{width:560px;max-width:96vw;max-height:92vh;resize:both;overflow:hidden;min-width:400px;min-height:320px}
|
||||
#popup-detail .pop-body{overflow-y:auto;flex:1}
|
||||
.pop-head{background:var(--bg-3);padding:14px 18px;border-bottom:1px solid var(--border-2);display:flex;align-items:center;gap:12px;flex-shrink:0}
|
||||
.agent-icon-wrap{position:relative;width:44px;height:44px;border-radius:10px;flex-shrink:0;cursor:pointer;overflow:hidden;background:var(--bg-4);display:flex;align-items:center;justify-content:center;color:var(--accent);font-size:18px;border:2px solid var(--border-2);transition:border-color .15s}
|
||||
.agent-icon-wrap:hover{border-color:var(--accent)}
|
||||
.agent-icon-overlay{position:absolute;inset:0;background:rgba(0,0,0,.6);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;opacity:0;transition:opacity .15s;font-size:10px;color:#fff}
|
||||
.agent-icon-overlay i{font-size:14px}
|
||||
.agent-icon-wrap:hover .agent-icon-overlay{opacity:1}
|
||||
#icon-upload{display:none}
|
||||
.pop-head-info{flex:1}
|
||||
.pop-host{font-weight:700;font-size:15px}
|
||||
.pop-ip{font-family:var(--font-mono);font-size:11px;color:var(--ink-4)}
|
||||
.upload-hint{font-size:10px;color:var(--ink-4);font-family:var(--font-terminal);margin-top:2px}
|
||||
.pop-led{width:10px;height:10px;border-radius:50%;background:var(--ok);box-shadow:0 0 8px var(--ok);flex-shrink:0}
|
||||
.pop-body{padding:16px 18px;display:flex;flex-direction:column;gap:14px}
|
||||
.kpi-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:7px}
|
||||
.kpi{background:var(--bg-3);border-radius:8px;padding:10px 12px;border:1px solid var(--border-1);box-shadow:var(--tile-3d)}
|
||||
.kpi-lbl{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.06em}
|
||||
.kpi-val{font-family:var(--font-mono);font-size:20px;font-weight:700;line-height:1.1;margin-top:2px}
|
||||
.kpi-val .u{font-size:10px;color:var(--ink-3);font-weight:400}
|
||||
.kpi-sub{font-size:10px;color:var(--ink-4);font-family:var(--font-mono);margin-top:2px}
|
||||
.charts-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}
|
||||
.chart-card{background:var(--bg-3);border-radius:8px;padding:10px 12px;border:1px solid var(--border-1);box-shadow:var(--tile-3d)}
|
||||
.chart-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}
|
||||
.chart-label{display:flex;align-items:center;gap:6px;font-size:10px;font-family:var(--font-terminal);letter-spacing:.06em;color:var(--ink-3)}
|
||||
.chart-cur{font-family:var(--font-mono);font-size:16px;font-weight:700}
|
||||
.chart-svg{width:100%;height:52px;display:block}
|
||||
.chart-axis{display:flex;justify-content:space-between;margin-top:2px;font-family:var(--font-terminal);font-size:9px;color:var(--ink-4)}
|
||||
.storage-block{display:flex;flex-direction:column;gap:8px}
|
||||
.det-gauges{display:flex;flex-direction:column;gap:7px}
|
||||
.dg-row{display:flex;align-items:center;gap:10px}
|
||||
.dg-ico{width:22px;text-align:center;font-size:13px;cursor:help}
|
||||
.dg-bar{flex:1;height:7px;border-radius:4px;background:var(--bg-1);overflow:hidden}
|
||||
.dg-fill{height:100%;border-radius:4px;background:var(--ok)}.dg-fill.b{background:var(--blue)}
|
||||
.dg-val{font-family:var(--font-mono);font-size:12px;color:var(--ink-2);width:90px;text-align:right}
|
||||
/* user-select retiré du smart-btn — "SMART PASSED 34°C" doit être sélectionnable */
|
||||
.smart-btn{display:inline-flex;align-items:center;gap:8px;padding:7px 12px;border-radius:8px;border:1px solid var(--border-2);background:var(--bg-3);cursor:pointer;transition:background .12s,border-color .12s,transform .08s;font-family:var(--font-terminal);font-size:11px}
|
||||
.smart-btn:hover{background:var(--bg-4);border-color:var(--border-3)}.smart-btn:active{transform:translateY(1px);box-shadow:var(--tile-press)}
|
||||
.smart-btn.ok{border-color:rgba(77,187,38,.3);color:var(--ok)}.smart-dot{width:7px;height:7px;border-radius:50%;background:var(--ok);box-shadow:0 0 5px var(--ok)}
|
||||
.meta-grid{display:grid;grid-template-columns:1fr 1fr;gap:6px}
|
||||
.meta{background:var(--bg-3);border-radius:6px;padding:8px 10px;border:1px solid var(--border-1)}
|
||||
.meta-lbl{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.06em}
|
||||
.meta-val{font-family:var(--font-mono);font-size:12px;color:var(--ink-2);margin-top:2px}
|
||||
.proto-badges{display:flex;gap:5px;margin-top:4px}
|
||||
.proto-badge{display:inline-flex;align-items:center;gap:4px;padding:2px 7px;border-radius:999px;font-size:10px;font-family:var(--font-terminal);font-weight:600}
|
||||
.proto-badge.udp{background:rgba(61,176,209,.15);color:var(--blue);border:1px solid rgba(61,176,209,.3)}
|
||||
.proto-badge.mqtt{background:rgba(200,130,200,.15);color:var(--purple);border:1px solid rgba(200,130,200,.3)}
|
||||
.pop-foot{padding:10px 18px;border-top:1px solid var(--border-2);background:var(--bg-3);display:flex;align-items:center;gap:8px;flex-shrink:0}
|
||||
.pop-uptime{font-family:var(--font-terminal);font-size:11px;color:var(--ink-4);flex:1}
|
||||
.resize-hint{font-family:var(--font-terminal);font-size:9px;color:var(--ink-4);display:flex;align-items:center;gap:4px}
|
||||
.btn-agent-cfg{width:34px;height:34px;border-radius:8px;border:1px solid var(--border-2);background:var(--bg-4);color:var(--ink-3);font-size:15px;display:flex;align-items:center;justify-content:center;cursor:pointer;user-select:none;transition:background .12s,color .12s,transform .08s,box-shadow .08s}
|
||||
.btn-agent-cfg:hover{background:var(--bg-5);color:var(--accent)}.btn-agent-cfg:active{transform:translateY(1px) scale(.93);box-shadow:var(--tile-press)}
|
||||
|
||||
/* ══ CONFIG AGENT ══ */
|
||||
#overlay-agentcfg{z-index:200}
|
||||
#popup-agentcfg{width:520px;max-width:96vw;max-height:90vh}
|
||||
.cfg-head{background:var(--bg-3);padding:14px 18px;border-bottom:1px solid var(--border-2);display:flex;align-items:center;gap:10px}
|
||||
.cfg-head-icon{width:32px;height:32px;border-radius:8px;background:var(--bg-4);display:flex;align-items:center;justify-content:center;color:var(--accent);font-size:15px}
|
||||
.cfg-head-info{flex:1}.cfg-head-title{font-weight:700;font-size:14px}.cfg-head-sub{font-size:11px;color:var(--ink-4);font-family:var(--font-terminal)}
|
||||
.cfg-body{padding:18px;display:flex;flex-direction:column;gap:16px;overflow-y:auto;max-height:62vh}
|
||||
.cfg-section{display:flex;flex-direction:column;gap:8px}
|
||||
.cfg-sec-title{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.08em;padding-bottom:6px;border-bottom:1px solid var(--border-1)}
|
||||
/* paramètres MQTT */
|
||||
.mqtt-opts{background:var(--bg-3);border-radius:8px;border:1px solid rgba(200,130,200,.2);padding:12px 14px;display:flex;flex-direction:column;gap:10px}
|
||||
.mqtt-field{display:flex;align-items:center;gap:10px}
|
||||
.mqtt-field label{font-size:11px;color:var(--ink-3);font-family:var(--font-terminal);width:90px;flex-shrink:0}
|
||||
.mqtt-input{flex:1;background:var(--bg-1);border:1px solid var(--border-2);border-radius:6px;color:var(--ink-1);padding:6px 10px;font-size:12px;font-family:var(--font-mono)}
|
||||
.mqtt-input:focus{outline:none;border-color:var(--purple)}
|
||||
.mqtt-check-row{display:flex;align-items:center;justify-content:space-between;padding:3px 0}
|
||||
.mqtt-check-row label{font-size:12px;color:var(--ink-2);display:flex;align-items:center;gap:7px;cursor:pointer}
|
||||
.mqtt-check-row label i{color:var(--purple);font-size:11px}
|
||||
.tog-mqtt input:checked+.toggle-slider{background:rgba(200,130,200,.3);border-color:var(--purple)}
|
||||
.tog-mqtt input:checked+.toggle-slider::before{background:var(--purple)}
|
||||
|
||||
/* ══ MÉTRIQUES — tableau 3 colonnes : métrique / UDP / MQTT ══ */
|
||||
.metrics-table{display:flex;flex-direction:column;gap:0;border:1px solid var(--border-2);border-radius:8px;overflow:hidden}
|
||||
.metrics-header{display:grid;grid-template-columns:1fr 56px 56px;background:var(--bg-4);padding:8px 12px;gap:4px;align-items:center}
|
||||
.mh-label{font-size:9px;color:var(--ink-4);font-family:var(--font-terminal);letter-spacing:.06em}
|
||||
.mh-proto{font-size:9px;font-family:var(--font-terminal);font-weight:700;letter-spacing:.04em;text-align:center;display:flex;flex-direction:column;align-items:center;gap:2px}
|
||||
.mh-proto.udp{color:var(--blue)}.mh-proto.mqtt{color:var(--purple)}
|
||||
.mh-proto .proto-dot{width:5px;height:5px;border-radius:50%;margin-top:1px}
|
||||
.mh-proto.udp .proto-dot{background:var(--blue)}.mh-proto.mqtt .proto-dot{background:var(--purple)}
|
||||
.metric-row{display:grid;grid-template-columns:1fr 56px 56px;padding:8px 12px;gap:4px;align-items:center;background:var(--bg-3);border-top:1px solid var(--border-1);transition:background .1s}
|
||||
.metric-row:hover{background:var(--bg-4)}
|
||||
.metric-cell{display:flex;align-items:center;gap:8px}
|
||||
.metric-ico{font-size:13px;color:var(--ink-3);width:16px;text-align:center}
|
||||
.metric-name{font-size:12px;color:var(--ink-2);font-family:var(--font-terminal)}
|
||||
.metric-chk{display:flex;justify-content:center}
|
||||
/* checkbox compact — user-select:none pour éviter la sélection au clic rapide */
|
||||
.cbox{width:20px;height:20px;border-radius:5px;border:2px solid var(--border-2);background:var(--bg-1);display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:11px;color:transparent;transition:background .12s,border-color .12s,color .12s,transform .08s;user-select:none;flex-shrink:0}
|
||||
.cbox:hover{border-color:var(--border-3);transform:scale(1.08)}
|
||||
.cbox:active{transform:scale(.93)}
|
||||
.cbox.udp-on{background:rgba(61,176,209,.18);border-color:var(--blue);color:var(--blue)}
|
||||
.cbox.mqtt-on{background:rgba(200,130,200,.18);border-color:var(--purple);color:var(--purple)}
|
||||
|
||||
/* commandes */
|
||||
.cmds-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:6px}
|
||||
.cmd-btn{display:flex;flex-direction:column;align-items:center;gap:4px;padding:10px 8px;border-radius:8px;background:var(--bg-3);border:1px solid var(--border-1);cursor:not-allowed;opacity:.4}
|
||||
.cmd-btn i{font-size:16px;color:var(--ink-3)}.cmd-btn span{font-size:10px;color:var(--ink-4);font-family:var(--font-terminal)}
|
||||
.soon-tag{font-size:8px;color:var(--ink-4);font-family:var(--font-terminal)}
|
||||
.cfg-foot{padding:12px 18px;border-top:1px solid var(--border-2);background:var(--bg-3);display:flex;align-items:center;gap:8px}
|
||||
.cfg-status{flex:1;display:flex;align-items:center;gap:6px;font-family:var(--font-terminal);font-size:11px;color:var(--ink-4)}
|
||||
.cfg-status .dot{width:6px;height:6px;border-radius:50%;background:var(--ok)}
|
||||
|
||||
/* SMART */
|
||||
#overlay-smart{z-index:300}
|
||||
#popup-smart{width:500px;max-width:96vw;max-height:88vh}
|
||||
.smart-head{background:var(--bg-3);padding:14px 18px;border-bottom:1px solid var(--border-2);display:flex;align-items:center;gap:10px}
|
||||
.smart-head-icon{width:32px;height:32px;border-radius:8px;background:var(--bg-4);display:flex;align-items:center;justify-content:center;color:var(--ok);font-size:15px}
|
||||
.smart-head-info{flex:1}.smart-head-title{font-weight:700;font-size:14px}.smart-head-sub{font-size:11px;color:var(--ink-4);font-family:var(--font-terminal)}
|
||||
.smart-body{padding:18px;display:flex;flex-direction:column;gap:16px;overflow-y:auto;max-height:70vh}
|
||||
.smart-verdict{display:flex;align-items:center;gap:14px;background:rgba(77,187,38,.1);border:1px solid rgba(77,187,38,.3);border-radius:10px;padding:14px 18px}
|
||||
.verdict-icon{font-size:28px;color:var(--ok)}
|
||||
.verdict-text .v-title{font-size:16px;font-weight:700;color:var(--ok)}
|
||||
.verdict-text .v-sub{font-size:12px;color:var(--ink-3);margin-top:3px}
|
||||
.smart-indicators{display:grid;grid-template-columns:1fr 1fr;gap:8px}
|
||||
.smart-ind{background:var(--bg-3);border-radius:8px;padding:12px 14px;border:1px solid var(--border-1);box-shadow:var(--tile-3d)}
|
||||
.si-header{display:flex;align-items:center;gap:8px;margin-bottom:6px}
|
||||
.si-icon{font-size:14px;width:22px;text-align:center}
|
||||
.si-title{font-weight:600;font-size:12px;flex:1}
|
||||
.si-status{font-size:10px;font-family:var(--font-terminal);font-weight:700;padding:1px 7px;border-radius:999px}
|
||||
.si-status.ok{background:rgba(77,187,38,.15);color:var(--ok)}
|
||||
.si-val{font-family:var(--font-mono);font-size:18px;font-weight:700;color:var(--ink-1)}
|
||||
.si-val .u{font-size:11px;color:var(--ink-3);font-weight:400}
|
||||
.si-desc{font-size:11px;color:var(--ink-3);margin-top:4px;line-height:1.4}
|
||||
.smart-attrs{display:flex;flex-direction:column;gap:5px}
|
||||
.attr-row{display:flex;align-items:center;gap:10px;padding:6px 10px;border-radius:6px;background:var(--bg-3);border:1px solid var(--border-1)}
|
||||
.attr-id{font-family:var(--font-mono);font-size:10px;color:var(--ink-4);width:28px;flex-shrink:0}
|
||||
.attr-name{flex:1;font-size:11px;color:var(--ink-2)}
|
||||
.attr-val{font-family:var(--font-mono);font-size:11px;font-weight:600;width:40px;text-align:right}
|
||||
.attr-explain{font-size:10px;color:var(--ink-4);width:130px;text-align:right;font-family:var(--font-terminal)}
|
||||
.attr-ok{color:var(--ok)}
|
||||
.smart-footer{padding:12px 18px;border-top:1px solid var(--border-2);background:var(--bg-3);display:flex;align-items:center;gap:8px}
|
||||
.smart-footer-note{flex:1;font-size:10px;color:var(--ink-4);font-family:var(--font-terminal)}
|
||||
|
||||
::-webkit-scrollbar{width:5px}::-webkit-scrollbar-track{background:var(--bg-1)}::-webkit-scrollbar-thumb{background:var(--bg-4);border-radius:3px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="tooltip"></div>
|
||||
|
||||
<!-- HEADER -->
|
||||
<div class="header">
|
||||
<div class="logo"><div class="logo-led"></div><span class="logo-name">NANOMETRICS</span><span class="logo-ver">v1.0</span></div>
|
||||
<div class="h-sep"></div>
|
||||
<div class="h-stats">
|
||||
<div class="h-stat"><span class="lbl">AGENTS</span><span class="val c-n">8</span></div>
|
||||
<div class="h-stat"><span class="lbl">OK</span><span class="val c-ok">5</span></div>
|
||||
<div class="h-stat"><span class="lbl">WARN</span><span class="val c-warn">2</span></div>
|
||||
<div class="h-stat"><span class="lbl">ERR</span><span class="val c-err">1</span></div>
|
||||
</div>
|
||||
<div class="h-spacer"></div>
|
||||
<div class="hbtn" onclick="toggleTheme()" data-tip="Thème clair / sombre"><i class="fa-solid fa-moon" id="theme-icon"></i></div>
|
||||
<div class="hbtn" id="btn-srvcfg" onclick="showSrvCfg()" data-tip="Configuration serveur / interface"><i class="fa-solid fa-sliders"></i></div>
|
||||
</div>
|
||||
|
||||
<!-- GRID -->
|
||||
<div class="main">
|
||||
<div class="agents-grid">
|
||||
<div class="tile" onclick="showDetail()">
|
||||
<div class="tile-head"><div class="t-icon"><i class="fa-solid fa-server"></i></div><div class="t-names"><div class="t-host">srv-prod-01</div><div class="t-ip">10.0.0.11</div></div><div class="t-led s-ok"></div></div>
|
||||
<div class="tile-gauges">
|
||||
<div class="g-row"><div class="g-ico" data-tip="CPU"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill" style="width:42%"></div></div><span class="g-val">42%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="RAM"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill" style="width:58%"></div></div><span class="g-val">58%</span></div>
|
||||
<div class="g-row"><div class="g-ico" data-tip="Disque"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill" style="width:31%"></div></div><span class="g-val">31%</span></div>
|
||||
</div>
|
||||
<div class="tile-foot"><i class="fa-solid fa-clock"></i>14j 6h</div>
|
||||
</div>
|
||||
<div class="tile t-warn"><div class="tile-head"><div class="t-icon"><i class="fa-solid fa-server"></i></div><div class="t-names"><div class="t-host">srv-backup-02</div><div class="t-ip">10.0.0.12</div></div><div class="t-led s-warn"></div></div><div class="tile-gauges"><div class="g-row"><div class="g-ico" data-tip="CPU élevé" style="color:var(--warn)"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill w" style="width:78%"></div></div><span class="g-val" style="color:var(--warn)">78%</span></div><div class="g-row"><div class="g-ico" data-tip="RAM élevée" style="color:var(--warn)"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill w" style="width:72%"></div></div><span class="g-val" style="color:var(--warn)">72%</span></div><div class="g-row"><div class="g-ico" data-tip="Disque"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill" style="width:20%"></div></div><span class="g-val">20%</span></div></div><div class="tile-foot"><i class="fa-solid fa-clock"></i>3j 14h</div></div>
|
||||
<div class="tile t-err"><div class="tile-head"><div class="t-icon" style="color:var(--err)"><i class="fa-solid fa-microchip"></i></div><div class="t-names"><div class="t-host">rpi-sensor-03</div><div class="t-ip">10.0.0.23</div></div><div class="t-led s-err"></div></div><div class="tile-gauges"><div class="g-row"><div class="g-ico" data-tip="CPU"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill" style="width:12%"></div></div><span class="g-val">12%</span></div><div class="g-row"><div class="g-ico" data-tip="RAM"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill" style="width:38%"></div></div><span class="g-val">38%</span></div><div class="g-row"><div class="g-ico" data-tip="Disque critique !" style="color:var(--err)"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill e" style="width:94%"></div></div><span class="g-val" style="color:var(--err)">94%</span></div></div><div class="tile-foot"><i class="fa-solid fa-clock"></i>62j 1h</div></div>
|
||||
<div class="tile"><div class="tile-head"><div class="t-icon"><i class="fa-solid fa-hard-drive"></i></div><div class="t-names"><div class="t-host">nas-storage-04</div><div class="t-ip">10.0.0.30</div></div><div class="t-led s-ok"></div></div><div class="tile-gauges"><div class="g-row"><div class="g-ico" data-tip="CPU"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"><div class="g-fill" style="width:8%"></div></div><span class="g-val">8%</span></div><div class="g-row"><div class="g-ico" data-tip="RAM"><i class="fa-solid fa-memory"></i></div><div class="g-bar"><div class="g-fill" style="width:45%"></div></div><span class="g-val">45%</span></div><div class="g-row"><div class="g-ico" data-tip="Disque"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"><div class="g-fill" style="width:66%"></div></div><span class="g-val">66%</span></div></div><div class="tile-foot"><i class="fa-solid fa-clock"></i>128j 3h</div></div>
|
||||
<div class="tile t-off"><div class="tile-head"><div class="t-icon" style="color:var(--ink-4)"><i class="fa-solid fa-server"></i></div><div class="t-names"><div class="t-host">srv-dev-07</div><div class="t-ip">10.0.0.60</div></div><div class="t-led s-off"></div></div><div class="tile-gauges"><div class="g-row"><div class="g-ico"><i class="fa-solid fa-microchip"></i></div><div class="g-bar"></div><span class="g-val" style="color:var(--ink-4)">—</span></div><div class="g-row"><div class="g-ico"><i class="fa-solid fa-memory"></i></div><div class="g-bar"></div><span class="g-val" style="color:var(--ink-4)">—</span></div><div class="g-row"><div class="g-ico"><i class="fa-solid fa-hard-drive"></i></div><div class="g-bar"></div><span class="g-val" style="color:var(--ink-4)">—</span></div></div><div class="tile-foot" style="color:var(--err)"><i class="fa-solid fa-circle-xmark"></i>Hors ligne · vu il y a 2h</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<div class="footer">
|
||||
<div class="f-mode">LIVE</div>
|
||||
<div class="f-cell"><i class="fa-solid fa-server" style="font-size:10px"></i><span>SERVEUR</span></div>
|
||||
<div class="f-cell"><i class="fa-solid fa-microchip" style="font-size:10px"></i><span class="f-val">18%</span><div class="f-minibar"><div class="f-minifill" style="width:18%"></div></div></div>
|
||||
<div class="f-cell"><i class="fa-solid fa-memory" style="font-size:10px"></i><span class="f-val w">71%</span><div class="f-minibar"><div class="f-minifill w" style="width:71%"></div></div></div>
|
||||
<div class="f-spacer"></div>
|
||||
<div class="f-right"><i class="fa-solid fa-rotate"></i><span>Actualisation : <span class="f-time">22:14:07</span></span></div>
|
||||
</div>
|
||||
|
||||
<!-- ══ CONFIG SERVEUR ══ -->
|
||||
<div class="overlay" id="overlay-srvcfg" style="display:none" onclick="if(event.target===this)hideSrvCfg()">
|
||||
<div class="popup" id="popup-srvcfg" onclick="event.stopPropagation()">
|
||||
<div class="scfg-head">
|
||||
<div class="scfg-icon"><i class="fa-solid fa-sliders"></i></div>
|
||||
<span class="scfg-title">Configuration interface</span>
|
||||
<div class="pop-close" onclick="hideSrvCfg()" data-tip="Fermer"><i class="fa-solid fa-xmark"></i></div>
|
||||
</div>
|
||||
<div class="scfg-body">
|
||||
<div class="scfg-section">
|
||||
<div class="scfg-sec-title">AFFICHAGE DES TUILES</div>
|
||||
<div class="scfg-row">
|
||||
<label>Largeur min.</label>
|
||||
<input type="range" class="scfg-slider" min="160" max="420" value="220" oninput="this.nextElementSibling.textContent=this.value+'px'">
|
||||
<span class="scfg-val">220px</span>
|
||||
</div>
|
||||
<div class="scfg-row">
|
||||
<label>Taille du texte</label>
|
||||
<input type="range" class="scfg-slider" min="10" max="18" value="13" oninput="this.nextElementSibling.textContent=this.value+'px'">
|
||||
<span class="scfg-val">13px</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scfg-section">
|
||||
<div class="scfg-sec-title">SEUILS D'ALERTE PAR DÉFAUT</div>
|
||||
<div class="scfg-row">
|
||||
<label>Warning CPU/RAM</label>
|
||||
<input type="range" class="scfg-slider" min="50" max="95" value="70" oninput="this.nextElementSibling.textContent=this.value+'%'">
|
||||
<span class="scfg-val">70%</span>
|
||||
</div>
|
||||
<div class="scfg-row">
|
||||
<label>Erreur CPU/RAM</label>
|
||||
<input type="range" class="scfg-slider" min="60" max="100" value="85" oninput="this.nextElementSibling.textContent=this.value+'%'">
|
||||
<span class="scfg-val">85%</span>
|
||||
</div>
|
||||
<div class="scfg-row">
|
||||
<label>Warning Disque</label>
|
||||
<input type="range" class="scfg-slider" min="50" max="95" value="75" oninput="this.nextElementSibling.textContent=this.value+'%'">
|
||||
<span class="scfg-val">75%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scfg-section">
|
||||
<div class="scfg-sec-title">DONNÉES & RÉTENTION</div>
|
||||
<div class="scfg-row">
|
||||
<label>Historique</label>
|
||||
<select class="scfg-select"><option>7 jours</option><option selected>30 jours</option><option>90 jours</option><option>1 an</option></select>
|
||||
</div>
|
||||
<div class="scfg-row">
|
||||
<label>Courbes (durée)</label>
|
||||
<select class="scfg-select"><option>15 min</option><option selected>30 min</option><option>1 heure</option><option>6 heures</option></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scfg-section">
|
||||
<div class="scfg-sec-title">COMPORTEMENT</div>
|
||||
<div class="scfg-toggle-row">
|
||||
<label><i class="fa-solid fa-eye-slash"></i> Masquer les agents hors ligne</label>
|
||||
<label class="toggle"><input type="checkbox"><span class="toggle-slider"></span></label>
|
||||
</div>
|
||||
<div class="scfg-toggle-row">
|
||||
<label><i class="fa-solid fa-bell"></i> Notifications navigateur</label>
|
||||
<label class="toggle"><input type="checkbox" checked><span class="toggle-slider"></span></label>
|
||||
</div>
|
||||
<div class="scfg-toggle-row">
|
||||
<label><i class="fa-solid fa-up-right-and-down-left-from-center"></i> Mémoriser taille des popups</label>
|
||||
<label class="toggle"><input type="checkbox" checked><span class="toggle-slider"></span></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scfg-foot">
|
||||
<button class="btn" onclick="hideSrvCfg()">Annuler</button>
|
||||
<button class="btn primary" onclick="hideSrvCfg()"><i class="fa-solid fa-floppy-disk"></i> Sauvegarder</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══ POPUP DÉTAIL AGENT ══ -->
|
||||
<div class="overlay" id="overlay-detail" style="display:flex" onclick="if(event.target===this)this.style.display='none'">
|
||||
<div class="popup" id="popup-detail" onclick="event.stopPropagation()">
|
||||
<div class="pop-head">
|
||||
<div class="agent-icon-wrap" onclick="document.getElementById('icon-upload').click()" data-tip="Changer l'icône">
|
||||
<i class="fa-solid fa-server"></i>
|
||||
<div class="agent-icon-overlay"><i class="fa-solid fa-camera"></i><span>Changer</span></div>
|
||||
</div>
|
||||
<input type="file" id="icon-upload" accept=".svg,.jpg,.jpeg,.png,.webp">
|
||||
<div class="pop-head-info">
|
||||
<div class="pop-host">srv-prod-01</div>
|
||||
<div class="pop-ip">10.0.0.11</div>
|
||||
<div class="upload-hint">Cliquer sur l'icône pour personnaliser · SVG JPG PNG WEBP · max 128×128 px</div>
|
||||
</div>
|
||||
<div class="pop-led"></div>
|
||||
<div class="pop-close" onclick="document.getElementById('overlay-detail').style.display='none'" data-tip="Fermer"><i class="fa-solid fa-xmark"></i></div>
|
||||
</div>
|
||||
<div class="pop-body">
|
||||
<div>
|
||||
<div class="sec-title">MÉTRIQUES ACTUELLES</div>
|
||||
<div class="kpi-grid">
|
||||
<div class="kpi"><div class="kpi-lbl">CPU</div><div class="kpi-val c-ok">42<span class="u">%</span></div><div class="kpi-sub">4 cœurs</div></div>
|
||||
<div class="kpi"><div class="kpi-lbl">MÉMOIRE</div><div class="kpi-val">3.7<span class="u">Go</span></div><div class="kpi-sub">/ 8 Go · 46%</div></div>
|
||||
<div class="kpi"><div class="kpi-lbl">DISQUE</div><div class="kpi-val">62<span class="u">Go</span></div><div class="kpi-sub">/ 200 Go · 31%</div></div>
|
||||
<div class="kpi"><div class="kpi-lbl">UPTIME</div><div class="kpi-val" style="font-size:15px;color:var(--ink-1)">14j 6h</div><div class="kpi-sub">depuis boot</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sec-title">HISTORIQUE — 30 MIN</div>
|
||||
<div class="charts-grid">
|
||||
<div class="chart-card">
|
||||
<div class="chart-header"><div class="chart-label" style="color:var(--accent)"><i class="fa-solid fa-microchip"></i>CPU</div><span class="chart-cur c-ok">42%</span></div>
|
||||
<svg class="chart-svg" viewBox="0 0 200 52" preserveAspectRatio="none" id="cpu-chart"></svg>
|
||||
<div class="chart-axis"><span>−30min</span><span>−15min</span><span>now</span></div>
|
||||
</div>
|
||||
<div class="chart-card">
|
||||
<div class="chart-header"><div class="chart-label" style="color:var(--blue)"><i class="fa-solid fa-memory"></i>RAM</div><span class="chart-cur" style="color:var(--blue)">46%</span></div>
|
||||
<svg class="chart-svg" viewBox="0 0 200 52" preserveAspectRatio="none" id="ram-chart"></svg>
|
||||
<div class="chart-axis"><span>−30min</span><span>−15min</span><span>now</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sec-title">STOCKAGE</div>
|
||||
<div class="storage-block">
|
||||
<div class="det-gauges">
|
||||
<div class="dg-row"><div class="dg-ico" data-tip="Utilisé"><i class="fa-solid fa-hard-drive"></i></div><div class="dg-bar"><div class="dg-fill" style="width:31%"></div></div><span class="dg-val">62 / 200 Go</span></div>
|
||||
<div class="dg-row"><div class="dg-ico" data-tip="Libre" style="color:var(--blue)"><i class="fa-solid fa-floppy-disk"></i></div><div class="dg-bar"><div class="dg-fill b" style="width:69%"></div></div><span class="dg-val">138 Go libre</span></div>
|
||||
</div>
|
||||
<div class="smart-btn ok" onclick="showSmart();event.stopPropagation()" data-tip="Voir la santé complète du disque">
|
||||
<div class="smart-dot"></div>
|
||||
<span style="font-weight:600;letter-spacing:.04em">SMART</span>
|
||||
<span style="font-size:10px;color:var(--ink-3)">·</span>
|
||||
<span style="font-size:11px;font-family:var(--font-terminal)">PASSED</span>
|
||||
<span style="font-family:var(--font-mono);font-size:10px;color:var(--ink-3);margin-left:4px"><i class="fa-solid fa-temperature-half"></i> 34°C</span>
|
||||
<i class="fa-solid fa-chevron-right" style="font-size:10px;color:var(--ink-4);margin-left:auto"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sec-title">INFORMATIONS</div>
|
||||
<div class="meta-grid">
|
||||
<div class="meta"><div class="meta-lbl">HOSTNAME</div><div class="meta-val">srv-prod-01</div></div>
|
||||
<div class="meta"><div class="meta-lbl">ADRESSE IP</div><div class="meta-val">10.0.0.11</div></div>
|
||||
<div class="meta"><div class="meta-lbl">PROTOCOLES ACTIFS</div><div class="proto-badges"><span class="proto-badge udp"><i class="fa-solid fa-arrow-up"></i>UDP</span><span class="proto-badge mqtt">M MQTT</span></div></div>
|
||||
<div class="meta"><div class="meta-lbl">DERNIER CONTACT</div><div class="meta-val">22:14:07</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pop-foot">
|
||||
<span class="pop-uptime"><i class="fa-solid fa-clock" style="margin-right:4px"></i>En ligne depuis 14 jours 6 heures</span>
|
||||
<span class="resize-hint" data-tip="Taille sauvegardée sur le serveur"><i class="fa-solid fa-up-right-and-down-left-from-center"></i>Redimensionnable</span>
|
||||
<div class="btn-agent-cfg" onclick="showAgentCfg()" data-tip="Configurer l'agent"><i class="fa-solid fa-gears"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══ CONFIG AGENT ══ -->
|
||||
<div class="overlay" id="overlay-agentcfg" style="display:none;z-index:200" onclick="if(event.target===this)this.style.display='none'">
|
||||
<div class="popup" id="popup-agentcfg" onclick="event.stopPropagation()">
|
||||
<div class="cfg-head">
|
||||
<div class="cfg-head-icon"><i class="fa-solid fa-gears"></i></div>
|
||||
<div class="cfg-head-info"><div class="cfg-head-title">Configuration de l'agent</div><div class="cfg-head-sub">srv-prod-01 · 10.0.0.11 · config récupérée à 22:14:05</div></div>
|
||||
<div class="pop-close" onclick="this.closest('.overlay').style.display='none'" data-tip="Fermer"><i class="fa-solid fa-xmark"></i></div>
|
||||
</div>
|
||||
<div class="cfg-body">
|
||||
|
||||
<!-- MÉTRIQUES PAR PROTOCOLE — tableau 3 colonnes sans activation générale -->
|
||||
<div class="cfg-section">
|
||||
<div class="cfg-sec-title">MÉTRIQUES PAR PROTOCOLE</div>
|
||||
<div class="metrics-table">
|
||||
<div class="metrics-header">
|
||||
<span class="mh-label">MÉTRIQUE</span>
|
||||
<span class="mh-proto udp">
|
||||
<i class="fa-solid fa-arrow-up"></i> UDP
|
||||
<span class="proto-dot"></span>
|
||||
</span>
|
||||
<span class="mh-proto mqtt">
|
||||
<i class="fa-solid fa-tower-broadcast" style="font-size:8px"></i> MQTT
|
||||
<span class="proto-dot"></span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<div class="metric-cell"><div class="metric-ico"><i class="fa-solid fa-microchip"></i></div><span class="metric-name">cpu</span></div>
|
||||
<div class="metric-chk"><div class="cbox udp-on" onclick="toggleCbox(this,'udp')" data-tip="CPU via UDP"><i class="fa-solid fa-check"></i></div></div>
|
||||
<div class="metric-chk"><div class="cbox mqtt-on" onclick="toggleCbox(this,'mqtt')" data-tip="CPU via MQTT"><i class="fa-solid fa-check"></i></div></div>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<div class="metric-cell"><div class="metric-ico"><i class="fa-solid fa-memory"></i></div><span class="metric-name">memory</span></div>
|
||||
<div class="metric-chk"><div class="cbox udp-on" onclick="toggleCbox(this,'udp')" data-tip="RAM via UDP"><i class="fa-solid fa-check"></i></div></div>
|
||||
<div class="metric-chk"><div class="cbox mqtt-on" onclick="toggleCbox(this,'mqtt')" data-tip="RAM via MQTT"><i class="fa-solid fa-check"></i></div></div>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<div class="metric-cell"><div class="metric-ico"><i class="fa-solid fa-hard-drive"></i></div><span class="metric-name">disk</span></div>
|
||||
<div class="metric-chk"><div class="cbox udp-on" onclick="toggleCbox(this,'udp')" data-tip="Disque via UDP"><i class="fa-solid fa-check"></i></div></div>
|
||||
<div class="metric-chk"><div class="cbox mqtt-on" onclick="toggleCbox(this,'mqtt')" data-tip="Disque via MQTT"><i class="fa-solid fa-check"></i></div></div>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<div class="metric-cell"><div class="metric-ico"><i class="fa-solid fa-shield-heart"></i></div><span class="metric-name">smart</span></div>
|
||||
<div class="metric-chk"><div class="cbox udp-on" onclick="toggleCbox(this,'udp')" data-tip="SMART via UDP"><i class="fa-solid fa-check"></i></div></div>
|
||||
<div class="metric-chk"><div class="cbox" onclick="toggleCbox(this,'mqtt')" data-tip="SMART via MQTT"><i class="fa-solid fa-check"></i></div></div>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<div class="metric-cell"><div class="metric-ico"><i class="fa-solid fa-clock"></i></div><span class="metric-name">uptime</span></div>
|
||||
<div class="metric-chk"><div class="cbox udp-on" onclick="toggleCbox(this,'udp')" data-tip="Uptime via UDP"><i class="fa-solid fa-check"></i></div></div>
|
||||
<div class="metric-chk"><div class="cbox mqtt-on" onclick="toggleCbox(this,'mqtt')" data-tip="Uptime via MQTT"><i class="fa-solid fa-check"></i></div></div>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<div class="metric-cell"><div class="metric-ico"><i class="fa-solid fa-network-wired"></i></div><span class="metric-name">network</span></div>
|
||||
<div class="metric-chk"><div class="cbox" onclick="toggleCbox(this,'udp')" data-tip="Réseau via UDP"><i class="fa-solid fa-check"></i></div></div>
|
||||
<div class="metric-chk"><div class="cbox" onclick="toggleCbox(this,'mqtt')" data-tip="Réseau via MQTT"><i class="fa-solid fa-check"></i></div></div>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<div class="metric-cell"><div class="metric-ico"><i class="fa-solid fa-thermometer-half"></i></div><span class="metric-name">temperature</span></div>
|
||||
<div class="metric-chk"><div class="cbox" onclick="toggleCbox(this,'udp')" data-tip="Température via UDP"><i class="fa-solid fa-check"></i></div></div>
|
||||
<div class="metric-chk"><div class="cbox mqtt-on" onclick="toggleCbox(this,'mqtt')" data-tip="Température via MQTT"><i class="fa-solid fa-check"></i></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PARAMÈTRES MQTT -->
|
||||
<div class="cfg-section">
|
||||
<div class="cfg-sec-title">PARAMÈTRES MQTT</div>
|
||||
<div class="mqtt-opts">
|
||||
<div class="mqtt-field"><label>Broker</label><input class="mqtt-input" type="text" value="10.0.0.3"></div>
|
||||
<div class="mqtt-field"><label>Port</label><input class="mqtt-input" type="number" value="1883" style="width:90px;flex:none"></div>
|
||||
<div class="mqtt-field"><label>Topic base</label><input class="mqtt-input" type="text" value="nanometrics/agents"></div>
|
||||
<div style="border-top:1px solid var(--border-1);padding-top:8px;display:flex;flex-direction:column;gap:5px">
|
||||
<div class="mqtt-check-row"><label><i class="fa-solid fa-satellite-dish"></i> Auto-discovery (Home Assistant)</label><label class="toggle tog-mqtt"><input type="checkbox" checked><span class="toggle-slider"></span></label></div>
|
||||
<div class="mqtt-check-row"><label><i class="fa-solid fa-arrow-right-to-bracket"></i> Birth message</label><label class="toggle tog-mqtt"><input type="checkbox" checked><span class="toggle-slider"></span></label></div>
|
||||
<div class="mqtt-check-row"><label><i class="fa-solid fa-skull"></i> Last Will message</label><label class="toggle tog-mqtt"><input type="checkbox" checked><span class="toggle-slider"></span></label></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- COMMANDES -->
|
||||
<div class="cfg-section">
|
||||
<div class="cfg-sec-title">COMMANDES DISTANTES <span style="color:var(--ink-4);font-size:8px;margin-left:6px">— BIENTÔT</span></div>
|
||||
<div class="cmds-grid">
|
||||
<div class="cmd-btn"><i class="fa-solid fa-rotate-right"></i><span>reboot</span><span class="soon-tag">bientôt</span></div>
|
||||
<div class="cmd-btn"><i class="fa-solid fa-power-off"></i><span>shutdown</span><span class="soon-tag">bientôt</span></div>
|
||||
<div class="cmd-btn"><i class="fa-solid fa-display"></i><span>screen off</span><span class="soon-tag">bientôt</span></div>
|
||||
<div class="cmd-btn"><i class="fa-solid fa-arrow-up-from-bracket"></i><span>update</span><span class="soon-tag">bientôt</span></div>
|
||||
<div class="cmd-btn"><i class="fa-solid fa-arrow-up-right-dots"></i><span>upgrade</span><span class="soon-tag">bientôt</span></div>
|
||||
<div class="cmd-btn"><i class="fa-solid fa-terminal"></i><span>shell cmd</span><span class="soon-tag">bientôt</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cfg-foot">
|
||||
<div class="cfg-status"><div class="dot"></div><span>Config synchronisée avec l'agent</span></div>
|
||||
<button class="btn" onclick="this.closest('.overlay').style.display='none'">Annuler</button>
|
||||
<button class="btn primary"><i class="fa-solid fa-paper-plane"></i> Envoyer à l'agent</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SMART -->
|
||||
<div class="overlay" id="overlay-smart" style="display:none;z-index:300" onclick="if(event.target===this)this.style.display='none'">
|
||||
<div class="popup" id="popup-smart" onclick="event.stopPropagation()">
|
||||
<div class="smart-head">
|
||||
<div class="smart-head-icon"><i class="fa-solid fa-shield-heart"></i></div>
|
||||
<div class="smart-head-info"><div class="smart-head-title">Santé du disque dur</div><div class="smart-head-sub">srv-prod-01 · /dev/sda · Samsung SSD 870 EVO 250GB</div></div>
|
||||
<div class="pop-close" onclick="this.closest('.overlay').style.display='none'" data-tip="Fermer"><i class="fa-solid fa-xmark"></i></div>
|
||||
</div>
|
||||
<div class="smart-body">
|
||||
<div class="smart-verdict">
|
||||
<div class="verdict-icon"><i class="fa-solid fa-circle-check"></i></div>
|
||||
<div class="verdict-text"><div class="v-title">Disque en bonne santé</div><div class="v-sub">Aucun problème détecté. Le disque fonctionne normalement et peut être utilisé en toute confiance.</div></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sec-title">POINTS DE CONTRÔLE</div>
|
||||
<div class="smart-indicators">
|
||||
<div class="smart-ind"><div class="si-header"><div class="si-icon" style="color:var(--warn)"><i class="fa-solid fa-temperature-half"></i></div><span class="si-title">Température</span><span class="si-status ok">Normale</span></div><div class="si-val">34<span class="u">°C</span></div><div class="si-desc">Idéal : 20-50°C. Au-delà de 60°C le disque risque de s'abîmer prématurément.</div></div>
|
||||
<div class="smart-ind"><div class="si-header"><div class="si-icon" style="color:var(--ok)"><i class="fa-solid fa-circle-check"></i></div><span class="si-title">Secteurs défectueux</span><span class="si-status ok">Aucun</span></div><div class="si-val">0<span class="u"> sect.</span></div><div class="si-desc">S'ils apparaissent en grand nombre, une panne est imminente.</div></div>
|
||||
<div class="smart-ind"><div class="si-header"><div class="si-icon" style="color:var(--blue)"><i class="fa-solid fa-clock-rotate-left"></i></div><span class="si-title">Heures de fonctionnement</span><span class="si-status ok">Jeune</span></div><div class="si-val">4<span class="u">213h</span></div><div class="si-desc">≈175 jours. Un disque dure en moyenne 3 à 5 ans.</div></div>
|
||||
<div class="smart-ind"><div class="si-header"><div class="si-icon" style="color:var(--ok)"><i class="fa-solid fa-battery-full"></i></div><span class="si-title">Durée de vie SSD</span><span class="si-status ok">Excellente</span></div><div class="si-val">98<span class="u">%</span></div><div class="si-desc">100% = neuf · 0% = fin de vie recommandée.</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sec-title">ATTRIBUTS DÉTAILLÉS</div>
|
||||
<div class="smart-attrs">
|
||||
<div class="attr-row"><span class="attr-id">001</span><span class="attr-name">Taux d'erreurs de lecture</span><span class="attr-val attr-ok">0</span><span class="attr-explain attr-ok">✓ Parfait</span></div>
|
||||
<div class="attr-row"><span class="attr-id">005</span><span class="attr-name">Secteurs réalloués</span><span class="attr-val attr-ok">0</span><span class="attr-explain attr-ok">✓ Aucun dommage</span></div>
|
||||
<div class="attr-row"><span class="attr-id">009</span><span class="attr-name">Heures de fonctionnement</span><span class="attr-val attr-ok">4213</span><span class="attr-explain">175 jours</span></div>
|
||||
<div class="attr-row"><span class="attr-id">177</span><span class="attr-name">Usure cellules SSD</span><span class="attr-val attr-ok">2</span><span class="attr-explain attr-ok">✓ Très faible</span></div>
|
||||
<div class="attr-row"><span class="attr-id">190</span><span class="attr-name">Température</span><span class="attr-val attr-ok">34°C</span><span class="attr-explain attr-ok">✓ Normale</span></div>
|
||||
<div class="attr-row"><span class="attr-id">197</span><span class="attr-name">Secteurs instables</span><span class="attr-val attr-ok">0</span><span class="attr-explain attr-ok">✓ Aucun</span></div>
|
||||
<div class="attr-row"><span class="attr-id">198</span><span class="attr-name">Secteurs non corrigeables</span><span class="attr-val attr-ok">0</span><span class="attr-explain attr-ok">✓ Aucun</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="smart-footer">
|
||||
<span class="smart-footer-note"><i class="fa-solid fa-circle-info" style="margin-right:4px"></i>Données via smartctl · actualisées à chaque cycle de l'agent</span>
|
||||
<button class="btn primary" onclick="this.closest('.overlay').style.display='none'"><i class="fa-solid fa-xmark"></i> Fermer</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/* tooltip */
|
||||
const tip=document.getElementById('tooltip');let tt;
|
||||
document.addEventListener('mouseover',e=>{const el=e.target.closest('[data-tip]');if(!el)return;clearTimeout(tt);tt=setTimeout(()=>{tip.textContent=el.dataset.tip;tip.classList.add('show');mv(e);},120);});
|
||||
document.addEventListener('mousemove',e=>{if(tip.classList.contains('show'))mv(e);});
|
||||
document.addEventListener('mouseout',e=>{const el=e.target.closest('[data-tip]');if(!el)return;clearTimeout(tt);tip.classList.remove('show');});
|
||||
function mv(e){const w=tip.offsetWidth,h=tip.offsetHeight;let x=e.clientX-w/2,y=e.clientY-h-10;x=Math.max(6,Math.min(x,window.innerWidth-w-6));if(y<6)y=e.clientY+18;tip.style.left=x+'px';tip.style.top=y+'px';}
|
||||
|
||||
/* thème */
|
||||
function toggleTheme(){const h=document.documentElement;h.dataset.theme=h.dataset.theme==='dark'?'light':'dark';document.getElementById('theme-icon').className=h.dataset.theme==='dark'?'fa-solid fa-moon':'fa-solid fa-sun';drawCharts();}
|
||||
|
||||
/* config serveur */
|
||||
function showSrvCfg(){document.getElementById('overlay-srvcfg').style.display='flex';document.getElementById('btn-srvcfg').classList.add('active-btn');}
|
||||
function hideSrvCfg(){document.getElementById('overlay-srvcfg').style.display='none';document.getElementById('btn-srvcfg').classList.remove('active-btn');}
|
||||
|
||||
/* popups */
|
||||
function showDetail(){document.getElementById('overlay-detail').style.display='flex';drawCharts();}
|
||||
function showAgentCfg(){document.getElementById('overlay-agentcfg').style.display='flex';}
|
||||
function showSmart(){document.getElementById('overlay-smart').style.display='flex';}
|
||||
|
||||
/* checkbox métrique par protocole */
|
||||
function toggleCbox(el,proto){
|
||||
const isOn=el.classList.contains(proto+'-on');
|
||||
el.classList.toggle(proto+'-on',!isOn);
|
||||
el.style.color=isOn?'transparent':'';
|
||||
}
|
||||
|
||||
/* courbes */
|
||||
function makeCurve(pts,stroke,fill,w,h){
|
||||
const xs=pts.map((_,i)=>(i/(pts.length-1))*w);
|
||||
const ys=pts.map(v=>h-(v/100)*(h-6)-3);
|
||||
const wy=h-(70/100)*(h-6)-3;
|
||||
let d=`M${xs[0]} ${ys[0]}`;
|
||||
for(let i=1;i<pts.length;i++){const cx=(xs[i-1]+xs[i])/2;d+=` C${cx} ${ys[i-1]},${cx} ${ys[i]},${xs[i]} ${ys[i]}`;}
|
||||
const uid=Math.random().toString(36).slice(2);
|
||||
return `<defs><linearGradient id="g${uid}" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="${fill}" stop-opacity=".4"/><stop offset="100%" stop-color="${fill}" stop-opacity=".02"/></linearGradient></defs>
|
||||
<line x1="0" y1="${wy}" x2="${w}" y2="${wy}" stroke="var(--warn)" stroke-width=".8" stroke-dasharray="3,3" opacity=".5"/>
|
||||
<path d="${d} L${xs.at(-1)} ${h} L${xs[0]} ${h}Z" fill="url(#g${uid})"/>
|
||||
<path d="${d}" fill="none" stroke="${stroke}" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<circle cx="${xs.at(-1)}" cy="${ys.at(-1)}" r="2.5" fill="${stroke}"/>`;
|
||||
}
|
||||
function drawCharts(){
|
||||
const cpu=[38,41,35,40,44,42,39,45,50,48,43,42,44,41,40,43,47,45,42,44,41,43,42,40,43,41,44,43,41,42];
|
||||
const ram=[44,44,45,45,46,46,47,47,46,46,45,46,46,47,47,46,46,45,46,46,46,46,46,46,46,46,46,46,46,46];
|
||||
const cs=getComputedStyle(document.documentElement);
|
||||
const ac=cs.getPropertyValue('--accent').trim(),bl=cs.getPropertyValue('--blue').trim();
|
||||
document.getElementById('cpu-chart').innerHTML=makeCurve(cpu,ac,ac,200,52);
|
||||
document.getElementById('ram-chart').innerHTML=makeCurve(ram,bl,bl,200,52);
|
||||
}
|
||||
|
||||
/* resize popup → sauvegarde serveur */
|
||||
const pd=document.getElementById('popup-detail');
|
||||
new ResizeObserver(()=>{
|
||||
/* en prod : fetch('/api/config', {method:'PUT', body: JSON.stringify({popup_detail_size:{w:pd.offsetWidth,h:pd.offsetHeight}})}) */
|
||||
console.log('Taille sauvegardée sur serveur:',pd.offsetWidth,'x',pd.offsetHeight);
|
||||
}).observe(pd);
|
||||
|
||||
window.addEventListener('load',drawCharts);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,360 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Nanometrics — Layout du dashboard</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Share+Tech+Mono:wght@400&display=swap" rel="stylesheet">
|
||||
<script src="/helper.js"></script>
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
:root[data-theme="dark"] {
|
||||
--accent: #fe8019; --accent-soft: #d65d0e; --accent-glow: rgba(254,128,25,0.35);
|
||||
--bg-0: #1d1813; --bg-1: #2a231d; --bg-2: #32291f; --bg-3: #3c332a; --bg-4: #4a3f33; --bg-5: #57493c;
|
||||
--ink-1: #f2e5c7; --ink-2: #d5c4a1; --ink-3: #a89984; --ink-4: #7c6f64;
|
||||
--ok: #4dbb26; --warn: #fabd2f; --err: #fb4934; --info: #83a598; --blue: #3db0d1; --purple: #c882c8;
|
||||
--border-1: rgba(255,255,255,0.06); --border-2: rgba(255,255,255,0.12); --border-3: rgba(255,255,255,0.22);
|
||||
--shadow-1: 0 1px 4px rgba(0,0,0,0.4); --shadow-2: 0 4px 16px rgba(0,0,0,0.5); --shadow-3: 0 8px 32px rgba(0,0,0,0.6);
|
||||
--tile-3d: 0 1px 0 rgba(255,255,255,0.08) inset, 0 -1px 0 rgba(0,0,0,0.3) inset, 0 4px 16px rgba(0,0,0,0.5);
|
||||
--font-ui: 'Inter', system-ui, sans-serif; --font-mono: 'JetBrains Mono', monospace; --font-terminal: 'Share Tech Mono', monospace;
|
||||
}
|
||||
|
||||
body { background: var(--bg-1); color: var(--ink-1); font-family: var(--font-ui); font-size: 13px; min-height: 100vh; }
|
||||
|
||||
/* ── HEADER PAGE ── */
|
||||
.page-header {
|
||||
background: var(--bg-0); border-bottom: 1px solid var(--border-2);
|
||||
padding: 14px 24px; text-align: center;
|
||||
}
|
||||
.page-header h1 { font-size: 15px; color: var(--ink-2); font-weight: 500; }
|
||||
.page-header p { font-size: 12px; color: var(--ink-3); margin-top: 4px; }
|
||||
|
||||
/* ── CARDS LAYOUT ── */
|
||||
.cards-wrap { display: flex; gap: 24px; padding: 28px 24px; flex-wrap: wrap; justify-content: center; }
|
||||
|
||||
.card-opt {
|
||||
background: var(--bg-2); border: 2px solid var(--border-2); border-radius: 12px;
|
||||
width: 320px; cursor: pointer; transition: border-color .15s, box-shadow .15s;
|
||||
overflow: hidden;
|
||||
}
|
||||
.card-opt:hover { border-color: var(--accent-soft); }
|
||||
.card-opt.selected { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-glow); }
|
||||
|
||||
.card-label {
|
||||
background: var(--bg-3); padding: 10px 16px;
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
border-bottom: 1px solid var(--border-1);
|
||||
}
|
||||
.card-letter {
|
||||
width: 26px; height: 26px; border-radius: 6px;
|
||||
background: var(--accent); color: #1d1813; font-weight: 700; font-size: 13px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
}
|
||||
.card-name { font-weight: 600; font-size: 13px; color: var(--ink-1); }
|
||||
.card-sub { font-size: 11px; color: var(--ink-3); margin-top: 1px; }
|
||||
|
||||
.card-mock { padding: 14px; }
|
||||
|
||||
/* ── INDICATOR ── */
|
||||
#selection-bar {
|
||||
position: fixed; bottom: 0; left: 0; right: 0;
|
||||
background: var(--bg-0); border-top: 1px solid var(--border-2);
|
||||
padding: 10px 24px; display: flex; align-items: center; justify-content: space-between;
|
||||
font-family: var(--font-terminal); font-size: 12px; color: var(--ink-3);
|
||||
}
|
||||
#sel-text { color: var(--accent); }
|
||||
|
||||
/* ── MINI MAQUETTES ── */
|
||||
/* couleurs communes */
|
||||
.m { background: var(--bg-1); border-radius: 8px; overflow: hidden; }
|
||||
.mh { background: var(--bg-2); padding: 6px 10px; display: flex; align-items: center; gap: 6px; border-bottom: 1px solid var(--border-1); font-size: 10px; color: var(--ink-2); font-weight: 600; }
|
||||
.mh .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); }
|
||||
.mh .title { flex:1; }
|
||||
.mh .clock { font-family: var(--font-terminal); color: var(--ink-4); font-size: 9px; }
|
||||
|
||||
/* layout A — grille uniforme */
|
||||
.grid-a { display: grid; grid-template-columns: repeat(4, 1fr); gap: 5px; padding: 8px; }
|
||||
.tile-a {
|
||||
background: var(--bg-3); border-radius: 6px; padding: 7px 8px;
|
||||
border: 1px solid var(--border-1); box-shadow: var(--tile-3d);
|
||||
display: flex; flex-direction: column; gap: 3px;
|
||||
}
|
||||
.tile-a .t-header { display: flex; align-items: center; gap: 4px; }
|
||||
.tile-a .t-icon { font-size: 9px; color: var(--accent); }
|
||||
.tile-a .t-name { font-size: 9px; font-weight: 600; color: var(--ink-2); }
|
||||
.tile-a .t-led { width: 5px; height: 5px; border-radius: 50%; background: var(--ok); margin-left: auto; }
|
||||
.tile-a .t-led.warn { background: var(--warn); }
|
||||
.tile-a .t-led.err { background: var(--err); }
|
||||
.tile-a .gauge-row { display: flex; align-items: center; gap: 4px; }
|
||||
.tile-a .g-label { font-size: 8px; color: var(--ink-4); font-family: var(--font-terminal); width: 20px; }
|
||||
.tile-a .g-bar { flex:1; height: 4px; border-radius: 2px; background: var(--bg-4); overflow: hidden; }
|
||||
.tile-a .g-fill { height: 100%; border-radius: 2px; background: var(--ok); }
|
||||
.tile-a .g-fill.warn { background: var(--warn); }
|
||||
.tile-a .g-fill.err { background: var(--err); }
|
||||
.tile-a .g-val { font-size: 8px; color: var(--ink-3); font-family: var(--font-mono); width: 20px; text-align: right; }
|
||||
|
||||
/* layout B — sidebar + grille */
|
||||
.layout-b { display: flex; gap: 0; }
|
||||
.sidebar-b {
|
||||
width: 72px; background: var(--bg-2); padding: 6px 4px;
|
||||
display: flex; flex-direction: column; gap: 2px;
|
||||
border-right: 1px solid var(--border-1);
|
||||
}
|
||||
.sb-item {
|
||||
padding: 5px 4px; border-radius: 5px; display: flex; align-items: center; gap: 4px;
|
||||
font-size: 8px; color: var(--ink-3); cursor: pointer;
|
||||
}
|
||||
.sb-item.active { background: var(--bg-4); color: var(--accent); }
|
||||
.sb-item .sb-led { width: 4px; height: 4px; border-radius: 50%; background: var(--ok); flex-shrink: 0; }
|
||||
.sb-item .sb-led.warn { background: var(--warn); }
|
||||
.sb-item .sb-led.err { background: var(--err); }
|
||||
.sb-item .sb-label { font-family: var(--font-terminal); font-size: 8px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.grid-b { flex: 1; display: grid; grid-template-columns: repeat(3, 1fr); gap: 5px; padding: 6px; }
|
||||
.tile-b {
|
||||
background: var(--bg-3); border-radius: 6px; padding: 7px 8px;
|
||||
border: 1px solid var(--border-1); box-shadow: var(--tile-3d);
|
||||
display: flex; flex-direction: column; gap: 3px;
|
||||
}
|
||||
.tile-b .t-header { display: flex; align-items: center; gap: 4px; }
|
||||
.tile-b .t-icon { font-size: 9px; color: var(--accent); }
|
||||
.tile-b .t-name { font-size: 9px; font-weight: 600; color: var(--ink-2); }
|
||||
.tile-b .t-led { width: 5px; height: 5px; border-radius: 50%; background: var(--ok); margin-left: auto; }
|
||||
.tile-b .gauge-row { display: flex; align-items: center; gap: 4px; }
|
||||
.tile-b .g-label { font-size: 8px; color: var(--ink-4); font-family: var(--font-terminal); width: 20px; }
|
||||
.tile-b .g-bar { flex:1; height: 4px; border-radius: 2px; background: var(--bg-4); overflow: hidden; }
|
||||
.tile-b .g-fill { height: 100%; border-radius: 2px; background: var(--ok); }
|
||||
.tile-b .g-fill.warn { background: var(--warn); }
|
||||
.tile-b .g-val { font-size: 8px; color: var(--ink-3); font-family: var(--font-mono); width: 20px; text-align: right; }
|
||||
|
||||
/* layout C — 3 colonnes */
|
||||
.layout-c { display: flex; gap: 0; height: 160px; }
|
||||
.tree-c {
|
||||
width: 68px; background: var(--bg-2); padding: 5px 4px;
|
||||
display: flex; flex-direction: column; gap: 1px;
|
||||
border-right: 1px solid var(--border-1);
|
||||
overflow: hidden;
|
||||
}
|
||||
.tc-group { font-size: 8px; color: var(--accent); font-family: var(--font-terminal); padding: 2px 3px; letter-spacing: .05em; }
|
||||
.tc-item { display: flex; align-items: center; gap: 3px; padding: 2px 6px; border-radius: 3px; }
|
||||
.tc-item.active { background: var(--bg-4); }
|
||||
.tc-dot { width: 4px; height: 4px; border-radius: 50%; background: var(--ok); flex-shrink: 0; }
|
||||
.tc-dot.warn { background: var(--warn); }
|
||||
.tc-dot.err { background: var(--err); }
|
||||
.tc-label { font-size: 8px; color: var(--ink-3); font-family: var(--font-terminal); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.cockpit-c {
|
||||
flex: 1; padding: 6px; display: grid; grid-template-columns: repeat(2, 1fr); gap: 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.kpi-c {
|
||||
background: var(--bg-3); border-radius: 5px; padding: 6px 8px;
|
||||
border: 1px solid var(--border-1); box-shadow: var(--tile-3d);
|
||||
display: flex; flex-direction: column; gap: 3px;
|
||||
}
|
||||
.kpi-c .k-label { font-size: 8px; color: var(--ink-4); font-family: var(--font-terminal); letter-spacing: .06em; }
|
||||
.kpi-c .k-val { font-family: var(--font-mono); font-size: 16px; font-weight: 700; color: var(--accent); line-height: 1; }
|
||||
.kpi-c .k-sub { font-size: 8px; color: var(--ink-3); }
|
||||
.kpi-c .g-bar { height: 3px; border-radius: 2px; background: var(--bg-4); overflow: hidden; margin-top: 2px; }
|
||||
.kpi-c .g-fill { height: 100%; border-radius: 2px; background: var(--ok); }
|
||||
.kpi-c .g-fill.warn { background: var(--warn); }
|
||||
.logs-c {
|
||||
width: 90px; background: var(--bg-2); padding: 5px;
|
||||
border-left: 1px solid var(--border-1); overflow: hidden;
|
||||
display: flex; flex-direction: column; gap: 2px;
|
||||
}
|
||||
.log-line { font-family: var(--font-terminal); font-size: 7px; color: var(--ink-4); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.log-line.ok { color: var(--ok); }
|
||||
.log-line.warn { color: var(--warn); }
|
||||
.log-line.err { color: var(--err); }
|
||||
|
||||
/* status bar commune */
|
||||
.statusbar {
|
||||
background: var(--bg-0); border-top: 1px solid var(--border-1);
|
||||
display: flex; font-family: var(--font-terminal); font-size: 8px; color: var(--ink-4);
|
||||
}
|
||||
.sb-mode { background: var(--accent); color: var(--bg-0); padding: 2px 8px; font-weight: 700; }
|
||||
.sb-cell { padding: 2px 8px; border-right: 1px solid var(--border-1); }
|
||||
.sb-right { margin-left: auto; padding: 2px 8px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="page-header">
|
||||
<h1>Layout du dashboard Nanometrics</h1>
|
||||
<p>Clique sur le layout qui te correspond le mieux</p>
|
||||
</div>
|
||||
|
||||
<div class="cards-wrap">
|
||||
|
||||
<!-- OPTION A -->
|
||||
<div class="card-opt" data-choice="a" onclick="toggleSelect(this)">
|
||||
<div class="card-label">
|
||||
<div class="card-letter">A</div>
|
||||
<div>
|
||||
<div class="card-name">Grille uniforme</div>
|
||||
<div class="card-sub">Toutes les tuiles identiques, adaptatives</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-mock">
|
||||
<div class="m">
|
||||
<div class="mh"><div class="dot"></div><span class="title">NANOMETRICS</span><span class="clock">22:14:05</span></div>
|
||||
<div class="grid-a">
|
||||
<div class="tile-a">
|
||||
<div class="t-header"><span class="t-icon">⬡</span><span class="t-name">srv-01</span><span class="t-led"></span></div>
|
||||
<div class="gauge-row"><span class="g-label">CPU</span><div class="g-bar"><div class="g-fill" style="width:42%"></div></div><span class="g-val">42%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">RAM</span><div class="g-bar"><div class="g-fill warn" style="width:74%"></div></div><span class="g-val">74%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">DSK</span><div class="g-bar"><div class="g-fill" style="width:31%"></div></div><span class="g-val">31%</span></div>
|
||||
</div>
|
||||
<div class="tile-a">
|
||||
<div class="t-header"><span class="t-icon">⬡</span><span class="t-name">srv-02</span><span class="t-led warn"></span></div>
|
||||
<div class="gauge-row"><span class="g-label">CPU</span><div class="g-bar"><div class="g-fill warn" style="width:78%"></div></div><span class="g-val">78%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">RAM</span><div class="g-bar"><div class="g-fill" style="width:55%"></div></div><span class="g-val">55%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">DSK</span><div class="g-bar"><div class="g-fill" style="width:20%"></div></div><span class="g-val">20%</span></div>
|
||||
</div>
|
||||
<div class="tile-a">
|
||||
<div class="t-header"><span class="t-icon">⬡</span><span class="t-name">rpi-03</span><span class="t-led"></span></div>
|
||||
<div class="gauge-row"><span class="g-label">CPU</span><div class="g-bar"><div class="g-fill" style="width:12%"></div></div><span class="g-val">12%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">RAM</span><div class="g-bar"><div class="g-fill" style="width:38%"></div></div><span class="g-val">38%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">DSK</span><div class="g-bar"><div class="g-fill err" style="width:91%"></div></div><span class="g-val">91%</span></div>
|
||||
</div>
|
||||
<div class="tile-a">
|
||||
<div class="t-header"><span class="t-icon">⬡</span><span class="t-name">nas-04</span><span class="t-led"></span></div>
|
||||
<div class="gauge-row"><span class="g-label">CPU</span><div class="g-bar"><div class="g-fill" style="width:8%"></div></div><span class="g-val">8%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">RAM</span><div class="g-bar"><div class="g-fill" style="width:60%"></div></div><span class="g-val">60%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">DSK</span><div class="g-bar"><div class="g-fill" style="width:44%"></div></div><span class="g-val">44%</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="statusbar">
|
||||
<div class="sb-mode">LIVE</div>
|
||||
<div class="sb-cell">4 agents</div>
|
||||
<div class="sb-cell">3 ok · 1 warn</div>
|
||||
<div class="sb-right">22:14:05</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- OPTION B -->
|
||||
<div class="card-opt" data-choice="b" onclick="toggleSelect(this)">
|
||||
<div class="card-label">
|
||||
<div class="card-letter">B</div>
|
||||
<div>
|
||||
<div class="card-name">Sidebar + grille</div>
|
||||
<div class="card-sub">Liste des agents à gauche, tuiles à droite</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-mock">
|
||||
<div class="m">
|
||||
<div class="mh"><div class="dot"></div><span class="title">NANOMETRICS</span><span class="clock">22:14:05</span></div>
|
||||
<div class="layout-b">
|
||||
<div class="sidebar-b">
|
||||
<div class="sb-item active"><div class="sb-led"></div><span class="sb-label">srv-01</span></div>
|
||||
<div class="sb-item"><div class="sb-led warn"></div><span class="sb-label">srv-02</span></div>
|
||||
<div class="sb-item"><div class="sb-led err"></div><span class="sb-label">rpi-03</span></div>
|
||||
<div class="sb-item"><div class="sb-led"></div><span class="sb-label">nas-04</span></div>
|
||||
<div class="sb-item"><div class="sb-led"></div><span class="sb-label">db-05</span></div>
|
||||
<div class="sb-item"><div class="sb-led"></div><span class="sb-label">web-06</span></div>
|
||||
</div>
|
||||
<div class="grid-b">
|
||||
<div class="tile-b">
|
||||
<div class="t-header"><span class="t-icon">⬡</span><span class="t-name">srv-01</span><span class="t-led"></span></div>
|
||||
<div class="gauge-row"><span class="g-label">CPU</span><div class="g-bar"><div class="g-fill" style="width:42%"></div></div><span class="g-val">42%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">RAM</span><div class="g-bar"><div class="g-fill warn" style="width:74%"></div></div><span class="g-val">74%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">DSK</span><div class="g-bar"><div class="g-fill" style="width:31%"></div></div><span class="g-val">31%</span></div>
|
||||
</div>
|
||||
<div class="tile-b">
|
||||
<div class="t-header"><span class="t-icon">⬡</span><span class="t-name">srv-02</span><span class="t-led" style="background:var(--warn)"></span></div>
|
||||
<div class="gauge-row"><span class="g-label">CPU</span><div class="g-bar"><div class="g-fill warn" style="width:78%"></div></div><span class="g-val">78%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">RAM</span><div class="g-bar"><div class="g-fill" style="width:55%"></div></div><span class="g-val">55%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">DSK</span><div class="g-bar"><div class="g-fill" style="width:20%"></div></div><span class="g-val">20%</span></div>
|
||||
</div>
|
||||
<div class="tile-b">
|
||||
<div class="t-header"><span class="t-icon">⬡</span><span class="t-name">rpi-03</span><span class="t-led" style="background:var(--err)"></span></div>
|
||||
<div class="gauge-row"><span class="g-label">CPU</span><div class="g-bar"><div class="g-fill" style="width:12%"></div></div><span class="g-val">12%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">RAM</span><div class="g-bar"><div class="g-fill" style="width:38%"></div></div><span class="g-val">38%</span></div>
|
||||
<div class="gauge-row"><span class="g-label">DSK</span><div class="g-bar"><div class="g-fill" style="width:91%;background:var(--err)"></div></div><span class="g-val">91%</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="statusbar">
|
||||
<div class="sb-mode">LIVE</div>
|
||||
<div class="sb-cell">6 agents</div>
|
||||
<div class="sb-cell">4 ok · 1 warn · 1 err</div>
|
||||
<div class="sb-right">22:14:05</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- OPTION C -->
|
||||
<div class="card-opt" data-choice="c" onclick="toggleSelect(this)">
|
||||
<div class="card-label">
|
||||
<div class="card-letter">C</div>
|
||||
<div>
|
||||
<div class="card-name">3 colonnes (ops-style)</div>
|
||||
<div class="card-sub">Tree nav · cockpit KPI · logs live</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-mock">
|
||||
<div class="m">
|
||||
<div class="mh"><div class="dot"></div><span class="title">NANOMETRICS</span><span class="clock">22:14:05</span></div>
|
||||
<div class="layout-c">
|
||||
<div class="tree-c">
|
||||
<div class="tc-group">AGENTS</div>
|
||||
<div class="tc-item active"><div class="tc-dot"></div><span class="tc-label">srv-01</span></div>
|
||||
<div class="tc-item"><div class="tc-dot warn"></div><span class="tc-label">srv-02</span></div>
|
||||
<div class="tc-item"><div class="tc-dot err"></div><span class="tc-label">rpi-03</span></div>
|
||||
<div class="tc-item"><div class="tc-dot"></div><span class="tc-label">nas-04</span></div>
|
||||
<div class="tc-item"><div class="tc-dot"></div><span class="tc-label">db-05</span></div>
|
||||
</div>
|
||||
<div class="cockpit-c">
|
||||
<div class="kpi-c">
|
||||
<div class="k-label">CPU</div>
|
||||
<div class="k-val">42<span style="font-size:10px;color:var(--ink-3)">%</span></div>
|
||||
<div class="g-bar"><div class="g-fill" style="width:42%"></div></div>
|
||||
</div>
|
||||
<div class="kpi-c">
|
||||
<div class="k-label">MÉMOIRE</div>
|
||||
<div class="k-val">74<span style="font-size:10px;color:var(--ink-3)">%</span></div>
|
||||
<div class="g-bar"><div class="g-fill warn" style="width:74%"></div></div>
|
||||
</div>
|
||||
<div class="kpi-c">
|
||||
<div class="k-label">DISQUE</div>
|
||||
<div class="k-val">31<span style="font-size:10px;color:var(--ink-3)">%</span></div>
|
||||
<div class="g-bar"><div class="g-fill" style="width:31%"></div></div>
|
||||
</div>
|
||||
<div class="kpi-c">
|
||||
<div class="k-label">UPTIME</div>
|
||||
<div class="k-val" style="font-size:11px;color:var(--ink-1)">14j 6h</div>
|
||||
<div class="k-sub">depuis dernier boot</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="logs-c">
|
||||
<div class="log-line ok">22:14:05 srv-01 online</div>
|
||||
<div class="log-line warn">22:13:58 srv-02 cpu>75%</div>
|
||||
<div class="log-line err">22:13:41 rpi-03 disk>90%</div>
|
||||
<div class="log-line">22:13:30 nas-04 online</div>
|
||||
<div class="log-line">22:13:22 db-05 online</div>
|
||||
<div class="log-line warn">22:12:55 srv-02 cpu>70%</div>
|
||||
<div class="log-line">22:12:40 web-06 online</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="statusbar">
|
||||
<div class="sb-mode">LIVE</div>
|
||||
<div class="sb-cell">srv-01 sélectionné</div>
|
||||
<div class="sb-cell">5 ok · 1 warn · 1 err</div>
|
||||
<div class="sb-right">22:14:05</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="selection-bar">
|
||||
<span>Nanometrics — Layout du dashboard</span>
|
||||
<span id="sel-text">Clique sur un layout pour le sélectionner</span>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1 @@
|
||||
{"type":"server-started","port":55731,"host":"0.0.0.0","url_host":"10.0.0.50","url":"http://10.0.0.50:55731","screen_dir":"/home/gilles/projects/nano_metrics/.superpowers/brainstorm/599687-1779425985/content","state_dir":"/home/gilles/projects/nano_metrics/.superpowers/brainstorm/599687-1779425985/state"}
|
||||
@@ -0,0 +1,26 @@
|
||||
{"type":"server-started","port":55731,"host":"0.0.0.0","url_host":"10.0.0.50","url":"http://10.0.0.50:55731","screen_dir":"/home/gilles/projects/nano_metrics/.superpowers/brainstorm/599687-1779425985/content","state_dir":"/home/gilles/projects/nano_metrics/.superpowers/brainstorm/599687-1779425985/state"}
|
||||
{"type":"screen-added","file":"/home/gilles/projects/nano_metrics/.superpowers/brainstorm/599687-1779425985/content/approaches.html"}
|
||||
{"source":"user-event","type":"click","text":"C\n \n SQLite + WebSocket ⭐ Recommandé\n SQLite — simplicité opérationnelle, suffisant pour 20+ agents avec rétention configurable.\n WebSocket — bidirectionnel dès le départ, sans surcoût opérationnel.\n \n AvantagesPas de conteneur DB supplémentaireWebSocket prêt pour extensions futuresSimple à debugger et sauvegarder\n LimitesRequêtes temporelles moins expressives qu'InfluxDBScalabilité limitée au-delà de ~100 agents","choice":"c","id":null,"timestamp":1779426035162}
|
||||
{"source":"user-event","type":"click","text":"C\n \n SQLite + WebSocket ⭐ Recommandé\n SQLite — simplicité opérationnelle, suffisant pour 20+ agents avec rétention configurable.\n WebSocket — bidirectionnel dès le départ, sans surcoût opérationnel.\n \n AvantagesPas de conteneur DB supplémentaireWebSocket prêt pour extensions futuresSimple à debugger et sauvegarder\n LimitesRequêtes temporelles moins expressives qu'InfluxDBScalabilité limitée au-delà de ~100 agents","choice":"c","id":null,"timestamp":1779426056446}
|
||||
{"source":"user-event","type":"click","text":"C\n \n SQLite + WebSocket ⭐ Recommandé\n SQLite — simplicité opérationnelle, suffisant pour 20+ agents avec rétention configurable.\n WebSocket — bidirectionnel dès le départ, sans surcoût opérationnel.\n \n AvantagesPas de conteneur DB supplémentaireWebSocket prêt pour extensions futuresSimple à debugger et sauvegarder\n LimitesRequêtes temporelles moins expressives qu'InfluxDBScalabilité limitée au-delà de ~100 agents","choice":"c","id":null,"timestamp":1779426057055}
|
||||
{"type":"screen-added","file":"/home/gilles/projects/nano_metrics/.superpowers/brainstorm/599687-1779425985/content/layout.html"}
|
||||
{"source":"user-event","type":"click","text":"A\n \n Grille uniforme\n Toutes les tuiles identiques, adaptatives\n \n \n \n \n NANOMETRICS22:14:05\n \n \n ⬡srv-01\n CPU42%\n RAM74%\n DSK31%\n \n \n ⬡srv-02\n CPU78%\n RAM55%\n DSK20%\n \n \n ⬡rpi-03\n CPU12%\n RAM38%\n DSK91%\n \n \n ⬡nas-04\n CPU8%\n RAM60%\n DSK44%\n \n \n \n LIVE\n 4 agents\n 3 ok · 1 warn\n 22:14:05","choice":"a","id":null,"timestamp":1779426189689}
|
||||
{"source":"user-event","type":"click","text":"A\n \n Grille uniforme\n Toutes les tuiles identiques, adaptatives\n \n \n \n \n NANOMETRICS22:14:05\n \n \n ⬡srv-01\n CPU42%\n RAM74%\n DSK31%\n \n \n ⬡srv-02\n CPU78%\n RAM55%\n DSK20%\n \n \n ⬡rpi-03\n CPU12%\n RAM38%\n DSK91%\n \n \n ⬡nas-04\n CPU8%\n RAM60%\n DSK44%\n \n \n \n LIVE\n 4 agents\n 3 ok · 1 warn\n 22:14:05","choice":"a","id":null,"timestamp":1779426247715}
|
||||
{"source":"user-event","type":"click","text":"A\n \n Grille uniforme\n Toutes les tuiles identiques, adaptatives\n \n \n \n \n NANOMETRICS22:14:05\n \n \n ⬡srv-01\n CPU42%\n RAM74%\n DSK31%\n \n \n ⬡srv-02\n CPU78%\n RAM55%\n DSK20%\n \n \n ⬡rpi-03\n CPU12%\n RAM38%\n DSK91%\n \n \n ⬡nas-04\n CPU8%\n RAM60%\n DSK44%\n \n \n \n LIVE\n 4 agents\n 3 ok · 1 warn\n 22:14:05","choice":"a","id":null,"timestamp":1779426255628}
|
||||
{"source":"user-event","type":"click","text":"A\n \n Grille uniforme\n Toutes les tuiles identiques, adaptatives\n \n \n \n \n NANOMETRICS22:14:05\n \n \n ⬡srv-01\n CPU42%\n RAM74%\n DSK31%\n \n \n ⬡srv-02\n CPU78%\n RAM55%\n DSK20%\n \n \n ⬡rpi-03\n CPU12%\n RAM38%\n DSK91%\n \n \n ⬡nas-04\n CPU8%\n RAM60%\n DSK44%\n \n \n \n LIVE\n 4 agents\n 3 ok · 1 warn\n 22:14:05","choice":"a","id":null,"timestamp":1779426320054}
|
||||
{"source":"user-event","type":"click","text":"A\n \n Grille uniforme\n Toutes les tuiles identiques, adaptatives\n \n \n \n \n NANOMETRICS22:14:05\n \n \n ⬡srv-01\n CPU42%\n RAM74%\n DSK31%\n \n \n ⬡srv-02\n CPU78%\n RAM55%\n DSK20%\n \n \n ⬡rpi-03\n CPU12%\n RAM38%\n DSK91%\n \n \n ⬡nas-04\n CPU8%\n RAM60%\n DSK44%\n \n \n \n LIVE\n 4 agents\n 3 ok · 1 warn\n 22:14:05","choice":"a","id":null,"timestamp":1779426600975}
|
||||
{"source":"user-event","type":"click","text":"B\n \n Sidebar + grille\n Liste des agents à gauche, tuiles à droite\n \n \n \n \n NANOMETRICS22:14:05\n \n \n srv-01\n srv-02\n rpi-03\n nas-04\n db-05\n web-06\n \n \n \n ⬡srv-01\n CPU42%\n RAM74%\n DSK31%\n \n \n ⬡srv-02\n CPU78%\n RAM55%\n DSK20%\n \n \n ⬡rpi-03\n CPU12%\n RAM38%\n DSK91%\n \n \n \n \n LIVE\n 6 agents\n 4 ok · 1 warn · 1 err\n 22:14:05","choice":"b","id":null,"timestamp":1779426601678}
|
||||
{"source":"user-event","type":"click","text":"C\n \n 3 colonnes (ops-style)\n Tree nav · cockpit KPI · logs live\n \n \n \n \n NANOMETRICS22:14:05\n \n \n AGENTS\n srv-01\n srv-02\n rpi-03\n nas-04\n db-05\n \n \n \n CPU\n 42%\n \n \n \n MÉMOIRE\n 74%\n \n \n \n DISQUE\n 31%\n \n \n \n UPTIME\n 14j 6h\n depuis dernier boot\n \n \n \n 22:14:05 srv-01 online\n 22:13:58 srv-02 cpu>75%\n 22:13:41 rpi-03 disk>90%\n 22:13:30 nas-04 online\n 22:13:22 db-05 online\n 22:12:55 srv-02 cpu>70%\n 22:12:40 web-06 online\n \n \n \n LIVE\n srv-01 sélectionné\n 5 ok · 1 warn · 1 err\n 22:14:05","choice":"c","id":null,"timestamp":1779426603020}
|
||||
{"source":"user-event","type":"click","text":"C\n \n 3 colonnes (ops-style)\n Tree nav · cockpit KPI · logs live\n \n \n \n \n NANOMETRICS22:14:05\n \n \n AGENTS\n srv-01\n srv-02\n rpi-03\n nas-04\n db-05\n \n \n \n CPU\n 42%\n \n \n \n MÉMOIRE\n 74%\n \n \n \n DISQUE\n 31%\n \n \n \n UPTIME\n 14j 6h\n depuis dernier boot\n \n \n \n 22:14:05 srv-01 online\n 22:13:58 srv-02 cpu>75%\n 22:13:41 rpi-03 disk>90%\n 22:13:30 nas-04 online\n 22:13:22 db-05 online\n 22:12:55 srv-02 cpu>70%\n 22:12:40 web-06 online\n \n \n \n LIVE\n srv-01 sélectionné\n 5 ok · 1 warn · 1 err\n 22:14:05","choice":"c","id":null,"timestamp":1779426603540}
|
||||
{"source":"user-event","type":"click","text":"B\n \n Sidebar + grille\n Liste des agents à gauche, tuiles à droite\n \n \n \n \n NANOMETRICS22:14:05\n \n \n srv-01\n srv-02\n rpi-03\n nas-04\n db-05\n web-06\n \n \n \n ⬡srv-01\n CPU42%\n RAM74%\n DSK31%\n \n \n ⬡srv-02\n CPU78%\n RAM55%\n DSK20%\n \n \n ⬡rpi-03\n CPU12%\n RAM38%\n DSK91%\n \n \n \n \n LIVE\n 6 agents\n 4 ok · 1 warn · 1 err\n 22:14:05","choice":"b","id":null,"timestamp":1779426604201}
|
||||
{"source":"user-event","type":"click","text":"A\n \n Grille uniforme\n Toutes les tuiles identiques, adaptatives\n \n \n \n \n NANOMETRICS22:14:05\n \n \n ⬡srv-01\n CPU42%\n RAM74%\n DSK31%\n \n \n ⬡srv-02\n CPU78%\n RAM55%\n DSK20%\n \n \n ⬡rpi-03\n CPU12%\n RAM38%\n DSK91%\n \n \n ⬡nas-04\n CPU8%\n RAM60%\n DSK44%\n \n \n \n LIVE\n 4 agents\n 3 ok · 1 warn\n 22:14:05","choice":"a","id":null,"timestamp":1779426605083}
|
||||
{"source":"user-event","type":"click","text":"A\n \n Grille uniforme\n Toutes les tuiles identiques, adaptatives\n \n \n \n \n NANOMETRICS22:14:05\n \n \n ⬡srv-01\n CPU42%\n RAM74%\n DSK31%\n \n \n ⬡srv-02\n CPU78%\n RAM55%\n DSK20%\n \n \n ⬡rpi-03\n CPU12%\n RAM38%\n DSK91%\n \n \n ⬡nas-04\n CPU8%\n RAM60%\n DSK44%\n \n \n \n LIVE\n 4 agents\n 3 ok · 1 warn\n 22:14:05","choice":"a","id":null,"timestamp":1779426608505}
|
||||
{"source":"user-event","type":"click","text":"C\n \n 3 colonnes (ops-style)\n Tree nav · cockpit KPI · logs live\n \n \n \n \n NANOMETRICS22:14:05\n \n \n AGENTS\n srv-01\n srv-02\n rpi-03\n nas-04\n db-05\n \n \n \n CPU\n 42%\n \n \n \n MÉMOIRE\n 74%\n \n \n \n DISQUE\n 31%\n \n \n \n UPTIME\n 14j 6h\n depuis dernier boot\n \n \n \n 22:14:05 srv-01 online\n 22:13:58 srv-02 cpu>75%\n 22:13:41 rpi-03 disk>90%\n 22:13:30 nas-04 online\n 22:13:22 db-05 online\n 22:12:55 srv-02 cpu>70%\n 22:12:40 web-06 online\n \n \n \n LIVE\n srv-01 sélectionné\n 5 ok · 1 warn · 1 err\n 22:14:05","choice":"c","id":null,"timestamp":1779426641935}
|
||||
{"type":"screen-added","file":"/home/gilles/projects/nano_metrics/.superpowers/brainstorm/599687-1779425985/content/layout-v2.html"}
|
||||
{"type":"screen-added","file":"/home/gilles/projects/nano_metrics/.superpowers/brainstorm/599687-1779425985/content/layout-v3.html"}
|
||||
{"type":"screen-added","file":"/home/gilles/projects/nano_metrics/.superpowers/brainstorm/599687-1779425985/content/layout-v4.html"}
|
||||
{"type":"screen-added","file":"/home/gilles/projects/nano_metrics/.superpowers/brainstorm/599687-1779425985/content/layout-v5.html"}
|
||||
{"type":"screen-added","file":"/home/gilles/projects/nano_metrics/.superpowers/brainstorm/599687-1779425985/content/layout-v6.html"}
|
||||
{"type":"screen-added","file":"/home/gilles/projects/nano_metrics/.superpowers/brainstorm/599687-1779425985/content/layout-v7.html"}
|
||||
{"type":"screen-added","file":"/home/gilles/projects/nano_metrics/.superpowers/brainstorm/599687-1779425985/content/layout-v8.html"}
|
||||
{"type":"screen-added","file":"/home/gilles/projects/nano_metrics/.superpowers/brainstorm/599687-1779425985/content/layout-v9.html"}
|
||||
@@ -0,0 +1 @@
|
||||
599695
|
||||
Reference in New Issue
Block a user