Files
nano_metrics/.superpowers/brainstorm/599687-1779425985/content/layout-v5.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

578 lines
34 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 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>