1334 lines
91 KiB
HTML
1334 lines
91 KiB
HTML
<!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 & 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 & 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;">🔮 DSH PREDICT · 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;">—</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">—</div><div class="dshp-sub" id="dshp-load-sub">—</div></div>
|
||
<div class="dshp-card"><div class="dshp-lab">Alert threshold</div><div class="dshp-val" id="dshp-thr">—</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">—</div><div class="dshp-sub" id="dshp-trend-sub">—</div></div>
|
||
<div class="dshp-card"><div class="dshp-lab">Samples analyzed</div><div class="dshp-val" id="dshp-samples">—</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">—</div><div class="dshp-sub" id="dshp-cache-sub">—</div></div>
|
||
<div class="dshp-card"><div class="dshp-lab">Learned patterns</div><div class="dshp-val" id="dshp-patterns">—</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;">· <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='💡 '+recos.join(' · ');$('dshp-recos').style.display='block';}
|
||
}).catch(function(){$('dshp-status-badge').textContent='OFFLINE';});
|
||
}
|
||
load();setInterval(load,30000);
|
||
})();
|
||
</script>
|
||
</section>
|
||
<!-- DSH-PREDICT-v1 WIDGET END -->
|
||
|
||
</body>
|
||
</html>
|