Files
nano_metrics/.superpowers/brainstorm/599687-1779425985/content/layout-v6.html
T
Gilles Soulier a0f47bf966 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>
2026-05-22 08:13:53 +02:00

552 lines
43 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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>