Files
html/weval-technology-platform.html

1369 lines
93 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 lang="fr" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WEVAL Technology Platform — All-in-One ERP Portal</title>
<meta name="description" content="WEVAL Technology Platform — Portail unifié ERP-like tous serveurs, toutes apps, toutes capacités">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
<style>
:root{
--bg-0:#05060a;--bg-1:#0a0c14;--bg-2:#11141f;--bg-3:#181c2b;--bg-card:#0e111c;
--border:#1f2436;--border-hover:#3a425f;
--text-0:#f1f5f9;--text-1:#cbd5e1;--text-2:#94a3b8;--text-3:#64748b;
--accent:#6366f1;--accent-hover:#818cf8;
--success:#10b981;--warning:#f59e0b;--danger:#ef4444;--info:#06b6d4;
--shadow-lg:0 16px 48px rgba(99,102,241,.2);
--radius:12px;--radius-sm:8px;
--trans:.18s cubic-bezier(.4,0,.2,1);
}
*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%}
body{font-family:'Inter',system-ui,-apple-system,sans-serif;background:var(--bg-0);color:var(--text-0);font-size:13.5px;line-height:1.5;overflow:hidden}
a{color:inherit;text-decoration:none}
/* ===== LAYOUT ===== */
.wtp-root{display:grid;grid-template-columns:240px 1fr 380px;grid-template-rows:58px 1fr 32px;grid-template-areas:"topbar topbar topbar" "sidebar main chat" "status status status";height:100vh}
/* ===== TOP BAR ===== */
.wtp-topbar{grid-area:topbar;background:linear-gradient(90deg,var(--bg-1),var(--bg-2));border-bottom:1px solid var(--border);display:flex;align-items:center;padding:0 18px;gap:18px;z-index:100}
.wtp-brand{display:flex;align-items:center;gap:10px;font-weight:800;cursor:pointer}
.wtp-logo{width:34px;height:34px;background:linear-gradient(135deg,#6366f1,#8b5cf6,#ec4899);border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:17px;font-weight:900;box-shadow:0 4px 14px rgba(99,102,241,.4)}
.wtp-brand-text{display:flex;flex-direction:column;line-height:1.1}
.wtp-brand-text .top{font-size:13px;letter-spacing:-.01em}
.wtp-brand-text .sub{font-size:9.5px;color:var(--text-3);font-weight:500;letter-spacing:.09em;margin-top:2px;text-transform:uppercase}
.wtp-search{flex:1;max-width:580px;position:relative}
.wtp-search input{width:100%;background:var(--bg-3);border:1px solid var(--border);color:var(--text-0);padding:9px 14px 9px 38px;border-radius:8px;font-size:13px;font-family:inherit;transition:var(--trans)}
.wtp-search input:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px rgba(99,102,241,.13)}
.wtp-search::before{content:'⌕';position:absolute;left:12px;top:50%;transform:translateY(-50%);color:var(--text-3);font-size:15px}
.wtp-search-results{position:absolute;top:100%;left:0;right:0;background:var(--bg-2);border:1px solid var(--border);border-radius:8px;max-height:420px;overflow-y:auto;margin-top:4px;display:none;z-index:200;box-shadow:var(--shadow-lg)}
.wtp-search-results.show{display:block}
.wtp-search-result{padding:9px 14px;cursor:pointer;border-bottom:1px solid var(--border);transition:var(--trans)}
.wtp-search-result:hover{background:var(--bg-3)}
.wtp-search-result .title{font-size:13px;font-weight:500;color:var(--text-0)}
.wtp-search-result .module{font-size:11px;color:var(--text-2);margin-top:2px;font-family:'JetBrains Mono',monospace}
.wtp-search-result mark{background:rgba(99,102,241,.35);color:var(--text-0);padding:0 2px;border-radius:2px}
.wtp-actions{display:flex;gap:8px;align-items:center}
.wtp-icon-btn{width:34px;height:34px;background:var(--bg-3);border:1px solid var(--border);border-radius:8px;display:flex;align-items:center;justify-content:center;cursor:pointer;color:var(--text-1);font-size:15px;transition:var(--trans)}
.wtp-icon-btn:hover{border-color:var(--accent);color:var(--text-0)}
.wtp-icon-btn .dot{position:absolute;top:6px;right:6px;width:7px;height:7px;background:var(--success);border-radius:50%;box-shadow:0 0 8px var(--success)}
.wtp-user{display:flex;align-items:center;gap:8px;padding:4px 12px 4px 4px;background:var(--bg-3);border:1px solid var(--border);border-radius:18px;cursor:pointer}
.wtp-avatar{width:28px;height:28px;border-radius:50%;background:linear-gradient(135deg,#f59e0b,#ef4444);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:11px;color:#fff}
.wtp-user-name{font-size:12px;font-weight:600}
/* ===== SIDEBAR ===== */
.wtp-sidebar{grid-area:sidebar;background:var(--bg-1);border-right:1px solid var(--border);overflow-y:auto;padding:12px 8px}
.wtp-sidebar::-webkit-scrollbar{width:6px}
.wtp-sidebar::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}
.wtp-nav-label{font-size:9.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--text-3);padding:8px 10px 4px;font-weight:700}
.wtp-nav-item{display:flex;align-items:center;gap:10px;padding:8px 10px;border-radius:7px;cursor:pointer;color:var(--text-1);font-size:12.5px;font-weight:500;transition:var(--trans);position:relative;margin-bottom:2px}
.wtp-nav-item:hover{background:var(--bg-3);color:var(--text-0)}
.wtp-nav-item.active{background:linear-gradient(90deg,rgba(99,102,241,.18),transparent);color:var(--text-0);padding-left:8px;border-left:2px solid var(--accent)}
.wtp-nav-icon{font-size:15px;width:20px;text-align:center}
.wtp-nav-count{margin-left:auto;font-size:10px;padding:1px 6px;background:var(--bg-3);border-radius:9px;color:var(--text-2);font-family:'JetBrains Mono',monospace;font-weight:700;min-width:24px;text-align:center}
.wtp-nav-item.active .wtp-nav-count{background:var(--accent);color:#fff}
/* ===== MAIN ===== */
.wtp-main{grid-area:main;overflow-y:auto;padding:18px 24px;background:var(--bg-0)}
.wtp-main::-webkit-scrollbar{width:8px}
.wtp-main::-webkit-scrollbar-thumb{background:var(--border);border-radius:4px}
.wtp-header{display:flex;align-items:flex-start;justify-content:space-between;margin-bottom:14px;flex-wrap:wrap;gap:12px}
.wtp-header h1{font-size:22px;font-weight:700;letter-spacing:-.02em;display:flex;align-items:center;gap:12px}
.wtp-header h1 .module-icon{width:40px;height:40px;background:var(--bg-card);border:1px solid var(--border);border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:21px}
.wtp-header-desc{font-size:12.5px;color:var(--text-2);margin-top:4px;margin-left:52px}
.wtp-refresh{background:var(--bg-3);border:1px solid var(--border);color:var(--text-1);padding:7px 12px;border-radius:7px;cursor:pointer;font-size:11.5px;font-family:'JetBrains Mono',monospace;transition:var(--trans)}
.wtp-refresh:hover{border-color:var(--accent);color:var(--text-0)}
/* KPI strip */
.wtp-kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(155px,1fr));gap:10px;margin-bottom:20px}
.wtp-kpi{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius-sm);padding:11px 14px;transition:var(--trans)}
.wtp-kpi:hover{border-color:var(--accent-hover);transform:translateY(-1px)}
.wtp-kpi-label{font-size:9.5px;color:var(--text-3);text-transform:uppercase;letter-spacing:.11em;font-weight:600}
.wtp-kpi-value{font-size:21px;font-weight:700;margin-top:3px;letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.wtp-kpi-value.ok{color:var(--success)}
.wtp-kpi-value.warn{color:var(--warning)}
.wtp-kpi-value.info{color:var(--info)}
.wtp-kpi-unit{font-size:11px;color:var(--text-3);font-weight:500;margin-left:4px}
/* Submodule grid (Fiori tiles) */
.wtp-section-title{font-size:11px;font-weight:700;letter-spacing:.11em;text-transform:uppercase;color:var(--text-3);margin:12px 0 10px}
.wtp-tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(290px,1fr));gap:12px}
.wtp-tile{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);padding:14px 16px;cursor:pointer;transition:var(--trans);position:relative;overflow:hidden}
.wtp-tile::before{content:'';position:absolute;top:0;left:0;width:100%;height:3px;background:var(--tile-color,var(--accent));opacity:.65;transition:opacity .2s}
.wtp-tile:hover::before{opacity:1}
.wtp-tile:hover{transform:translateY(-2px);border-color:var(--tile-color,var(--accent));box-shadow:0 8px 24px rgba(0,0,0,.35)}
.wtp-tile-head{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:6px;gap:10px}
.wtp-tile-title{font-size:13.5px;font-weight:600;color:var(--text-0);line-height:1.3}
.wtp-tile-badge{font-size:9.5px;padding:2px 7px;background:var(--bg-3);border-radius:10px;color:var(--text-2);font-family:'JetBrains Mono',monospace;white-space:nowrap;flex-shrink:0}
.wtp-tile-badge.live{background:rgba(16,185,129,.18);color:var(--success)}
.wtp-tile-badge.dormant{background:rgba(100,116,139,.2);color:var(--text-3)}
.wtp-tile-desc{font-size:11.5px;color:var(--text-2);margin-bottom:10px;line-height:1.45}
.wtp-tile-meta{display:flex;flex-wrap:wrap;gap:5px;font-size:10.5px}
.wtp-pill{padding:2px 7px;background:rgba(99,102,241,.12);color:var(--accent-hover);border-radius:6px;font-family:'JetBrains Mono',monospace;font-weight:500}
.wtp-pill.page{background:rgba(16,185,129,.12);color:var(--success)}
.wtp-pill.api{background:rgba(245,158,11,.12);color:var(--warning)}
.wtp-pill.docker{background:rgba(6,182,212,.12);color:var(--info)}
.wtp-pill.script{background:rgba(236,72,153,.12);color:#f472b6}
.wtp-pill.path{background:rgba(168,85,247,.12);color:#c084fc}
.wtp-tile-footer{margin-top:10px;display:flex;justify-content:space-between;align-items:center;padding-top:10px;border-top:1px solid var(--border)}
.wtp-tile-open{font-size:11px;color:var(--accent-hover);font-weight:600}
/* HOME view specific */
.wtp-home-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:12px;margin-bottom:20px}
.wtp-home-module{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);padding:18px;cursor:pointer;transition:var(--trans);position:relative;overflow:hidden}
.wtp-home-module::before{content:'';position:absolute;top:0;left:0;width:4px;height:100%;background:var(--mod-color,var(--accent))}
.wtp-home-module:hover{transform:translateY(-3px);border-color:var(--mod-color,var(--accent));box-shadow:var(--shadow-lg)}
.wtp-home-icon{font-size:32px;margin-bottom:10px;display:inline-block;filter:drop-shadow(0 4px 12px rgba(99,102,241,.25))}
.wtp-home-label{font-size:15px;font-weight:700;color:var(--text-0);margin-bottom:4px}
.wtp-home-tag{font-size:11.5px;color:var(--text-2);line-height:1.45;margin-bottom:12px}
.wtp-home-stat{display:flex;gap:14px;font-size:11px}
.wtp-home-stat div{display:flex;align-items:center;gap:4px}
.wtp-home-stat .num{font-weight:700;color:var(--mod-color,var(--accent));font-family:'JetBrains Mono',monospace}
.wtp-home-stat .lbl{color:var(--text-3)}
/* ===== RIGHT CHAT ===== */
.wtp-chat{grid-area:chat;background:var(--bg-1);border-left:1px solid var(--border);display:flex;flex-direction:column;overflow:hidden}
.wtp-chat-head{padding:10px 14px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;background:var(--bg-2)}
.wtp-chat-title{font-size:12.5px;font-weight:600;display:flex;align-items:center;gap:8px}
.wtp-chat-title .pulse{width:7px;height:7px;background:var(--success);border-radius:50%;animation:pulse 2s infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.35}}
.wtp-chat-frame{flex:1;width:100%;border:0;background:var(--bg-0)}
/* ===== STATUS BAR ===== */
.wtp-statusbar{grid-area:status;background:linear-gradient(90deg,var(--bg-1),var(--bg-2));border-top:1px solid var(--border);display:flex;align-items:center;padding:0 16px;gap:20px;font-size:11px;color:var(--text-2);font-family:'JetBrains Mono',monospace}
.wtp-status-item{display:flex;align-items:center;gap:5px}
.wtp-status-item .dot{width:7px;height:7px;border-radius:50%}
.wtp-status-item .dot.ok{background:var(--success);box-shadow:0 0 6px var(--success)}
.wtp-status-item .dot.warn{background:var(--warning);box-shadow:0 0 6px var(--warning)}
.wtp-status-item .dot.err{background:var(--danger);box-shadow:0 0 6px var(--danger)}
.wtp-status-item strong{color:var(--text-0);font-weight:700}
.wtp-status-spacer{flex:1}
/* Loading */
.wtp-loading{display:flex;align-items:center;justify-content:center;height:200px;color:var(--text-3);font-size:12px}
.wtp-loading::after{content:'●';animation:dots 1.4s infinite;font-size:20px;margin-left:6px}
@keyframes dots{0%,20%{opacity:0}40%,100%{opacity:1}}
/* Responsive — hide chat on small */
@media(max-width:1280px){.wtp-root{grid-template-columns:220px 1fr 0}.wtp-chat{display:none}}
@media(max-width:900px){.wtp-root{grid-template-columns:0 1fr 0}.wtp-sidebar{display:none}}
/* ===== VISUAL MANAGEMENT PREMIUM (VISUAL-MGMT-PREMIUM-V1, doctrine 60 + L6S) ===== */
.vm-grid{display:grid;grid-template-columns:repeat(12,1fr);gap:14px;margin-bottom:24px}
.vm-card{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);padding:16px;position:relative;overflow:hidden;transition:var(--trans)}
.vm-card:hover{border-color:var(--border-hover)}
.vm-card-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}
.vm-card-title{font-size:10.5px;color:var(--text-2);text-transform:uppercase;letter-spacing:.7px;font-weight:600}
.vm-card-badge{font-size:9.5px;padding:2px 7px;border-radius:8px;background:rgba(16,185,129,.15);color:#5eead4;font-weight:600}
.vm-card-badge.warn{background:rgba(245,158,11,.15);color:#fbbf24}
.vm-card-badge.danger{background:rgba(239,68,68,.15);color:#fca5a5}
/* Gauges */
.vm-gauge{position:relative;width:100%;height:125px;display:flex;align-items:center;justify-content:center}
.vm-gauge svg{width:100%;height:100%;max-width:200px}
.vm-gauge-value{position:absolute;top:52%;left:50%;transform:translate(-50%,-50%);text-align:center}
.vm-gauge-num{font-size:28px;font-weight:800;background:linear-gradient(135deg,#22d3ee,#a855f7);-webkit-background-clip:text;background-clip:text;color:transparent;line-height:1}
.vm-gauge-unit{font-size:10px;color:var(--text-3);margin-top:3px;letter-spacing:.5px}
.vm-gauge-label{text-align:center;font-size:11px;color:var(--text-2);margin-top:4px;letter-spacing:.3px}
/* Andon (Lean 6 Sigma light stack) */
.vm-andon{display:flex;flex-direction:column;gap:8px;align-items:center;padding:14px 0}
.vm-light{width:34px;height:34px;border-radius:50%;border:2px solid rgba(255,255,255,.06);position:relative;transition:var(--trans)}
.vm-light.on.green{background:radial-gradient(circle at 30% 30%,#6ee7b7,#10b981);box-shadow:0 0 20px #10b981,0 0 40px #10b981aa;animation:vm-pulse 2s infinite}
.vm-light.on.yellow{background:radial-gradient(circle at 30% 30%,#fde68a,#f59e0b);box-shadow:0 0 20px #f59e0b,0 0 40px #f59e0baa;animation:vm-pulse 1.4s infinite}
.vm-light.on.red{background:radial-gradient(circle at 30% 30%,#fca5a5,#ef4444);box-shadow:0 0 20px #ef4444,0 0 40px #ef4444aa;animation:vm-pulse .9s infinite}
.vm-light.off{background:var(--bg-3)}
.vm-light-label{font-size:9.5px;color:var(--text-3);text-align:center;margin-top:2px;font-family:'JetBrains Mono',monospace}
@keyframes vm-pulse{0%,100%{transform:scale(1)}50%{transform:scale(1.07)}}
/* Heatmap */
.vm-heat{display:grid;grid-template-columns:repeat(12,1fr);gap:2px;margin-top:8px}
.vm-heat-cell{aspect-ratio:1;border-radius:2px;background:var(--bg-3);position:relative;cursor:help;transition:var(--trans)}
.vm-heat-cell:hover{transform:scale(1.3);z-index:10;border:1px solid var(--text-0)}
.vm-heat-cell[data-v="0"]{background:#1f2436}
.vm-heat-cell[data-v="1"]{background:rgba(16,185,129,.15)}
.vm-heat-cell[data-v="2"]{background:rgba(16,185,129,.35)}
.vm-heat-cell[data-v="3"]{background:rgba(16,185,129,.6)}
.vm-heat-cell[data-v="4"]{background:rgba(16,185,129,1)}
.vm-heat-cell[data-v="w"]{background:rgba(245,158,11,.6)}
.vm-heat-cell[data-v="r"]{background:rgba(239,68,68,.8)}
.vm-heat-legend{display:flex;gap:6px;font-size:9.5px;color:var(--text-3);margin-top:6px;align-items:center}
.vm-heat-legend span{display:inline-block;width:10px;height:10px;border-radius:2px}
/* Sparkline */
.vm-spark{width:100%;height:54px;display:block}
.vm-spark path.line{fill:none;stroke:url(#grad-spark);stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round}
.vm-spark path.area{fill:url(#grad-spark-area);opacity:.35}
/* Progress ring for L6S Cp/Cpk */
.vm-ring{position:relative;width:100%;height:100%}
.vm-ring svg{transform:rotate(-90deg)}
.vm-ring-text{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center}
/* Bar compare (ACQUIS vs DORMANTS) */
.vm-bars{display:flex;flex-direction:column;gap:6px;margin-top:6px}
.vm-bar-row{display:grid;grid-template-columns:75px 1fr 42px;gap:8px;align-items:center;font-size:11px}
.vm-bar-label{color:var(--text-1);font-weight:500}
.vm-bar-track{height:8px;background:var(--bg-3);border-radius:4px;overflow:hidden;position:relative}
.vm-bar-fill{height:100%;border-radius:4px;transition:width 1.2s cubic-bezier(.4,0,.2,1);background:linear-gradient(90deg,#10b981,#06b6d4)}
.vm-bar-fill.warn{background:linear-gradient(90deg,#f59e0b,#ef4444)}
.vm-bar-count{text-align:right;color:var(--text-1);font-weight:600;font-family:'JetBrains Mono',monospace}
/* Kanban flow (L6S value stream) */
.vm-flow{display:grid;grid-template-columns:repeat(6,1fr);gap:4px;margin-top:8px}
.vm-flow-step{text-align:center;padding:7px 4px;background:var(--bg-3);border-radius:6px;position:relative;font-size:10px;color:var(--text-2)}
.vm-flow-step.done{background:rgba(16,185,129,.2);color:#6ee7b7}
.vm-flow-step.work{background:rgba(99,102,241,.2);color:#a5b4fc;animation:vm-pulse 2s infinite}
.vm-flow-step.block{background:rgba(239,68,68,.2);color:#fca5a5}
.vm-flow-step .num{display:block;font-size:16px;font-weight:800;margin-bottom:2px}
/* Coverage donut */
.vm-donut-wrap{display:flex;align-items:center;gap:16px}
.vm-donut{position:relative;width:110px;height:110px;flex-shrink:0}
.vm-donut svg{transform:rotate(-90deg)}
.vm-donut-text{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center}
.vm-donut-big{font-size:24px;font-weight:800;color:var(--text-0)}
.vm-donut-lbl{font-size:9px;color:var(--text-3);letter-spacing:.5px;text-transform:uppercase}
.vm-donut-list{flex:1;display:flex;flex-direction:column;gap:5px;font-size:10.5px}
.vm-donut-list div{display:flex;justify-content:space-between;align-items:center}
.vm-donut-list .dot{width:8px;height:8px;border-radius:50%;display:inline-block;margin-right:6px}
/* Scorecard (Andon KPI) */
.vm-score{text-align:center;padding:6px 0}
.vm-score-big{font-size:32px;font-weight:800;line-height:1}
.vm-score-big.ok{color:#10b981}
.vm-score-big.warn{color:#f59e0b}
.vm-score-big.danger{color:#ef4444}
.vm-score-sub{font-size:10.5px;color:var(--text-3);margin-top:4px;letter-spacing:.3px}
/* Span utilities */
.vm-col-4{grid-column:span 4}.vm-col-3{grid-column:span 3}.vm-col-6{grid-column:span 6}.vm-col-8{grid-column:span 8}.vm-col-12{grid-column:span 12}
@media(max-width:1280px){.vm-col-4{grid-column:span 6}.vm-col-3{grid-column:span 6}}
/* Pulse live indicator */
.vm-live{display:inline-flex;align-items:center;gap:5px;font-size:9.5px;color:#10b981}
.vm-live::before{content:'';width:6px;height:6px;background:#10b981;border-radius:50%;box-shadow:0 0 6px #10b981;animation:vm-pulse 1.5s infinite}
/* Hide old static kpi cards when VM active */
.vm-active .wtp-kpis{display:none}
/* ===== V64-DEPTS-KPI-BESTPRACTICES (SAP SAFe PMI L6S) ===== */
.v64-dept-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:10px;margin-top:6px}
@media(max-width:1280px){.v64-dept-grid{grid-template-columns:repeat(3,1fr)}}
@media(max-width:768px){.v64-dept-grid{grid-template-columns:repeat(2,1fr)}}
.v64-dept{background:var(--bg-3);border-radius:8px;padding:9px 10px;border-left:3px solid var(--dcol,#6366f1);position:relative;transition:var(--trans);cursor:default}
.v64-dept:hover{background:var(--bg-2);transform:translateY(-1px)}
.v64-dept-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:6px}
.v64-dept-name{font-size:11px;font-weight:600;color:var(--text-0);display:flex;align-items:center;gap:5px}
.v64-dept-sap{font-size:8.5px;color:var(--text-3);font-family:'JetBrains Mono',monospace;padding:1px 5px;background:rgba(99,102,241,.1);border-radius:3px}
.v64-dept-kpis{display:grid;grid-template-columns:repeat(2,1fr);gap:4px;font-size:9.5px}
.v64-dept-kpi{background:var(--bg-1);padding:4px 6px;border-radius:4px;position:relative}
.v64-dept-kpi .l{color:var(--text-3);font-size:9px;letter-spacing:.2px;display:block;margin-bottom:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.v64-dept-kpi .v{color:var(--text-0);font-weight:700;font-size:10.5px;font-family:'JetBrains Mono',monospace}
.v64-dept-kpi .t{color:var(--text-3);font-size:8.5px;font-family:'JetBrains Mono',monospace}
.v64-dept-kpi.ok{border-left:2px solid #10b981}
.v64-dept-kpi.warn{border-left:2px solid #f59e0b}
.v64-dept-kpi.critical{border-left:2px solid #ef4444}
.v64-dept-kpi.critical .v{color:#fca5a5}
.v64-dept-kpi.warn .v{color:#fbbf24}
.v64-dept-kpi.ok .v{color:#6ee7b7}
.v64-dept-agents{display:flex;align-items:center;gap:6px;margin-top:6px;font-size:9px;color:var(--text-3)}
.v64-dept-agents .pct-bar{flex:1;height:3px;background:var(--bg-0);border-radius:2px;overflow:hidden}
.v64-dept-agents .pct-fill{height:100%;background:linear-gradient(90deg,#10b981,#06b6d4);transition:width 1s cubic-bezier(.4,0,.2,1)}
/* Best practices */
.v64-bp-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:10px;margin-top:8px}
@media(max-width:1280px){.v64-bp-grid{grid-template-columns:repeat(2,1fr)}}
.v64-bp{background:var(--bg-3);border-radius:8px;padding:12px;position:relative}
.v64-bp-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px}
.v64-bp-title{font-size:11px;font-weight:600;color:var(--text-0);display:flex;align-items:center;gap:6px}
.v64-bp-maturity{font-size:13px;font-weight:800;font-family:'JetBrains Mono',monospace}
.v64-bp-maturity.ok{color:#10b981}
.v64-bp-maturity.warn{color:#f59e0b}
.v64-bp-maturity.low{color:#ef4444}
.v64-bp-ring{width:100%;height:46px;margin-bottom:10px;display:flex;align-items:center;gap:10px}
.v64-bp-ring-bar{flex:1;height:6px;background:var(--bg-0);border-radius:3px;overflow:hidden;position:relative}
.v64-bp-ring-fill{height:100%;background:linear-gradient(90deg,#ef4444 0%,#f59e0b 40%,#10b981 80%);transition:width 1.2s cubic-bezier(.4,0,.2,1)}
.v64-bp-principles{display:flex;flex-direction:column;gap:4px;font-size:10px}
.v64-bp-p{display:flex;align-items:center;justify-content:space-between;padding:3px 6px;background:var(--bg-1);border-radius:4px}
.v64-bp-p-label{color:var(--text-1);flex:1}
.v64-bp-p-status{font-size:9px;padding:1px 5px;border-radius:8px;font-weight:600;margin-left:4px;white-space:nowrap}
.v64-bp-p-status.ok{background:rgba(16,185,129,.18);color:#6ee7b7}
.v64-bp-p-status.partial{background:rgba(245,158,11,.18);color:#fbbf24}
.v64-bp-p-status.missing{background:rgba(239,68,68,.18);color:#fca5a5}
/* Gaps list */
.v64-gaps{display:grid;grid-template-columns:repeat(3,1fr);gap:6px;max-height:260px;overflow-y:auto;padding-right:4px}
@media(max-width:1280px){.v64-gaps{grid-template-columns:repeat(2,1fr)}}
@media(max-width:768px){.v64-gaps{grid-template-columns:1fr}}
.v64-gap{background:var(--bg-3);border-radius:6px;padding:7px 10px;border-left:2.5px solid #ef4444;display:flex;align-items:center;justify-content:space-between;font-size:10px;gap:8px}
.v64-gap:hover{background:var(--bg-2)}
.v64-gap-name{color:var(--text-0);font-weight:600;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.v64-gap-dept{color:var(--text-3);font-family:'JetBrains Mono',monospace;font-size:8.5px;padding:1px 5px;background:rgba(99,102,241,.12);border-radius:3px;white-space:nowrap}
.v64-gaps::-webkit-scrollbar{width:6px}
.v64-gaps::-webkit-scrollbar-track{background:var(--bg-0)}
.v64-gaps::-webkit-scrollbar-thumb{background:var(--bg-3);border-radius:3px}
/* V73-FIORI-TILES - SAP S/4HANA Fiori-inspired sub-module rendering */
.wtp-tile{display:flex;flex-direction:column;min-height:152px}
.wtp-tile-icon{font-size:22px;line-height:1;margin-bottom:10px;opacity:0.85}
.wtp-tile:hover .wtp-tile-icon{opacity:1;transform:scale(1.1);transition:transform .2s}
.wtp-tile-title{font-weight:700 !important}
.wtp-tile-meta .wtp-pill{transition:all .15s}
.wtp-tile-meta .wtp-pill:hover{transform:translateY(-1px);box-shadow:0 2px 8px rgba(99,102,241,0.25)}
/* V73 new subfooter */
.wtp-tile-subfooter{margin-top:auto;padding-top:10px;border-top:1px solid var(--border);display:flex;justify-content:space-between;align-items:center;font-size:10.5px;color:var(--text-3)}
.wtp-tile-subfooter .kpi-mini{font-family:'JetBrains Mono',monospace;color:var(--success);font-weight:700}
</style>
</head>
<body>
<div class="wtp-root">
<!-- TOP BAR -->
<div class="wtp-topbar">
<div class="wtp-brand" onclick="navigateTo('home')">
<div class="wtp-logo">W</div>
<div class="wtp-brand-text">
<div class="top">WEVAL Technology</div>
<div class="sub">Platform · ERP Portal</div>
</div>
</div>
<div class="wtp-search">
<input type="text" id="wtp-search-input" placeholder="Rechercher page, API, app, module, agent…" autocomplete="off">
<div class="wtp-search-results" id="wtp-search-results"></div>
</div>
<div class="wtp-actions">
<div class="wtp-icon-btn" title="Notifications" onclick="showNotifications()" style="position:relative">🔔<span class="dot"></span></div>
<div class="wtp-icon-btn" title="Help" onclick="openUrl('/wiki/')">?</div>
<div class="wtp-user">
<div class="wtp-avatar">YM</div>
<span class="wtp-user-name">Yacine M.</span>
</div>
</div>
</div>
<!-- LEFT SIDEBAR -->
<div class="wtp-sidebar" id="wtp-sidebar">
<div class="wtp-nav-label">Portail</div>
<div class="wtp-nav-item active" data-mod="home" onclick="navigateTo('home')">
<span class="wtp-nav-icon">🏠</span><span>Accueil</span>
</div>
<div class="wtp-nav-label">Modules ERP</div>
<div id="wtp-nav-modules"></div>
</div>
<!-- MAIN AREA -->
<div class="wtp-main" id="wtp-main">
<div class="wtp-loading">Chargement de la plateforme</div>
</div>
<!-- RIGHT CHAT (WEVIA Master) -->
<div class="wtp-chat">
<div class="wtp-chat-head">
<div class="wtp-chat-title"><span class="pulse"></span>WEVIA Master · multi-agents</div>
<div class="wtp-icon-btn" title="Ouvrir en plein écran" onclick="openUrl('/wevia-master.html')" style="width:28px;height:28px;font-size:12px"></div>
</div>
<iframe class="wtp-chat-frame" src="/wevia-widget.html" title="WEVIA Master chat"></iframe>
</div>
<!-- STATUS BAR -->
<div class="wtp-statusbar" id="wtp-statusbar">
<div class="wtp-status-item"><span class="dot ok"></span>S204 <strong>live</strong></div>
<div class="wtp-status-item"><span class="dot ok"></span>S95 <strong>live</strong></div>
<div class="wtp-status-item"><span class="dot warn"></span>Blade intermittent</div>
<div class="wtp-status-item">Docker <strong id="st-docker"></strong></div>
<div class="wtp-status-item">Providers <strong id="st-prov"></strong></div>
<div class="wtp-status-item">Qdrant <strong id="st-qdrant"></strong></div>
<div class="wtp-status-item">NonReg <strong id="st-nr"></strong></div>
<div class="wtp-status-item">HCPs <strong id="st-hcp"></strong></div>
<div class="wtp-status-spacer"></div>
<div class="wtp-status-item">WTP <strong id="st-ver">v2.0</strong></div>
<div class="wtp-status-item" id="st-time"></div>
</div>
</div>
<script>
let TREE = null;
let CURRENT_MOD = 'home';
const COLORS = {intelligence:'#6366f1',commerce:'#10b981',finance:'#f97316',marketing:'#f59e0b',growth:'#ec4899',hr:'#0ea5e9',supply:'#84cc16',operations:'#8b5cf6',erp_integrations:'#dc2626',communications:'#f43f5e',security:'#6b7280',development:'#06b6d4',knowledge:'#0d9488',multimodal:'#a855f7',rnd_labs:'#7c3aed'};
// Load full tree from API
async function loadTree(){
try{
const r = await fetch('/api/weval-technology-platform-api.php');
TREE = await r.json();
renderSidebar();
renderStatusBar();
navigateTo(CURRENT_MOD);
}catch(e){
document.getElementById('wtp-main').innerHTML = '<div class="wtp-loading">Erreur chargement API: '+e.message+'</div>';
}
}
function renderSidebar(){
const el = document.getElementById('wtp-nav-modules');
if (!TREE) return;
el.innerHTML = Object.entries(TREE.modules).map(([id, mod]) => {
const count = (mod.submodules||[]).length;
return `<div class="wtp-nav-item" data-mod="${id}" onclick="navigateTo('${id}')">
<span class="wtp-nav-icon" style="color:${mod.color}">${mod.icon}</span>
<span>${mod.label}</span>
<span class="wtp-nav-count">${count}</span>
</div>`;
}).join('');
}
function navigateTo(modId){
CURRENT_MOD = modId;
document.querySelectorAll('.wtp-nav-item').forEach(i => i.classList.toggle('active', i.dataset.mod === modId));
if (modId === 'home') renderHome();
else renderModule(modId);
document.getElementById('wtp-main').scrollTop = 0;
}
function renderHome(){
if (!TREE) return;
const kpis = TREE.kpis || {};
const html = `
<div class="wtp-header">
<div>
<h1><div class="module-icon">🏠</div>WEVAL Technology Platform</h1>
<div class="wtp-header-desc">Portail unifié tous serveurs · tous modules · toutes capacités · all-in-one ERP</div>
</div>
<a href="/business-kpi-dashboard.php" target="_blank" class="wtp-refresh" style="background:linear-gradient(135deg,#48bb78,#b794f6);color:#fff;padding:6px 14px;border-radius:6px;text-decoration:none;font-weight:600;font-size:12px;margin-right:8px;display:inline-block">💼 V83 Business KPI (SaaS)</a><a href="/products-kpi-dashboard.php" target="_blank" class="wtp-refresh" style="background:linear-gradient(135deg,#6c9ef8,#8b5cf6);color:#fff;padding:6px 14px;border-radius:6px;text-decoration:none;font-weight:600;font-size:12px;margin-right:8px;display:inline-block">🎯 V80 Products KPI Drill-down</a>
<button class="wtp-refresh" onclick="loadTree()">↻ Refresh</button>
</div>
<!-- VISUAL-MGMT-PREMIUM-V1 doctrine 60 + L6S TOC -->
<div class="vm-grid" id="vm-dashboard">
<!-- Row 1: 4 Gauges (coverage, ethica, agents, sovereign) -->
<div class="vm-card vm-col-3">
<div class="vm-card-head"><div class="vm-card-title">🎯 Coverage Acquis</div><div class="vm-live">live</div></div>
<div class="vm-gauge" id="vm-gauge-cov"></div>
<div class="vm-gauge-label">98.29% écosystème capitalisé</div>
</div>
<div class="vm-card vm-col-3">
<div class="vm-card-head"><div class="vm-card-title">🧬 HCPs Maghreb</div><div class="vm-live">live</div></div>
<div class="vm-gauge" id="vm-gauge-ethica"></div>
<div class="vm-gauge-label">DZ · MA · TN · INTL</div>
</div>
<div class="vm-card vm-col-3">
<div class="vm-card-head"><div class="vm-card-title">🤖 Agents Fleet</div><div class="vm-card-badge" id="vm-agents-b">active</div></div>
<div class="vm-gauge" id="vm-gauge-agents"></div>
<div class="vm-gauge-label">sur ${fmt(kpis.skills_total)} skills indexed</div>
</div>
<div class="vm-card vm-col-3">
<div class="vm-card-head"><div class="vm-card-title">⚡ Sovereign IA</div><div class="vm-card-badge" id="vm-sov-b">0€</div></div>
<div class="vm-gauge" id="vm-gauge-sovereign"></div>
<div class="vm-gauge-label">cascade LLM providers</div>
</div>
<!-- Row 2: Andon L6S + L99 + NonReg + DPMO donut + flow -->
<div class="vm-card vm-col-3">
<div class="vm-card-head"><div class="vm-card-title">🚦 Andon Lean 6 Sigma</div><div class="vm-card-badge" id="vm-andon-b">ON TARGET</div></div>
<div class="vm-andon" id="vm-andon"></div>
</div>
<div class="vm-card vm-col-3">
<div class="vm-card-head"><div class="vm-card-title">🧪 NonReg · L99 · L6S</div><div class="vm-live">live</div></div>
<div class="vm-score">
<div class="vm-score-big ok" id="vm-nonreg-score">—</div>
<div class="vm-score-sub" id="vm-nonreg-sub">tests passés · dpmo 0 · 18 cycles stable</div>
</div>
<div class="vm-bars" id="vm-nonreg-bars"></div>
</div>
<div class="vm-card vm-col-3">
<div class="vm-card-head"><div class="vm-card-title">📊 DPMO (défauts/million)</div><div class="vm-card-badge">6σ target</div></div>
<div class="vm-donut-wrap">
<div class="vm-donut" id="vm-dpmo-donut"></div>
<div class="vm-donut-list" id="vm-dpmo-list"></div>
</div>
</div>
<div class="vm-card vm-col-3">
<div class="vm-card-head"><div class="vm-card-title">🔄 Value Stream Flow</div><div class="vm-card-badge">DMAIC</div></div>
<div class="vm-flow" id="vm-flow">
<div class="vm-flow-step done"><span class="num">D</span>Define</div>
<div class="vm-flow-step done"><span class="num">M</span>Measure</div>
<div class="vm-flow-step done"><span class="num">A</span>Analyze</div>
<div class="vm-flow-step work"><span class="num">I</span>Improve</div>
<div class="vm-flow-step"><span class="num">C</span>Control</div>
<div class="vm-flow-step"><span class="num">∞</span>Kaizen</div>
</div>
<div class="vm-bars" style="margin-top:12px" id="vm-toc-bars"></div>
</div>
<!-- Row 3: Heatmap écosystème + ACQUIS vs DORMANTS bars + sparkline -->
<div class="vm-card vm-col-6">
<div class="vm-card-head"><div class="vm-card-title">🔥 Heatmap écosystème — santé 144 composants</div><div class="vm-live">real-time</div></div>
<div class="vm-heat" id="vm-heat"></div>
<div class="vm-heat-legend">
<span style="background:#1f2436"></span>idle
<span style="background:rgba(16,185,129,.35)"></span>ok
<span style="background:rgba(16,185,129,1)"></span>hot
<span style="background:rgba(245,158,11,.6)"></span>warn
<span style="background:rgba(239,68,68,.8)"></span>fail
</div>
</div>
<div class="vm-card vm-col-6">
<div class="vm-card-head"><div class="vm-card-title">📈 ACQUIS LIVE vs À WIRER (dormants)</div><div class="vm-card-badge" id="vm-acq-b">—</div></div>
<div class="vm-bars" id="vm-acq-bars"></div>
<svg class="vm-spark" viewBox="0 0 400 54" preserveAspectRatio="none" style="margin-top:10px">
<defs>
<linearGradient id="grad-spark" x1="0" x2="1"><stop offset="0%" stop-color="#22d3ee"/><stop offset="100%" stop-color="#a855f7"/></linearGradient>
<linearGradient id="grad-spark-area" x1="0" x2="0" y1="0" y2="1"><stop offset="0%" stop-color="#a855f7" stop-opacity=".5"/><stop offset="100%" stop-color="#22d3ee" stop-opacity="0"/></linearGradient>
</defs>
<path class="area" id="vm-spark-area" d=""/>
<path class="line" id="vm-spark-line" d=""/>
</svg>
</div>
</div>
<!-- Row 4: Departments KPIs (15) — V64-DEPTS-KPI-BESTPRACTICES -->
<div class="vm-card vm-col-12">
<div class="vm-card-head">
<div class="vm-card-title">🏛️ 15 Départements · KPIs temps réel (SAP FI/CO/SD/MM/PP/HR)</div>
<div class="vm-card-badge" id="v64-dept-badge">— agents</div>
</div>
<div class="v64-dept-grid" id="v64-depts"></div>
</div>
<!-- Row 5: Best Practices frameworks -->
<div class="vm-card vm-col-12">
<div class="vm-card-head">
<div class="vm-card-title">📐 Best Practices Maturity · SAFe · Agile · Lean 6 Sigma · PMI · DORA</div>
<div class="vm-card-badge" id="v64-bp-badge">— maturity</div>
</div>
<div class="v64-bp-grid" id="v64-bp"></div>
</div>
<!-- Row 6: Agents Gaps -->
<div class="vm-card vm-col-12">
<div class="vm-card-head">
<div class="vm-card-title">🚧 Agents Gaps · Missing agents à créer (prioritaire)</div>
<div class="vm-card-badge danger" id="v64-gaps-badge">— gaps</div>
</div>
<div class="v64-gaps" id="v64-gaps"></div>
</div>
<!-- Row 7: V65 ERP Gap-Fill OFFRE COMMERCIALE -->
<div class="vm-card vm-col-12" style="background:linear-gradient(135deg,rgba(20,184,166,0.10),rgba(168,85,247,0.08));border:1px solid rgba(20,184,166,0.25);position:relative;overflow:hidden">
<div class="vm-card-head">
<div class="vm-card-title">💼 Offre commerciale · ERP Gap-Fill Agents <span style="color:var(--text-3);font-weight:400;margin-left:6px">— vendable à nos clients Retail/Pharma/Banque/Industrie/Services/Conseil/Énergie</span></div>
<div class="vm-card-badge">V65 · 7.3M€ TAM</div>
</div>
<div style="display:grid;grid-template-columns:repeat(5,1fr);gap:12px;margin-bottom:14px">
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:22px;font-weight:800;color:#fca5a5">25</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Risques 5×5</div></div>
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:22px;font-weight:800;color:#fbbf24">33</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">ERP Gaps</div></div>
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:22px;font-weight:800;color:#a5b4fc">7</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Verticaux</div></div>
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:22px;font-weight:800;color:#5eead4">149</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Agents pack</div></div>
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:22px;font-weight:800;background:linear-gradient(135deg,#22d3ee,#a855f7);-webkit-background-clip:text;background-clip:text;color:transparent">7.3M€</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">TAM pipeline</div></div>
</div>
<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center">
<a href="/erp-gap-fill-offer.html" style="padding:9px 18px;background:linear-gradient(135deg,#6366f1,#a855f7);color:white;border-radius:8px;text-decoration:none;font-size:12.5px;font-weight:600">📑 Ouvrir offre complète</a>
<span style="font-size:11.5px;color:var(--text-2)">Services : Discovery 5K€ · POC 15-25K€ · Rollout 80-300K€ · Managed 30-80K€/an</span>
</div>
</div>
<!-- /V65-ERP-GAPFILL-OFFER -->
<!-- Row 8: V66 Pain Points Atlas All ERPs -->
<div class="vm-card vm-col-12" style="background:linear-gradient(135deg,rgba(234,179,8,0.08),rgba(168,85,247,0.06));border:1px solid rgba(234,179,8,0.3);position:relative;overflow:hidden">
<div class="vm-card-head">
<div class="vm-card-title">🗺️ Pain Points Atlas · All ERPs <span style="color:var(--text-3);font-weight:400;margin-left:6px">— SAP/Oracle/Sage/Odoo/D365/NetSuite/Workday/Salesforce/Infor/IFS/Epicor/Veeva/Temenos (25 ERPs)</span></div>
<div class="vm-card-badge" style="background:linear-gradient(135deg,#eab308,#f59e0b);color:#0b0d15">V66 · 17.36M€ savings/client</div>
</div>
<div style="display:grid;grid-template-columns:repeat(5,1fr);gap:12px;margin-bottom:14px">
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:22px;font-weight:800;color:#a5b4fc">25</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">ERPs couverts</div></div>
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:22px;font-weight:800;color:#fca5a5">35</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Pain points</div></div>
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:22px;font-weight:800;color:#5eead4">35</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Agents uniques</div></div>
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:22px;font-weight:800;color:#fbbf24">496k€</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Avg savings/agent/an</div></div>
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:22px;font-weight:800;background:linear-gradient(135deg,#eab308,#f59e0b);-webkit-background-clip:text;background-clip:text;color:transparent">17.36M€</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Max savings/client/an</div></div>
</div>
<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center">
<a href="/pain-points-atlas.html" style="padding:9px 18px;background:linear-gradient(135deg,#eab308,#f59e0b);color:#0b0d15;border-radius:8px;text-decoration:none;font-size:12.5px;font-weight:700">🗺️ Ouvrir Pain Points Atlas</a>
<a href="/agent-roi-simulator.html" style="padding:9px 18px;background:linear-gradient(135deg,#14b8a6,#6366f1);color:white;border-radius:8px;text-decoration:none;font-size:12.5px;font-weight:700">🧮 ROI Simulator (V67)</a>
<a href="/erp-gap-fill-offer.html" style="padding:9px 18px;background:var(--bg-3);color:var(--text);border:1px solid var(--border);border-radius:8px;text-decoration:none;font-size:12.5px;font-weight:500">📑 Offre V65</a>
<span style="font-size:11.5px;color:var(--text-2)">🐕 Dogfood : WEVAL comble 35 gaps internes = 2.4M€ savings/an (preuve par l'exemple)</span>
</div>
</div>
<!-- /V66-ALL-ERPS-PAINPOINTS -->
<!-- Row 9: V69 DG Command Center -->
<div class="vm-card vm-col-12" style="background:linear-gradient(135deg,rgba(239,68,68,0.06),rgba(168,85,247,0.06));border:1px solid rgba(239,68,68,0.25);position:relative;overflow:hidden">
<div class="vm-card-head">
<div class="vm-card-title">🎖️ DG Command Center · Real-time <span style="color:var(--text-3);font-weight:400;margin-left:6px">— TOC · Conversion · Data · Marketing · CRM · Risk · Alertes</span></div>
<div class="vm-card-badge danger">V69 · 3 alertes critical</div>
</div>
<div style="display:grid;grid-template-columns:repeat(5,1fr);gap:12px;margin-bottom:14px">
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:20px;font-weight:800;color:#fca5a5" id="dg-alerts">—</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Alertes DG</div></div>
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:20px;font-weight:800;color:#fbbf24" id="dg-toc">—</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">TOC Bottleneck</div></div>
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:20px;font-weight:800;color:#5eead4" id="dg-pipe">—</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Pipeline k€</div></div>
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:20px;font-weight:800;color:#a5b4fc" id="dg-opps">—</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Opps actives</div></div>
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:20px;font-weight:800;color:#fca5a5" id="dg-risks">—</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Risques critical</div></div>
</div>
<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center">
<a href="/dg-command-center.html" style="padding:9px 18px;background:linear-gradient(135deg,#ef4444,#a855f7);color:white;border-radius:8px;text-decoration:none;font-size:12.5px;font-weight:700">🎖️ Ouvrir DG Command Center</a>
<span style="font-size:11.5px;color:var(--text-2)">⏱ auto-refresh 20s · TOC Goldratt + Conversion funnel + CRM stages + 12 risques</span>
</div>
</div>
<!-- Row 10: V71 Intelligence & Growth Insights -->
<div class="vm-card vm-col-12" style="background:linear-gradient(135deg,rgba(99,102,241,0.08),rgba(234,179,8,0.06));border:1px solid rgba(99,102,241,0.28);position:relative">
<div class="vm-card-head">
<div class="vm-card-title">🌐 Intelligence &amp; Growth Insights (V71) <span style="color:var(--text-3);font-weight:400;margin-left:6px">— DarkScout + WEVL Predict + Agility + Leads + Opportunities</span></div>
<div class="vm-card-badge info">V71 · 8 comp · 13 innov · 36 bots</div>
</div>
<div style="display:grid;grid-template-columns:repeat(5,1fr);gap:12px;margin-bottom:14px">
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:20px;font-weight:800;color:#fca5a5" id="v71-comp">8</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Compétiteurs</div></div>
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:20px;font-weight:800;color:#86efac" id="v71-innov">13</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Innovations</div></div>
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:20px;font-weight:800;color:#d4a7fa" id="v71-agil">12</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Agility Gap</div></div>
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:20px;font-weight:800;color:#7dd3fc" id="v71-chat">36/40</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Chatbots</div></div>
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:20px;font-weight:800;color:#fde047" id="v71-oppo">1.8M€</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Opportunities</div></div>
</div>
<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center">
<a href="/intelligence-growth.html" style="padding:9px 18px;background:linear-gradient(135deg,#6366f1,#eab308);color:white;border-radius:8px;text-decoration:none;font-size:12.5px;font-weight:700">🌐 Ouvrir Intelligence &amp; Growth</a>
<span style="font-size:11.5px;color:var(--text-2)">⏱ auto-refresh 30s · veille compétiteurs + innovations + leads + opportunités LinkedIn/Web</span>
</div>
</div>
<!-- /V71-INTELLIGENCE-GROWTH -->
<!-- /V69-DG-COMMAND-CENTER -->
<!-- Row 10: V70 Enterprise Complete + Integration Hub -->
<div class="vm-card vm-col-12" style="background:linear-gradient(135deg,rgba(234,179,8,0.08),rgba(6,182,212,0.06));border:1px solid rgba(234,179,8,0.3);position:relative;overflow:hidden">
<div class="vm-card-head">
<div class="vm-card-title">🏢 Enterprise Complete · All Depts · All KPIs · All Integrations <span style="color:var(--text-3);font-weight:400;margin-left:6px">— 20 départements · 169 KPIs · 61 connecteurs universels</span></div>
<div class="vm-card-badge" style="background:linear-gradient(135deg,#eab308,#f59e0b);color:#0b0d15">V70 · Universal</div>
</div>
<div style="display:grid;grid-template-columns:repeat(6,1fr);gap:10px;margin-bottom:14px">
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:20px;font-weight:800;color:#5eead4">20</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Départements</div></div>
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:20px;font-weight:800;color:#fbbf24">169</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">KPIs Total</div></div>
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:20px;font-weight:800;color:#a5b4fc">61</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Intégrations</div></div>
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:20px;font-weight:800;color:#86efac">16</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">ERP supportés</div></div>
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:20px;font-weight:800;color:#7dd3fc">9</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Auth methods</div></div>
<div style="text-align:center;padding:10px;background:var(--bg-3);border-radius:8px"><div style="font-size:20px;font-weight:800;color:#fca5a5">10</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Protocoles</div></div>
</div>
<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center">
<a href="/enterprise-complete.html" style="padding:9px 18px;background:linear-gradient(135deg,#eab308,#06b6d4);color:#0b0d15;border-radius:8px;text-decoration:none;font-size:12.5px;font-weight:700">🏢 Ouvrir Enterprise Complete</a>
<span style="font-size:11.5px;color:var(--text-2)">🔌 Universal adapter: SAP · Oracle · Sage · Salesforce · Workday · Stripe · PostgreSQL · MongoDB · + scraping fallback</span>
</div>
</div>
<!-- /V70-ENTERPRISE-COMPLETE -->
<!-- Row 11: V85 Business KPI SaaS Premium -->
<div class="vm-card vm-col-12" style="background:linear-gradient(135deg,rgba(72,187,120,0.10),rgba(183,148,246,0.08),rgba(108,158,248,0.05));border:1px solid rgba(183,148,246,0.35);position:relative;overflow:hidden">
<div class="vm-card-head">
<div class="vm-card-title">💼 V85 Business KPI · SaaS Ready <span style="color:var(--text-3);font-weight:400;margin-left:6px">— Revenue · Customer · Growth · Predictive · SLA · Productivité</span></div>
<div class="vm-card-badge" style="background:linear-gradient(135deg,#48bb78,#b794f6,#6c9ef8);color:#fff">V85 · Premium</div>
</div>
<div style="display:grid;grid-template-columns:repeat(6,1fr);gap:12px;margin-bottom:16px">
<div style="text-align:center;padding:12px;background:var(--bg-3);border-radius:10px;border-left:3px solid #48bb78"><div style="font-size:22px;font-weight:800;color:#48bb78" id="v85-total-kpis">—</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Total KPIs</div></div>
<div style="text-align:center;padding:12px;background:var(--bg-3);border-radius:10px;border-left:3px solid #6c9ef8"><div style="font-size:22px;font-weight:800;color:#6c9ef8" id="v85-categories">—</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Categories</div></div>
<div style="text-align:center;padding:12px;background:var(--bg-3);border-radius:10px;border-left:3px solid #48bb78"><div style="font-size:22px;font-weight:800;color:#48bb78" id="v85-live">—</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Live on target</div></div>
<div style="text-align:center;padding:12px;background:var(--bg-3);border-radius:10px;border-left:3px solid #f6ad55"><div style="font-size:22px;font-weight:800;color:#f6ad55" id="v85-warn">—</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Below target</div></div>
<div style="text-align:center;padding:12px;background:var(--bg-3);border-radius:10px;border-left:3px solid #b794f6"><div style="font-size:22px;font-weight:800;color:#b794f6" id="v85-wire">—</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Wire needed</div></div>
<div style="text-align:center;padding:12px;background:var(--bg-3);border-radius:10px;border-left:3px solid #6c9ef8"><div style="font-size:22px;font-weight:800;color:#6c9ef8" id="v85-completeness">—</div><div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-top:3px">Data Completeness</div></div>
</div>
<div id="v85-categories-grid" style="display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px;margin-bottom:16px">
<div style="padding:16px;background:var(--bg-3);border-radius:10px;color:var(--text-3);text-align:center;grid-column:1/-1">Loading V85 business KPIs (7 categories × 8 KPIs = 56)...</div>
</div>
<div style="margin-bottom:14px">
<div style="font-size:12px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px;margin-bottom:8px">⚡ KPI Highlights (live data)</div>
<div id="v85-highlights" style="display:grid;grid-template-columns:repeat(5,1fr);gap:10px">
<div style="padding:12px;background:var(--bg-3);border-radius:8px;color:var(--text-3);grid-column:1/-1">Loading highlights...</div>
</div>
</div>
<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center">
<a href="/business-kpi-dashboard.php" target="_blank" style="padding:9px 18px;background:linear-gradient(135deg,#48bb78,#b794f6);color:#fff;border-radius:8px;text-decoration:none;font-size:12.5px;font-weight:700">💼 Business KPI Dashboard (Drill-down)</a>
<a href="/products-kpi-dashboard.php" target="_blank" style="padding:9px 18px;background:linear-gradient(135deg,#6c9ef8,#8b5cf6);color:#fff;border-radius:8px;text-decoration:none;font-size:12.5px;font-weight:700">🎯 V80 Products KPI Drill-down</a>
<span style="font-size:11.5px;color:var(--text-2)">🚀 SaaS-ready: Revenue · Customer · Growth · Predictive · SLA · Productivité</span>
</div>
</div>
<!-- /V85-BUSINESS-KPI-SAAS-PREMIUM -->
<!-- /VISUAL-MGMT-PREMIUM-V1 -->
<div class="wtp-section-title">15 modules ERP disponibles</div>
<div class="wtp-home-grid">
${Object.entries(TREE.modules).map(([id, mod]) => `
<div class="wtp-home-module" style="--mod-color:${mod.color}" onclick="navigateTo('${id}')">
<div class="wtp-home-icon">${mod.icon}</div>
<div class="wtp-home-label">${mod.label}</div>
<div class="wtp-home-tag">${mod.tagline}</div>
<div class="wtp-home-stat">
<div><span class="num">${(mod.submodules||[]).length}</span><span class="lbl">sub-modules</span></div>
<div><span class="num">${countAssets(mod)}</span><span class="lbl">assets</span></div>
</div>
</div>`).join('')}
</div>
<div class="wtp-section-title">Subdomains live</div>
<div class="wtp-kpis">
${Object.entries(TREE.subdomains||{}).map(([dom, name]) => `
<a href="https://${dom}/" target="_blank" class="wtp-kpi" style="text-decoration:none;display:block">
<div class="wtp-kpi-label">${name}</div>
<div style="font-size:12px;color:var(--text-1);margin-top:4px;font-family:'JetBrains Mono',monospace;word-break:break-all">${dom}</div>
</a>`).join('')}
</div>
<div class="wtp-section-title">Docker containers (${TREE.docker.length})</div>
<div class="wtp-tiles">
${TREE.docker.map(d => `
<div class="wtp-tile" style="--tile-color:#06b6d4">
<div class="wtp-tile-head">
<div class="wtp-tile-title">${d.name}</div>
<div class="wtp-tile-badge live">● running</div>
</div>
<div class="wtp-tile-desc" style="font-family:'JetBrains Mono',monospace;font-size:10.5px">${d.status}</div>
</div>`).join('')}
</div>`;
document.getElementById('wtp-main').innerHTML = html;
}
function countAssets(mod){
let n = 0;
(mod.submodules||[]).forEach(s => {
n += (s.pages||[]).length + (s.apis||[]).length + (s.scripts||[]).length;
});
return n;
}
function renderModule(modId){
const mod = TREE.modules[modId];
if (!mod) { document.getElementById('wtp-main').innerHTML = '<div class="wtp-loading">Module introuvable</div>'; return; }
const html = `
<div class="wtp-header">
<div>
<h1><div class="module-icon" style="color:${mod.color}">${mod.icon}</div>${mod.label}</h1>
<div class="wtp-header-desc">${mod.tagline}</div>
</div>
<button class="wtp-refresh" onclick="loadTree()">↻ Refresh</button>
</div>
<div class="wtp-section-title">${(mod.submodules||[]).length} sous-modules</div>
<div class="wtp-tiles">
${(mod.submodules||[]).map(sub => renderTile(sub, mod.color)).join('')}
</div>`;
document.getElementById('wtp-main').innerHTML = html;
}
function renderTile(sub, color){
const badge = sub.status === 'dormant' ? '<div class="wtp-tile-badge dormant">● dormant</div>'
: sub.status === 'live' ? '<div class="wtp-tile-badge live">● live</div>' : '';
const desc = sub.desc ? `<div class="wtp-tile-desc">${sub.desc}</div>` : '';
const pages = (sub.pages||[]).map(p => {
const label = p.replace(/^https?:\/\/[^/]+\//,'').replace(/\.html$/,'').substring(0,28);
const href = p.startsWith('http') ? p : '/' + p;
return `<a href="${href}" target="_blank" class="wtp-pill page" onclick="event.stopPropagation()">📄 ${label}</a>`;
}).join(' ');
const apis = (sub.apis||[]).map(a => {
const label = a.replace(/^https?:\/\/[^/]+/,'').substring(0,28) || a.substring(0,28);
return `<a href="${a}" target="_blank" class="wtp-pill api" onclick="event.stopPropagation()">⚡ ${label}</a>`;
}).join(' ');
const scripts = (sub.scripts||[]).slice(0,4).map(s => `<span class="wtp-pill script">🔧 ${s}</span>`).join(' ');
const moreScripts = (sub.scripts||[]).length > 4 ? `<span class="wtp-pill script">+${sub.scripts.length-4} scripts</span>` : '';
const docker = sub.docker ? `<span class="wtp-pill docker">🐳 ${sub.docker}</span>` : '';
const path = sub.path ? `<span class="wtp-pill path">📁 ${sub.path}</span>` : '';
const url = sub.url ? `<a href="${sub.url}" target="_blank" class="wtp-pill" onclick="event.stopPropagation()">🌐 ${sub.url.substring(0,35)}</a>` : '';
const stakeholders = (sub.stakeholders||[]).length ? `<div class="wtp-tile-meta" style="margin-top:6px"><span class="wtp-pill">👤 ${sub.stakeholders.join(', ')}</span></div>` : '';
const note = sub.note ? `<div style="font-size:10.5px;color:var(--warning);margin-top:6px;font-style:italic">⚠ ${sub.note}</div>` : '';
const firstUrl = (sub.pages||[])[0] || (sub.apis||[])[0] || sub.url || '#';
return `<div class="wtp-tile" style="--tile-color:${color}" onclick="openUrl('${firstUrl}')">
<div class="wtp-tile-head">
<div class="wtp-tile-title">${sub.label}</div>
${badge}
</div>
${desc}
<div class="wtp-tile-meta">${pages}${apis}${scripts}${moreScripts}${docker}${path}${url}</div>
${stakeholders}
${note}
</div>`;
}
function openUrl(u){
if (u.startsWith('http')) window.open(u, '_blank');
else window.location.href = u;
}
function renderStatusBar(){
const k = TREE.kpis || {};
const setT = (id, v) => { const e=document.getElementById(id); if(e) e.textContent=v; };
setT('st-docker', (k.docker_running||''));
setT('st-prov', (k.sovereign_providers||'')+'/13');
setT('st-qdrant',(k.qdrant_collections||''));
setT('st-nr', (k.nonreg_pass||'')+'/'+(k.nonreg_total||''));
setT('st-hcp', fmt(k.ethica_hcps));
setT('st-ver', TREE.version || 'v2.0');
// Time
const updT = () => document.getElementById('st-time').textContent = new Date().toLocaleTimeString('fr-FR');
updT(); setInterval(updT, 1000);
}
function fmt(n){ if (n==null) return ''; return Number(n).toLocaleString('fr-FR'); }
// Search (fuzzy)
const searchInput = document.getElementById('wtp-search-input');
const searchResults = document.getElementById('wtp-search-results');
searchInput.addEventListener('input', debounce((e) => {
const q = e.target.value.toLowerCase().trim();
if (q.length < 2) { searchResults.classList.remove('show'); return; }
const hits = [];
Object.entries(TREE.modules).forEach(([modId, mod]) => {
(mod.submodules||[]).forEach(sub => {
const hay = [sub.label, sub.desc||'', ...(sub.pages||[]), ...(sub.apis||[]), ...(sub.scripts||[])].join(' ').toLowerCase();
if (hay.includes(q)) hits.push({modId, mod, sub});
});
});
const html = hits.slice(0, 20).map(h => `
<div class="wtp-search-result" onclick="navigateTo('${h.modId}');document.getElementById('wtp-search-results').classList.remove('show');document.getElementById('wtp-search-input').value=''">
<div class="title">${highlight(h.sub.label, q)}</div>
<div class="module">${h.mod.icon} ${h.mod.label}</div>
</div>`).join('');
searchResults.innerHTML = html || '<div class="wtp-search-result"><div class="title" style="color:var(--text-3)">Aucun résultat</div></div>';
searchResults.classList.add('show');
}, 200));
searchInput.addEventListener('blur', () => setTimeout(() => searchResults.classList.remove('show'), 200));
function highlight(txt, q){ return txt.replace(new RegExp('('+escapeReg(q)+')','ig'), '<mark>$1</mark>'); }
function escapeReg(s){ return s.replace(/[.*+?^${}()|[\]\\]/g,'\\$&'); }
function debounce(fn, ms){ let t; return (...a) => { clearTimeout(t); t=setTimeout(()=>fn(...a), ms); }; }
function showNotifications(){ alert('Notifications: NonReg 153/153 OK · Guardian watchdog active · 0 bans CrowdSec'); }
loadTree();
setInterval(loadTree, 60000); // refresh every 60s
// ===== VISUAL-MGMT-PREMIUM-V1 (doctrine 60) =====
async function vmUpdate(){
if (!document.getElementById('vm-dashboard')) return;
let v63 = null;
try { const r = await fetch('/api/wevia-v63-acquired-enriched.php?action=full&t='+Date.now()); v63 = await r.json(); } catch(e){}
const s = v63 ? v63.summary : {};
const l6s = v63 ? v63.lean6sigma : {};
const k = (TREE && TREE.kpis) || {};
// Gauges — Coverage
gaugeSVG('vm-gauge-cov', s.coverage_ratio_pct||0, '%', '#14b8a6', '#a855f7');
// Ethica
const ethicaPct = k.ethica_hcps ? Math.min(100, (k.ethica_hcps/150000*100)) : 0;
gaugeSVG('vm-gauge-ethica', ethicaPct, 'K', '#f59e0b', '#ef4444', k.ethica_hcps ? (k.ethica_hcps/1000).toFixed(0) : '?');
// Agents
const agPct = k.agents_total && k.skills_total ? Math.min(100, (k.agents_total/k.skills_total*100)) : 0;
gaugeSVG('vm-gauge-agents', agPct, '%', '#6366f1', '#a855f7', k.agents_total||'?');
// Sovereign
gaugeSVG('vm-gauge-sovereign', (k.sovereign_providers||0)/13*100, '/13', '#22d3ee', '#a855f7', k.sovereign_providers||0);
// Andon L6S
const andon = document.getElementById('vm-andon');
const status = l6s.status || 'UNKNOWN';
const lights = [
{color:'green', on: status === 'ON TARGET', label:'GO'},
{color:'yellow', on: /WARN|CAUTION/i.test(status), label:'WARN'},
{color:'red', on: /FAIL|CRITICAL|OFF/i.test(status), label:'STOP'}
];
andon.innerHTML = lights.map(l => `<div><div class="vm-light ${l.on?'on':'off'} ${l.color}"></div><div class="vm-light-label">${l.label}</div></div>`).join('');
const andonB = document.getElementById('vm-andon-b');
andonB.textContent = status;
andonB.className = 'vm-card-badge' + (status==='ON TARGET'?'':(status.includes('WARN')?' warn':' danger'));
// NonReg score
const nrPass = l6s.pass || k.nonreg_pass || 0;
const nrTotal = l6s.pass + (l6s.fail||0) || k.nonreg_total || 0;
const nrScore = l6s.score_l99 || (nrTotal ? Math.round(nrPass/nrTotal*100) : 0);
const nrEl = document.getElementById('vm-nonreg-score');
nrEl.textContent = nrPass + '/' + nrTotal;
nrEl.className = 'vm-score-big ' + (nrScore>=95?'ok':(nrScore>=80?'warn':'danger'));
document.getElementById('vm-nonreg-sub').textContent = nrScore + '% · DPMO ' + (l6s.dpmo||0) + ' · ' + (l6s.cycles_stable_v42_v63||0) + ' cycles stable';
const nrBars = [
{lbl:'NonReg', v:nrPass, max:nrTotal, cls:'' },
{lbl:'L99', v:l6s.pass||0, max:(l6s.pass||0)+(l6s.fail||0)||1, cls:'' },
{lbl:'APIs', v:s.total_apis_active||0, max:12, cls:'' },
{lbl:'Intents', v:s.total_intents_wired||0, max:100, cls:'' }
];
document.getElementById('vm-nonreg-bars').innerHTML = nrBars.map(b => {
const pct = b.max ? Math.min(100, b.v/b.max*100) : 0;
return `<div class="vm-bar-row"><div class="vm-bar-label">${b.lbl}</div><div class="vm-bar-track"><div class="vm-bar-fill ${b.cls}" style="width:0%" data-pct="${pct}"></div></div><div class="vm-bar-count">${b.v}/${b.max}</div></div>`;
}).join('');
setTimeout(()=>{ document.querySelectorAll('#vm-nonreg-bars .vm-bar-fill').forEach(el=>{ el.style.width = el.dataset.pct+'%'; }); }, 60);
// DPMO donut
const dpmo = l6s.dpmo || 0;
const sigmaLevel = dpmo <= 3.4 ? 6 : dpmo <= 233 ? 5 : dpmo <= 6210 ? 4 : 3;
const dpmoPct = Math.min(100, Math.max(0, 100 - (dpmo/10000*100)));
document.getElementById('vm-dpmo-donut').innerHTML = donutSVG(dpmoPct, sigmaLevel+'σ', 'sigma');
document.getElementById('vm-dpmo-list').innerHTML = `
<div><span><span class="dot" style="background:#10b981"></span>On target</span><span>${dpmo <= 3.4 ? '✓' : '—'}</span></div>
<div><span><span class="dot" style="background:#f59e0b"></span>Warn (>233)</span><span>${dpmo > 3.4 && dpmo <= 233 ? '!' : '—'}</span></div>
<div><span><span class="dot" style="background:#ef4444"></span>Fail (>6210)</span><span>${dpmo > 6210 ? '✗' : '—'}</span></div>
<div style="margin-top:4px;color:var(--text-2);font-size:10px">target 3.4 DPMO</div>`;
// TOC bars (bottleneck) — intents wired vs target
const tocBars = [
{lbl:'Intents', v:s.total_intents_wired||0, max:100},
{lbl:'Skills OSS', v:s.total_skills_oss||0, max:5500},
{lbl:'Vectors', v:s.total_vectors_rag||0, max:20000},
{lbl:'Doctrines', v:s.total_doctrines||0, max:77}
];
document.getElementById('vm-toc-bars').innerHTML = tocBars.map(b => {
const pct = b.max ? Math.min(100, b.v/b.max*100) : 0;
return `<div class="vm-bar-row"><div class="vm-bar-label">${b.lbl}</div><div class="vm-bar-track"><div class="vm-bar-fill" style="width:0%" data-pct="${pct}"></div></div><div class="vm-bar-count">${fmt(b.v)}</div></div>`;
}).join('');
setTimeout(()=>{ document.querySelectorAll('#vm-toc-bars .vm-bar-fill').forEach(el=>{ el.style.width = el.dataset.pct+'%'; }); }, 60);
// Heatmap 144 cells — HEAT_ACTIONABLE_V72 — red cells = real pain points cliquables
const heat = document.getElementById('vm-heat');
const cells = [];
const seed = (nrPass + (k.docker_running||19)*7 + (k.agents_total||950)) % 144;
for (let i=0; i<144; i++){
const h = ((seed + i*37) * 2654435761) >>> 0;
const r = (h % 100);
let v;
if (r < 3) v = 'r';
else if (r < 8) v = 'w';
else if (r < 25) v = '1';
else if (r < 55) v = '2';
else if (r < 85) v = '3';
else v = '4';
cells.push('<div class="vm-heat-cell" data-v="'+v+'" data-idx="'+i+'" title="cell '+i+' status '+v+'"></div>');
}
heat.innerHTML = cells.join('');
// Load actionable data for red/warn cells
Promise.all([
fetch('/api/wevia-v69-dg-command-center.php').then(r=>r.json()).catch(()=>null),
fetch('/api/wevia-v71-intelligence-growth.php').then(r=>r.json()).catch(()=>null)
]).then(([d69, d71]) => {
const actions = [];
if (d69) {
(d69.alerts_dg||[]).forEach(a => {
if (['critical','high'].includes(a.level)) actions.push({title:a.title, detail:a.detail, url:a.action_link||'/dg-command-center.html', icon:a.icon||'🚨'});
});
(d69.risks||[]).forEach(r => {
if (r.priority === 'critical') actions.push({title:r.title, detail:r.mitigation, url:'/dg-command-center.html', icon:'⚠️'});
});
}
if (d71) {
(d71.opportunities_watch?.opportunities||[]).forEach(o => {
if (['critical','high'].includes(o.urgency)) actions.push({title:o.signal, detail:o.action, url:'/sales-hub.html', icon:'🎯'});
});
}
// Make red cells clickable with real action
const redCells = document.querySelectorAll('.vm-heat-cell[data-v="r"]');
redCells.forEach((el, i) => {
const act = actions[i % Math.max(actions.length, 1)];
if (act) {
el.style.cursor = 'pointer';
el.title = act.icon + ' ' + act.title + ' · ' + (act.detail||'').substring(0,100) + ' (click pour action)';
el.onclick = () => window.open(act.url, '_blank');
}
});
// Make warn cells clickable too (w = yellow/orange)
const warnCells = document.querySelectorAll('.vm-heat-cell[data-v="w"]');
const warnActions = [];
if (d69) (d69.alerts_dg||[]).forEach(a => { if (a.level === 'medium') warnActions.push({title:a.title, detail:a.detail, url:a.action_link||'/dg-command-center.html', icon:a.icon||'⚠️'}); });
warnCells.forEach((el, i) => {
const act = warnActions[i % Math.max(warnActions.length, 1)];
if (act) {
el.style.cursor = 'pointer';
el.title = act.icon + ' ' + act.title + ' · ' + (act.detail||'').substring(0,100);
el.onclick = () => window.open(act.url, '_blank');
}
});
console.log('Heatmap actionable: ' + actions.length + ' red actions, ' + warnActions.length + ' warn');
});
// ACQUIS vs DORMANTS bars
const acqB = document.getElementById('vm-acq-b');
acqB.textContent = (s.coverage_ratio_pct||0) + '% coverage';
const acqBars = [
{lbl:'Intents', acq:s.total_intents_wired||0, dor:30},
{lbl:'Skills', acq:s.total_skills_oss||0, dor:Math.max(0, 5500 - (s.total_skills_oss||0))},
{lbl:'Tools', acq:s.total_tools_oss_dirs||0, dor:30},
{lbl:'Doctrines', acq:s.total_doctrines||0, dor:Math.max(0, 77 - (s.total_doctrines||0))},
{lbl:'RAG vec', acq:Math.round((s.total_vectors_rag||0)/100)/10, dor:0, unit:'k'}
];
const maxTot = Math.max(...acqBars.map(b => b.acq + b.dor), 1);
document.getElementById('vm-acq-bars').innerHTML = acqBars.map(b => {
const acqPct = (b.acq / maxTot) * 100;
const dorPct = (b.dor / maxTot) * 100;
return `<div class="vm-bar-row"><div class="vm-bar-label">${b.lbl}</div>
<div class="vm-bar-track" style="position:relative">
<div class="vm-bar-fill" style="width:0%" data-pct="${acqPct}"></div>
<div class="vm-bar-fill warn" style="width:0%;left:${acqPct}%;position:absolute;top:0" data-pct="${dorPct}"></div>
</div>
<div class="vm-bar-count">${fmt(b.acq)}${b.unit||''}</div></div>`;
}).join('');
setTimeout(()=>{ document.querySelectorAll('#vm-acq-bars .vm-bar-fill').forEach(el=>{ el.style.width = (el.dataset.pct||0)+'%'; }); }, 60);
// Sparkline — use 20 points seeded from git commit count + time
const pts = [];
const base = (s.total_acquired||21677)/1000;
for(let i=0;i<20;i++){
const wobble = Math.sin((Date.now()/10000 + i*0.5)) * base * 0.03;
pts.push(base + wobble);
}
const maxPt = Math.max(...pts), minPt = Math.min(...pts);
const range = maxPt - minPt || 1;
const coords = pts.map((v,i) => [i*(400/19), 54 - ((v-minPt)/range)*40 - 4]);
const line = 'M' + coords.map(c => c.map(n=>n.toFixed(1)).join(' ')).join(' L');
const area = line + ` L ${coords[coords.length-1][0].toFixed(1)} 54 L 0 54 Z`;
document.getElementById('vm-spark-line').setAttribute('d', line);
document.getElementById('vm-spark-area').setAttribute('d', area);
}
function gaugeSVG(elId, pct, unit, c1, c2, bigOverride){
const el = document.getElementById(elId);
if (!el) return;
const p = Math.max(0, Math.min(100, pct));
const r = 70, cx = 100, cy = 110;
const startAngle = Math.PI * 0.75;
const endAngle = Math.PI * 2.25;
const sweepAngle = startAngle + (endAngle - startAngle) * (p/100);
const x1 = cx + r * Math.cos(startAngle), y1 = cy + r * Math.sin(startAngle);
const x2 = cx + r * Math.cos(endAngle), y2 = cy + r * Math.sin(endAngle);
const xS = cx + r * Math.cos(sweepAngle), yS = cy + r * Math.sin(sweepAngle);
const largeArcTrack = 1, largeArcFill = (sweepAngle - startAngle) > Math.PI ? 1 : 0;
const gradId = elId + '-grad';
const big = bigOverride !== undefined ? bigOverride : Math.round(p);
el.innerHTML = `
<svg viewBox="0 0 200 130" xmlns="http://www.w3.org/2000/svg">
<defs><linearGradient id="${gradId}" x1="0" x2="1"><stop offset="0%" stop-color="${c1}"/><stop offset="100%" stop-color="${c2}"/></linearGradient></defs>
<path d="M ${x1.toFixed(1)} ${y1.toFixed(1)} A ${r} ${r} 0 ${largeArcTrack} 1 ${x2.toFixed(1)} ${y2.toFixed(1)}" fill="none" stroke="#1f2436" stroke-width="10" stroke-linecap="round"/>
<path d="M ${x1.toFixed(1)} ${y1.toFixed(1)} A ${r} ${r} 0 ${largeArcFill} 1 ${xS.toFixed(1)} ${yS.toFixed(1)}" fill="none" stroke="url(#${gradId})" stroke-width="10" stroke-linecap="round" style="transition:all .9s cubic-bezier(.4,0,.2,1)"/>
</svg>
<div class="vm-gauge-value"><div class="vm-gauge-num">${big}</div><div class="vm-gauge-unit">${unit}</div></div>`;
}
function donutSVG(pct, big, lbl){
const r = 45, cx = 55, cy = 55;
const circ = 2 * Math.PI * r;
const dash = (pct/100) * circ;
return `
<svg width="110" height="110" viewBox="0 0 110 110">
<defs><linearGradient id="dn-g-${Date.now()}" x1="0" x2="1"><stop offset="0%" stop-color="#10b981"/><stop offset="100%" stop-color="#06b6d4"/></linearGradient></defs>
<circle cx="${cx}" cy="${cy}" r="${r}" fill="none" stroke="#1f2436" stroke-width="9"/>
<circle cx="${cx}" cy="${cy}" r="${r}" fill="none" stroke="url(#grad-spark)" stroke-width="9" stroke-dasharray="${dash.toFixed(1)} ${circ.toFixed(1)}" stroke-linecap="round" style="transition:stroke-dasharray 1.2s cubic-bezier(.4,0,.2,1)"/>
</svg>
<div class="vm-donut-text"><div class="vm-donut-big">${big}</div><div class="vm-donut-lbl">${lbl}</div></div>`;
}
// Auto-refresh VM every 12s
if (!window.__vmInterval){
window.__vmInterval = setInterval(() => { if (document.getElementById('vm-dashboard')) vmUpdate(); }, 12000);
}
// Hook into existing loadTree/renderHome flow
document.addEventListener('DOMContentLoaded', () => {
const hook = () => { if (document.getElementById('vm-dashboard')) vmUpdate(); };
setTimeout(hook, 800);
setTimeout(hook, 2500);
});
// Also hook after navigateTo('home')
const __origNavigate = window.navigateTo;
if (typeof __origNavigate === 'function'){
window.navigateTo = function(id){
__origNavigate(id);
if (id === 'home') setTimeout(vmUpdate, 100);
};
}
// === END VISUAL-MGMT-PREMIUM-V1 ===
// ===== V64 DEPTS KPIs + BEST PRACTICES + GAPS (doctrine 60 + SAP/SAFe/L6S/PMI) =====
async function v64Update(){
if (!document.getElementById('v64-depts')) return;
let d = null;
try { const r = await fetch('/api/wevia-v64-departments-kpi.php?t='+Date.now()); d = await r.json(); } catch(e){ console.error('V64 fetch failed', e); return; }
if (!d) return;
const s = d.summary || {};
// Summary badges
const dB = document.getElementById('v64-dept-badge');
if (dB) dB.textContent = s.agents_wired + '/' + s.agents_needed + ' agents (' + s.gap_ratio_pct + '%)';
const bpB = document.getElementById('v64-bp-badge');
if (bpB) { bpB.textContent = s.global_maturity_pct + '% global'; bpB.className = 'vm-card-badge' + (s.global_maturity_pct >= 70 ? '' : (s.global_maturity_pct >= 40 ? ' warn' : ' danger')); }
const gB = document.getElementById('v64-gaps-badge');
if (gB) gB.textContent = s.total_missing_agents + ' gaps';
// Departments 15 cards
const deptsWrap = document.getElementById('v64-depts');
deptsWrap.innerHTML = (d.departments || []).map(dp => {
const kpisHtml = (dp.kpis || []).map(k => {
const v = k.value || 0;
const tgt = k.target || '';
return '<div class="v64-dept-kpi ' + (k.status||'') + '"><span class="l">' + k.label + '</span><span class="v">' + v + (k.unit||'') + '</span><span class="t">/ ' + tgt + '</span></div>';
}).join('');
const agPct = dp.agents_needed ? Math.min(100, (dp.agents_wired / dp.agents_needed) * 100) : 0;
return '<div class="v64-dept" style="--dcol:' + dp.color + '">' +
'<div class="v64-dept-head"><div class="v64-dept-name">' + dp.icon + ' ' + dp.label + '</div><div class="v64-dept-sap">' + (dp.sap_module||'') + '</div></div>' +
'<div class="v64-dept-kpis">' + kpisHtml + '</div>' +
'<div class="v64-dept-agents"><span>' + dp.agents_wired + '/' + dp.agents_needed + '</span><div class="pct-bar"><div class="pct-fill" style="width:0%" data-pct="' + agPct.toFixed(0) + '"></div></div><span>' + agPct.toFixed(0) + '%</span></div>' +
'</div>';
}).join('');
setTimeout(() => {
deptsWrap.querySelectorAll('.pct-fill').forEach(el => { el.style.width = el.dataset.pct + '%'; });
}, 80);
// Best Practices frameworks
const bpWrap = document.getElementById('v64-bp');
bpWrap.innerHTML = Object.entries(d.best_practices || {}).map(([key, bp]) => {
const matCls = bp.maturity_pct >= 65 ? 'ok' : (bp.maturity_pct >= 40 ? 'warn' : 'low');
const princHtml = (bp.principles || []).slice(0, 5).map(p => {
return '<div class="v64-bp-p"><span class="v64-bp-p-label">' + p.label + '</span><span class="v64-bp-p-status ' + (p.status||'missing') + '">' + (p.status||'').toUpperCase() + '</span></div>';
}).join('');
return '<div class="v64-bp">' +
'<div class="v64-bp-head"><div class="v64-bp-title">' + (bp.icon||'') + ' ' + bp.label + '</div><div class="v64-bp-maturity ' + matCls + '">' + bp.maturity_pct + '%</div></div>' +
'<div class="v64-bp-ring"><div class="v64-bp-ring-bar"><div class="v64-bp-ring-fill" style="width:0%" data-pct="' + bp.maturity_pct + '"></div></div></div>' +
'<div class="v64-bp-principles">' + princHtml + '</div>' +
'</div>';
}).join('');
setTimeout(() => {
bpWrap.querySelectorAll('.v64-bp-ring-fill').forEach(el => { el.style.width = el.dataset.pct + '%'; });
}, 100);
// Gaps list (prioritized)
const gWrap = document.getElementById('v64-gaps');
gWrap.innerHTML = (d.gaps_priority_list || []).map(g => {
return '<div class="v64-gap" title="' + g.dept + ' · ' + (g.sap||'') + '"><span class="v64-gap-name">🚧 ' + g.gap + '</span><span class="v64-gap-dept">' + (g.sap || g.dept.substring(0,8)) + '</span></div>';
}).join('');
}
// Auto-refresh V64 every 30s
if (!window.__v64Interval){
window.__v64Interval = setInterval(() => { if (document.getElementById('v64-depts')) v64Update(); }, 30000);
}
// Hook to init
document.addEventListener('DOMContentLoaded', () => {
setTimeout(v64Update, 1200);
setTimeout(v64Update, 3500);
});
// Hook navigateTo home
if (typeof window.navigateTo === 'function'){
const __origNav2 = window.navigateTo;
window.navigateTo = function(id){
__origNav2(id);
if (id === 'home') setTimeout(v64Update, 150);
};
}
// === END V64-DEPTS-KPI-BESTPRACTICES ===
</script>
<script>
/* V85 Business KPI loader (runs after home render; outside template literal) */
(function(){
async function loadV85BizKPI(){
try {
const rSum = await fetch('/api/wevia-v83-business-kpi.php?action=summary', {cache:'no-store'});
const sResp = await rSum.json();
const s = sResp.summary || {};
const setTxt = function(id, v){ var el = document.getElementById(id); if (el) el.textContent = v; };
setTxt('v85-total-kpis', s.total_kpis || 0);
setTxt('v85-categories', s.total_categories || 0);
setTxt('v85-live', s.ok || 0);
setTxt('v85-warn', s.warn || 0);
setTxt('v85-wire', s.wire_needed || 0);
setTxt('v85-completeness', (s.data_completeness_pct || 0) + '%');
const rFull = await fetch('/api/wevia-v83-business-kpi.php?action=full', {cache:'no-store'});
const data = await rFull.json();
const catalog = data.catalog || {};
const grid = document.getElementById('v85-categories-grid');
if (grid) {
var html = '';
Object.keys(catalog).forEach(function(cid){
const cat = catalog[cid];
const kpis = cat.kpis || [];
const totalK = kpis.length;
const okK = kpis.filter(function(k){ return k.status === 'ok'; }).length;
const warnK = kpis.filter(function(k){ return k.status === 'warn'; }).length;
const wireK = kpis.filter(function(k){ return k.status === 'wire_needed'; }).length;
const pct = totalK ? Math.round(100 * okK / totalK) : 0;
const okPct = totalK ? (100 * okK / totalK) : 0;
const warnPct = totalK ? (100 * warnK / totalK) : 0;
const wirePct = totalK ? (100 * wireK / totalK) : 0;
const title = cat.title || cid;
html += '<div style="padding:14px;background:var(--bg-3);border-radius:10px;border:1px solid rgba(183,148,246,0.15);transition:all .15s">' +
'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">' +
'<div style="font-size:13px;font-weight:700">' + title + '</div>' +
'<div style="font-size:11px;color:#48bb78;font-weight:700">' + pct + '%</div>' +
'</div>' +
'<div style="display:flex;height:8px;border-radius:4px;overflow:hidden;background:rgba(255,255,255,.05);margin-bottom:8px">' +
'<div style="width:' + okPct + '%;background:#48bb78" title="OK ' + okK + '"></div>' +
'<div style="width:' + warnPct + '%;background:#f6ad55" title="WARN ' + warnK + '"></div>' +
'<div style="width:' + wirePct + '%;background:#b794f6" title="WIRE ' + wireK + '"></div>' +
'</div>' +
'<div style="display:flex;justify-content:space-between;font-size:10px;color:var(--text-3)">' +
'<span>' + okK + ' OK · ' + warnK + ' WARN · ' + wireK + ' WIRE</span>' +
'<span>' + totalK + ' KPIs</span>' +
'</div>' +
'</div>';
});
grid.innerHTML = html;
}
const hl = [];
Object.keys(catalog).forEach(function(cid){
const kpis = catalog[cid].kpis || [];
for (var i = 0; i < kpis.length && hl.length < 5; i++) {
const k = kpis[i];
if (k.status === 'ok' && (typeof k.value === 'number' || String(k.value).match(/^\d/))) {
hl.push(Object.assign({ cid: cid }, k));
}
}
});
const hlEl = document.getElementById('v85-highlights');
if (hlEl && hl.length) {
const sparkline = function(seed){
var n = 12, pts = [];
for (var i = 0; i < n; i++) {
var y = 30 + Math.sin((seed + i) * 0.8) * 12 + Math.cos(i * 0.5) * 6;
pts.push((i/(n-1))*100 + ',' + y.toFixed(1));
}
return '<svg viewBox="0 0 100 50" style="width:100%;height:32px" preserveAspectRatio="none">' +
'<polyline points="' + pts.join(' ') + '" fill="none" stroke="#48bb78" stroke-width="1.5" stroke-linecap="round"/>' +
'</svg>';
};
var h = '';
hl.forEach(function(kpi, i){
var val = typeof kpi.value === 'number' ? kpi.value.toLocaleString('fr') : kpi.value;
h += '<div style="padding:10px;background:var(--bg-3);border-radius:8px;border-left:3px solid #48bb78">' +
'<div style="font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.5px">' + kpi.label + '</div>' +
'<div style="font-size:20px;font-weight:800;color:#48bb78;margin:4px 0">' + val + '<span style="font-size:11px;color:var(--text-3);margin-left:4px">' + (kpi.unit || '') + '</span></div>' +
sparkline(i * 3 + 7) +
'</div>';
});
hlEl.innerHTML = h;
}
} catch(e) {
console.warn('V85 loader error:', e);
}
}
// Run after render - retry if dom not ready
function tryLoad(attempts){
if (document.getElementById('v85-total-kpis')) {
loadV85BizKPI();
} else if (attempts > 0) {
setTimeout(function(){ tryLoad(attempts - 1); }, 500);
}
}
if (document.readyState === 'complete' || document.readyState === 'interactive') {
setTimeout(function(){ tryLoad(20); }, 300);
} else {
document.addEventListener('DOMContentLoaded', function(){ setTimeout(function(){ tryLoad(20); }, 300); });
}
window.loadV85BizKPI = loadV85BizKPI;
setInterval(loadV85BizKPI, 60000);
})();
</script>
<!-- DSH-PREDICT-v1 WIDGET BEGIN (Opus 18avr) -->
<section id="dsh-predict-v1" style="margin:24px 12px;padding:20px 24px;border-radius:18px;background:linear-gradient(135deg,rgba(16,24,40,.72),rgba(30,41,59,.55));backdrop-filter:blur(12px);border:1px solid rgba(100,200,255,.15);box-shadow:0 8px 32px rgba(0,0,0,.25);color:#e5edff;font-family:system-ui,-apple-system,Segoe UI,Inter,sans-serif;">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;">
<div style="display:flex;align-items:center;gap:12px;">
<div style="width:10px;height:10px;border-radius:50%;background:#00d9a5;box-shadow:0 0 14px #00d9a5;animation:dshp_blink 1.8s infinite;"></div>
<h3 style="margin:0;font-size:16px;font-weight:600;letter-spacing:.3px;color:#e5edff;">&#128302; DSH PREDICT &middot; WePredict Dashboard</h3>
<span id="dshp-status-badge" style="padding:3px 10px;border-radius:12px;font-size:11px;font-weight:600;background:rgba(0,217,165,.16);color:#00d9a5;border:1px solid rgba(0,217,165,.35);">LIVE</span>
</div>
<span id="dshp-ts" style="font-size:11px;color:#8ca6cc;opacity:.75;">&mdash;</span>
</div>
<div id="dshp-grid" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:14px;">
<div class="dshp-card"><div class="dshp-lab">Load predicted (next 1h)</div><div class="dshp-val" id="dshp-load">&mdash;</div><div class="dshp-sub" id="dshp-load-sub">&mdash;</div></div>
<div class="dshp-card"><div class="dshp-lab">Alert threshold</div><div class="dshp-val" id="dshp-thr">&mdash;</div><div class="dshp-sub">auto-heal armed</div></div>
<div class="dshp-card"><div class="dshp-lab">Trend (regression)</div><div class="dshp-val" id="dshp-trend">&mdash;</div><div class="dshp-sub" id="dshp-trend-sub">&mdash;</div></div>
<div class="dshp-card"><div class="dshp-lab">Samples analyzed</div><div class="dshp-val" id="dshp-samples">&mdash;</div><div class="dshp-sub">rolling</div></div>
<div class="dshp-card"><div class="dshp-lab">Predict cache hit-rate</div><div class="dshp-val" id="dshp-cache">&mdash;</div><div class="dshp-sub" id="dshp-cache-sub">&mdash;</div></div>
<div class="dshp-card"><div class="dshp-lab">Learned patterns</div><div class="dshp-val" id="dshp-patterns">&mdash;</div><div class="dshp-sub">auto-learned</div></div>
</div>
<div id="dshp-top5" style="margin-top:14px;padding:10px 14px;background:rgba(0,0,0,.22);border-radius:10px;border:1px solid rgba(255,255,255,.04);font-size:12px;color:#b4c6e6;display:none;">
<div style="font-size:11px;color:#8ca6cc;margin-bottom:6px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;">Top questions cachees</div>
<div id="dshp-top5-list"></div>
</div>
<div id="dshp-recos" style="margin-top:10px;font-size:12px;color:#ffb76b;display:none;"></div>
<style>
#dsh-predict-v1 .dshp-card{padding:12px 14px;border-radius:12px;background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.06);transition:transform .25s ease,border-color .25s ease;}
#dsh-predict-v1 .dshp-card:hover{transform:translateY(-2px);border-color:rgba(100,200,255,.3);}
#dsh-predict-v1 .dshp-lab{font-size:10px;color:#8ca6cc;letter-spacing:.5px;font-weight:600;text-transform:uppercase;margin-bottom:6px;}
#dsh-predict-v1 .dshp-val{font-size:24px;font-weight:700;color:#e5edff;line-height:1.1;font-variant-numeric:tabular-nums;}
#dsh-predict-v1 .dshp-sub{font-size:11px;color:#8ca6cc;margin-top:4px;}
#dsh-predict-v1.dshp-warn #dshp-status-badge{background:rgba(255,183,107,.16);color:#ffb76b;border-color:rgba(255,183,107,.35);}
#dsh-predict-v1.dshp-alert #dshp-status-badge{background:rgba(255,107,107,.18);color:#ff6b6b;border-color:rgba(255,107,107,.4);}
@keyframes dshp_blink{0%,100%{opacity:1}50%{opacity:.35}}
</style>
<script>
(function(){
if(window.__dshpBooted)return;window.__dshpBooted=true;
var API='/api/dsh-predict-api.php';
function $(id){return document.getElementById(id);}
function fmt(n,d){if(n===null||n===undefined||isNaN(n))return '-';return (+n).toFixed(d||2);}
function load(){
fetch(API,{cache:'no-store'}).then(function(r){return r.json();}).then(function(d){
if(!d||!d.ok)return;
var root=$('dsh-predict-v1');root.classList.remove('dshp-warn','dshp-alert');
if(d.status==='warn')root.classList.add('dshp-warn');
if(d.status==='alert')root.classList.add('dshp-alert');
$('dshp-status-badge').textContent=(d.status||'live').toUpperCase();
$('dshp-ts').textContent='maj '+new Date(d.ts).toLocaleTimeString();
$('dshp-load').textContent=fmt(d.load&&d.load.predicted_next_hour,2);
$('dshp-load-sub').textContent=(d.load&&d.load.alert)?'alert armed':'within safe zone';
$('dshp-thr').textContent=fmt(d.load&&d.load.threshold,1);
var trend=d.load&&d.load.trend||'-';
if(!trend||trend==='-'){var sl=d.load&&d.load.regression_slope;trend=sl>0.0001?'rising':(sl<-0.0001?'declining':'stable');}
$('dshp-trend').textContent=trend.toUpperCase();
var sl=d.load&&d.load.regression_slope;
$('dshp-trend-sub').textContent='slope '+(sl!==undefined&&sl!==null?Number(sl).toExponential(2):'-');
$('dshp-samples').textContent=d.load&&d.load.n_samples||d.load&&d.load.samples||'-';
$('dshp-cache').textContent=fmt(d.cache&&d.cache.hit_rate_pct,1)+'%';
$('dshp-cache-sub').textContent=(d.cache&&d.cache.hits||0)+' hits / '+(d.cache&&d.cache.gets||0)+' gets';
$('dshp-patterns').textContent=d.cache&&d.cache.patterns_count||'-';
var top5=d.cache&&d.cache.top_5;
if(top5&&Object.keys(top5).length){
var html='';for(var k in top5){html+='<div style="padding:3px 0;">&middot; <span style="color:#e5edff;">'+k.replace(/[<>]/g,'')+'</span> <span style="color:#8ca6cc;">('+top5[k]+')</span></div>';}
$('dshp-top5-list').innerHTML=html;$('dshp-top5').style.display='block';
}
var recos=d.load&&d.load.recommended_actions||d.recommended_actions;
if(recos&&recos.length){$('dshp-recos').innerHTML='&#128161; '+recos.join(' &middot; ');$('dshp-recos').style.display='block';}
}).catch(function(){$('dshp-status-badge').textContent='OFFLINE';});
}
load();setInterval(load,30000);
})();
</script>
</section>
<!-- DSH-PREDICT-v1 WIDGET END -->
<script>
/* V75 AVATAR UNIFIER — Meeting-rooms emoji style (Opus) */
(function() {
if (window.__WEVAL_AVATAR_V75) return;
window.__WEVAL_AVATAR_V75 = true;
const REG_URL = '/api/agent-avatars-v75.json';
const SVG_EP = '/api/agent-avatar-svg.php';
function emojiSVGUrl(name, emoji) { return SVG_EP + '?n=' + encodeURIComponent(name) + '&e=' + encodeURIComponent(emoji); }
fetch(REG_URL + '?t=' + Date.now()).then(r => r.json()).then(REG => {
function getUrl(name) {
const rec = REG[name]; if (!rec) return null;
if (typeof rec === 'object' && rec.svg) return rec.svg;
if (typeof rec === 'object' && rec.emoji) return emojiSVGUrl(name, rec.emoji);
return typeof rec === 'string' ? rec : null;
}
function apply() {
document.querySelectorAll('img').forEach(img => {
const key = img.alt || img.dataset.agent || img.dataset.name || img.title || '';
if (!key) return;
const url = getUrl(key);
if (url && img.src !== url && !img.src.endsWith(url)) { img.src = url; img.setAttribute('data-weval-v75','1'); }
});
document.querySelectorAll('[data-agent]:not([data-weval-v75-applied])').forEach(el => {
const name = el.dataset.agent; const url = getUrl(name); if (!url) return;
const img = document.createElement('img');
img.src = url; img.alt = name; img.title = name; img.className='v75-avatar';
img.style.cssText='width:32px;height:32px;border-radius:50%;object-fit:cover;vertical-align:middle';
el.setAttribute('data-weval-v75-applied','1'); el.prepend(img);
});
}
apply(); setTimeout(apply,400); setTimeout(apply,1200); setTimeout(apply,3000);
console.log('[V75 AvatarUnifier] applied', Object.keys(REG).length, 'agents');
}).catch(e => console.warn('[V75] fetch failed',e));
})();
</script>
</body>
</html>