673 lines
57 KiB
HTML
673 lines
57 KiB
HTML
<!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.82: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>
|