2606 lines
170 KiB
HTML
2606 lines
170 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="fr" data-theme="dark">
|
||
<head>
|
||
<!-- V86 AUTH GUARD WTP · Yacine doctrine: WTP = point entrée unique AVEC AUTHENTIFICATION -->
|
||
<script>
|
||
(function() {
|
||
if (window.__WEVAL_V86_AUTH) return;
|
||
window.__WEVAL_V86_AUTH = true;
|
||
|
||
// Dev bypass: ?dev=1 or localhost
|
||
var isDev = /[?&]dev=1/.test(location.search) || location.hostname === 'localhost' || location.hostname === '127.0.0.1';
|
||
|
||
// Quick check via async fetch (non-blocking for speed, redirects if fail)
|
||
fetch('/api/auth-check.php', { credentials: 'include', cache: 'no-store' })
|
||
.then(function(r) {
|
||
if (r.status === 200) {
|
||
// Authenticated - mark body + log
|
||
document.body && document.body.setAttribute('data-auth', 'ok');
|
||
if (window.console) console.log('[V86 Auth Guard] SSO authenticated (200)');
|
||
} else if (r.status === 401 || r.status === 403) {
|
||
if (isDev) {
|
||
if (window.console) console.warn('[V86 Auth Guard] DEV mode - 401 bypassed');
|
||
document.body && document.body.setAttribute('data-auth', 'dev');
|
||
} else {
|
||
// Show redirect banner + redirect after 2s (UX premium - pas brutal)
|
||
var banner = document.createElement('div');
|
||
banner.style.cssText = 'position:fixed;top:0;left:0;right:0;z-index:99999;background:linear-gradient(135deg,#ef4444,#f59e0b);color:#fff;padding:14px 20px;text-align:center;font:600 14px -apple-system,sans-serif;box-shadow:0 4px 20px rgba(0,0,0,.3);';
|
||
banner.innerHTML = '🔐 Authentification requise · redirection vers /login.html dans <span id="v86-countdown">2</span>s · <a href="/login.html" style="color:#fff;text-decoration:underline;font-weight:700;">cliquer ici</a>';
|
||
(document.body || document.documentElement).appendChild(banner);
|
||
var sec = 2;
|
||
var iv = setInterval(function() {
|
||
sec--;
|
||
var span = document.getElementById('v86-countdown');
|
||
if (span) span.textContent = sec;
|
||
if (sec <= 0) {
|
||
clearInterval(iv);
|
||
location.href = '/login.html?from=' + encodeURIComponent(location.pathname);
|
||
}
|
||
}, 1000);
|
||
}
|
||
} else {
|
||
// Other (500, network etc.) - log + non-blocking
|
||
if (window.console) console.warn('[V86 Auth Guard] auth-check returned', r.status);
|
||
}
|
||
})
|
||
.catch(function(e) {
|
||
// Auth-check unreachable - graceful: allow (don't break WTP if API down)
|
||
if (window.console) console.warn('[V86 Auth Guard] auth-check unreachable, allowing (graceful)', e);
|
||
});
|
||
})();
|
||
</script>
|
||
<!-- V86 AUTH GUARD WTP END -->
|
||
|
||
<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>
|
||
<button id="v80-toggle-inline" onclick="v80Open()" title="Navigation Archi Unifiee - toutes les portes" style="background:linear-gradient(135deg,#6366f1,#8b5cf6);color:#fff;border:none;padding:8px 14px;border-radius:18px;cursor:pointer;font-size:12px;font-weight:700;letter-spacing:.3px;display:flex;align-items:center;gap:6px;transition:all .2s;box-shadow:0 4px 12px rgba(99,102,241,.3)" onmouseover="this.style.transform='translateY(-1px)';this.style.boxShadow='0 6px 18px rgba(99,102,241,.5)'" onmouseout="this.style.transform='translateY(0)';this.style.boxShadow='0 4px 12px rgba(99,102,241,.3)'"><span style="font-size:14px">🧭</span>Archi complete</button>
|
||
<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" id="st-blade-wrap"><span class="dot warn" id="st-blade-dot"></span><span id="st-blade-label">Blade <strong id="st-blade-text">checking</strong></span></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;
|
||
// 16 ERP modules
|
||
let html = 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('');
|
||
|
||
// SECTION SÉPARATRICE · POINT D'ENTRÉE ENRICHI (Opus Yacine 19avr)
|
||
html += `<div style="padding:10px 14px 6px;font-size:10px;color:var(--wtp-muted);text-transform:uppercase;letter-spacing:1.5px;margin-top:14px;border-top:1px solid var(--wtp-border);padding-top:14px">POINT D'ENTRÉE TOTAL</div>`;
|
||
|
||
const extras = [
|
||
{id:'infra', icon:'🖥', label:'Infrastructure', color:'#22d3ee', count: (TREE.infra?.machines?.length||0) + (TREE.infra?.docker?.length||0)},
|
||
{id:'all_pages', icon:'📂', label:'Toutes les pages', color:'#d4af37', count: TREE.all_pages?.total||0},
|
||
{id:'all_apis', icon:'🔌', label:'Toutes les APIs', color:'#a855f7', count: TREE.apis?.total||0},
|
||
{id:'multiagent', icon:'🤖', label:'Multi-Agent Modes', color:'#ef4444', count: TREE.multiagent_modes?.length||0},
|
||
{id:'truth', icon:'🔒', label:'Truth Registry', color:'#22c55e', count: TREE.truth_registry?.agents||0},
|
||
{id:'crm_bridge', icon:'🔗', label:'CRM Bridge (4 CRMs)', color:'#22d3ee', count: 4},
|
||
];
|
||
html += extras.map(e => `<div class="wtp-nav-item" data-mod="${e.id}" onclick="navigateTo('${e.id}')">
|
||
<span class="wtp-nav-icon" style="color:${e.color}">${e.icon}</span>
|
||
<span>${e.label}</span>
|
||
<span class="wtp-nav-count">${e.count}</span>
|
||
</div>`).join('');
|
||
|
||
el.innerHTML = html;
|
||
}
|
||
|
||
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 if (modId === 'infra') renderInfra();
|
||
else if (modId === 'crm_bridge') { window.open('/wevia-ia/wevia-admin-crm-v68.php', '_blank'); return; }
|
||
else if (modId === 'all_pages') renderAllPages();
|
||
else if (modId === 'all_apis') renderAllApis();
|
||
else if (modId === 'multiagent') renderMultiagent();
|
||
else if (modId === 'truth') renderTruth();
|
||
else renderModule(modId);
|
||
document.getElementById('wtp-main').scrollTop = 0;
|
||
}
|
||
|
||
// === POINT D'ENTRÉE TOTAL views (Opus Yacine 19avr) ===
|
||
function renderInfra(){
|
||
const main = document.getElementById('wtp-main');
|
||
const i = TREE.infra || {};
|
||
const machines = (i.machines||[]).map(m => `
|
||
<div style="background:var(--wtp-panel2);padding:18px;border-radius:8px;border:1px solid var(--wtp-border)">
|
||
<div style="font-size:18px;font-weight:800;margin-bottom:4px;color:var(--wtp-ac)">${m.id}</div>
|
||
<div style="font-size:11px;color:var(--wtp-muted);margin-bottom:10px">${m.type||''}</div>
|
||
<div style="font-size:13px;line-height:1.6">${m.role||''}</div>
|
||
${m.cpu ? `<div style="margin-top:8px;font-size:11px;color:var(--wtp-muted)">CPU: ${m.cpu} · RAM: ${m.ram||'?'} · Disk: ${m.disk||'?'}</div>` : ''}
|
||
</div>`).join('');
|
||
|
||
const docker = (i.docker||[]).slice(0, 25).map(d => `
|
||
<div style="display:flex;justify-content:space-between;padding:8px 12px;background:var(--wtp-panel);border-radius:6px;margin-bottom:4px;font-size:12px">
|
||
<span style="font-weight:600">${d.name||'?'}</span>
|
||
<span style="color:var(--wtp-muted);font-family:monospace;font-size:10px">${d.status||''}</span>
|
||
</div>`).join('');
|
||
|
||
const gpus = (i.gpus||[]).map(g => `
|
||
<div style="background:var(--wtp-panel);padding:14px;border-radius:8px;margin-bottom:6px">
|
||
<div style="font-weight:700">${g.name||'?'}</div>
|
||
<div style="font-size:11px;color:var(--wtp-muted);margin-top:4px">${g.note||''}</div>
|
||
${g.mem_total_mb ? `<div style="margin-top:6px;font-family:monospace;font-size:11px">Mem: ${g.mem_used_mb}/${g.mem_total_mb} MB · Temp: ${g.temp_c}°C · Util: ${g.util_pct}%</div>` : ''}
|
||
</div>`).join('');
|
||
|
||
const subdoms = Object.entries(i.subdomains||{}).map(([d, role]) => `
|
||
<a href="https://${d}/" target="_blank" style="display:flex;justify-content:space-between;padding:8px 12px;background:var(--wtp-panel);border-radius:6px;margin-bottom:4px;font-size:12px;text-decoration:none;color:var(--wtp-text)">
|
||
<span style="font-family:monospace">${d}</span>
|
||
<span style="color:var(--wtp-muted)">${role}</span>
|
||
</a>`).join('');
|
||
|
||
main.innerHTML = `
|
||
<div style="padding:24px">
|
||
<h1 style="font-size:28px;margin-bottom:6px">🖥 Infrastructure</h1>
|
||
<div style="color:var(--wtp-muted);margin-bottom:24px">Machines · Docker · GPUs · Blade · Subdomains · tout en un</div>
|
||
|
||
<h2 style="font-size:18px;margin-bottom:12px">💻 Machines · ${(i.machines||[]).length}</h2>
|
||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px;margin-bottom:28px">${machines}</div>
|
||
|
||
<h2 style="font-size:18px;margin-bottom:12px">🔧 Blade yacineutt (Sentinel · Chrome persist)</h2>
|
||
<div style="background:var(--wtp-panel2);padding:18px;border-radius:8px;border:1px solid var(--wtp-border);margin-bottom:28px">
|
||
<div style="font-size:14px;font-weight:700;margin-bottom:8px">${i.blade?.host||'Razer Blade'}</div>
|
||
<div style="color:var(--wtp-muted);font-size:12px;margin-bottom:10px">${i.blade?.role||''}</div>
|
||
<div style="font-size:12px;margin-bottom:6px">Profile: ${i.blade?.yacineutt_profile||'?'}</div>
|
||
<div style="display:flex;gap:6px;flex-wrap:wrap;margin-top:10px">
|
||
${(i.blade?.services||[]).map(s=>`<span style="background:var(--wtp-panel);padding:4px 10px;border-radius:99px;font-size:11px;font-family:monospace">${s}</span>`).join('')}
|
||
</div>
|
||
</div>
|
||
|
||
<h2 style="font-size:18px;margin-bottom:12px">🎮 GPUs · ${(i.gpus||[]).length}</h2>
|
||
${gpus}
|
||
<div style="height:24px"></div>
|
||
|
||
<h2 style="font-size:18px;margin-bottom:12px">🐳 Docker · ${(i.docker||[]).length} containers</h2>
|
||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:6px;margin-bottom:28px">${docker}</div>
|
||
|
||
<h2 style="font-size:18px;margin-bottom:12px">🌐 Subdomains · ${Object.keys(i.subdomains||{}).length}</h2>
|
||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:6px">${subdoms}</div>
|
||
</div>`;
|
||
}
|
||
|
||
function renderAllPages(){
|
||
const main = document.getElementById('wtp-main');
|
||
const ap = TREE.all_pages || {};
|
||
const cats = ap.pages_by_category || {};
|
||
const catHtml = Object.entries(cats).sort((a,b)=>b[1].length-a[1].length).map(([cat, pages]) => {
|
||
const items = pages.slice(0, 50).map(p => `
|
||
<a href="${p.url}" style="display:flex;justify-content:space-between;padding:7px 11px;background:var(--wtp-panel);border:1px solid var(--wtp-border);border-radius:5px;text-decoration:none;color:var(--wtp-text);font-size:12px;transition:.15s"
|
||
onmouseover="this.style.borderColor='var(--wtp-ac)'" onmouseout="this.style.borderColor='var(--wtp-border)'">
|
||
<span style="font-weight:500">${p.name.replace(/\.html$/,'').replace(/[-_]/g,' ')}</span>
|
||
<span style="color:var(--wtp-muted);font-family:monospace;font-size:10px">${p.size_kb}k${p.is_orphan?' ·⚠':''}</span>
|
||
</a>`).join('');
|
||
return `<div style="margin-bottom:24px">
|
||
<h3 style="font-size:15px;margin-bottom:10px;display:flex;justify-content:space-between">
|
||
<span>${cat}</span><span style="color:var(--wtp-muted);font-size:11px;font-weight:400">${pages.length} pages</span>
|
||
</h3>
|
||
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(340px,1fr));gap:4px">${items}</div>
|
||
${pages.length>50?`<div style="color:var(--wtp-muted);font-size:11px;margin-top:6px">+${pages.length-50} autres</div>`:''}
|
||
</div>`;
|
||
}).join('');
|
||
|
||
main.innerHTML = `
|
||
<div style="padding:24px">
|
||
<h1 style="font-size:28px;margin-bottom:6px">📂 Toutes les pages · ${ap.total}</h1>
|
||
<div style="color:var(--wtp-muted);margin-bottom:20px">${ap.orphans} orphelines identifiées · toutes accessibles depuis WTP</div>
|
||
<div style="margin-bottom:18px">
|
||
<input type="text" id="pg-search" placeholder="Recherche page..." oninput="_pgSearch(this.value)" style="width:100%;padding:12px 16px;background:var(--wtp-panel);border:1px solid var(--wtp-border);color:var(--wtp-text);border-radius:6px;font-size:13px;outline:none">
|
||
</div>
|
||
<div id="pg-content">${catHtml}</div>
|
||
</div>`;
|
||
}
|
||
window._pgSearch = function(q){
|
||
q = (q||'').toLowerCase();
|
||
document.querySelectorAll('#pg-content a').forEach(a => {
|
||
a.style.display = (!q || a.textContent.toLowerCase().includes(q)) ? '' : 'none';
|
||
});
|
||
};
|
||
|
||
function renderAllApis(){
|
||
const main = document.getElementById('wtp-main');
|
||
const a = TREE.apis || {};
|
||
main.innerHTML = `
|
||
<div style="padding:24px">
|
||
<h1 style="font-size:28px;margin-bottom:6px">🔌 Toutes les APIs · ${a.total}</h1>
|
||
<div style="color:var(--wtp-muted);margin-bottom:24px">Endpoints PHP dans /api/</div>
|
||
<h2 style="font-size:16px;margin-bottom:10px">Top WEVIA APIs (${(a.top_wevia||[]).length})</h2>
|
||
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:6px">
|
||
${(a.top_wevia||[]).map(api => `
|
||
<a href="/api/${api}" target="_blank" style="padding:8px 12px;background:var(--wtp-panel);border:1px solid var(--wtp-border);border-radius:5px;text-decoration:none;color:var(--wtp-text);font-size:12px;font-family:monospace">
|
||
${api}
|
||
</a>`).join('')}
|
||
</div>
|
||
</div>`;
|
||
}
|
||
|
||
function renderMultiagent(){
|
||
const main = document.getElementById('wtp-main');
|
||
const modes = TREE.multiagent_modes || [];
|
||
main.innerHTML = `
|
||
<div style="padding:24px">
|
||
<h1 style="font-size:28px;margin-bottom:6px">🤖 Multi-Agent Modes</h1>
|
||
<div style="color:var(--wtp-muted);margin-bottom:24px">3 modes coexistent via WEVIA Master chat · /wevia-master.html</div>
|
||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:14px">
|
||
${modes.map((m, i) => `
|
||
<div style="background:var(--wtp-panel2);padding:20px;border-radius:10px;border:1px solid var(--wtp-border);border-left:4px solid ${['#6366f1','#22c55e','#f59e0b'][i]||'#6366f1'}">
|
||
<div style="font-size:17px;font-weight:800;margin-bottom:6px">${m.mode}</div>
|
||
<div style="font-size:22px;font-weight:700;margin:8px 0">${m.agents || m.agents_target} agents</div>
|
||
<div style="font-size:12px;color:var(--wtp-muted);margin-bottom:8px">Latency: <span style="color:var(--wtp-ac);font-family:monospace">${m.latency_ms}ms</span></div>
|
||
<div style="font-size:12px;margin-bottom:10px">${m.use}</div>
|
||
<div style="font-size:10px;color:var(--wtp-muted);font-family:monospace;padding:6px 10px;background:var(--wtp-panel);border-radius:4px">${m.trigger}</div>
|
||
</div>`).join('')}
|
||
</div>
|
||
<div style="margin-top:24px;padding:18px;background:var(--wtp-panel2);border-radius:8px">
|
||
<strong>💬 Tester en live:</strong> ouvre <a href="/wevia-master.html" style="color:var(--wtp-ac)">/wevia-master.html</a> et tape "agis en multiagent"
|
||
</div>
|
||
</div>`;
|
||
}
|
||
|
||
function renderTruth(){
|
||
const main = document.getElementById('wtp-main');
|
||
const t = TREE.truth_registry || {};
|
||
main.innerHTML = `
|
||
<div style="padding:24px">
|
||
<h1 style="font-size:28px;margin-bottom:6px">🔒 Truth Registry · source unique</h1>
|
||
<div style="color:var(--wtp-muted);margin-bottom:24px">Built: <span style="font-family:monospace">${t.built_at||'?'}</span> · rebuild cron /30min</div>
|
||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;margin-bottom:24px">
|
||
${[['Agents',t.agents,'#d4af37'],['Intents',t.intents,'#22c55e'],['Skills',t.skills,'#6366f1'],['Brains',t.brains,'#a855f7'],['Doctrines',t.doctrines,'#ef4444'],['Dashboards',t.dashboards,'#22d3ee'],['Providers',t.providers,'#f59e0b'],['Qdrant Cols',t.qdrant_cols,'#ec4899'],['Qdrant Points',t.qdrant_points,'#10b981'],['Autonomy',(t.autonomy_score||0)+'/100 '+(t.autonomy_level||''),'#d4af37']].map(([l,v,c])=>`
|
||
<div style="background:var(--wtp-panel2);padding:16px;border-radius:8px;border-top:2px solid ${c}">
|
||
<div style="font-size:10px;color:var(--wtp-muted);text-transform:uppercase;letter-spacing:1px;margin-bottom:6px">${l}</div>
|
||
<div style="font-size:24px;font-weight:800;color:${c}">${typeof v==='number' ? v.toLocaleString('fr-FR') : v}</div>
|
||
</div>`).join('')}
|
||
</div>
|
||
<div style="display:flex;gap:10px;flex-wrap:wrap">
|
||
<a href="/api/wevia-truth-registry.json" target="_blank" style="padding:10px 16px;background:var(--wtp-panel);border:1px solid var(--wtp-border);border-radius:6px;text-decoration:none;color:var(--wtp-text);font-size:13px">📄 Truth Registry JSON (560 KB)</a>
|
||
<a href="/api/source-of-truth.json" target="_blank" style="padding:10px 16px;background:var(--wtp-panel);border:1px solid var(--wtp-border);border-radius:6px;text-decoration:none;color:var(--wtp-text);font-size:13px">📋 Source of Truth (snapshot)</a>
|
||
<a href="/api/wevia.php?q=status&format=text" target="_blank" style="padding:10px 16px;background:var(--wtp-panel);border:1px solid var(--wtp-border);border-radius:6px;text-decoration:none;color:var(--wtp-text);font-size:13px">🔍 NL Query API</a>
|
||
</div>
|
||
</div>`;
|
||
}
|
||
|
||
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="/infra-tour-2s-5c-blade.html" style="padding:9px 18px;background:linear-gradient(135deg,#06b6d4,#8b5cf6);color:#0b0d15;border-radius:8px;text-decoration:none;font-size:12.5px;font-weight:700">🗺️ Ouvrir Infra Tour 2S+5C+Blade</a>
|
||
<a href="/growth-engine-v2.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 Growth Engine v4</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">40/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 -->
|
||
|
||
|
||
|
||
<!-- Row 12: V67-WEVIA-APPLE — Photos Scanner OSS Intelligence (Opus 19avr) -->
|
||
<section id="wevia-apple-section" style="margin:16px 12px;padding:20px 24px;border-radius:16px;background:linear-gradient(135deg,rgba(0,0,0,.35),rgba(30,30,50,.25));backdrop-filter:blur(10px);border:1px solid rgba(229,229,234,.18);box-shadow:0 6px 24px rgba(0,0,0,.28)">
|
||
<div style="display:flex;align-items:center;gap:14px;margin-bottom:12px">
|
||
<div style="width:44px;height:44px;background:linear-gradient(135deg,#000,#1d1d1f);border-radius:11px;display:grid;place-items:center;font-size:26px">🍎</div>
|
||
<div style="flex:1">
|
||
<div style="font-size:1.05rem;font-weight:700;letter-spacing:-.2px">WEVIA Apple — Photos Scanner OSS Intelligence</div>
|
||
<div style="font-size:.78rem;color:rgba(229,229,234,.7);margin-top:2px">Upload photo iPhone → OCR tesseract + Vision Gemini 2.5 → extract GitHub/OSS/Docker · iPhone Shortcuts ready</div>
|
||
</div>
|
||
<a href="/wevia-apple.html" style="padding:8px 14px;background:rgba(96,165,250,.15);border:1px solid rgba(96,165,250,.4);color:#60a5fa;border-radius:8px;text-decoration:none;font-size:.85rem;font-weight:600;white-space:nowrap">Ouvrir →</a>
|
||
</div>
|
||
<div id="wevia-apple-kpi" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:8px;margin-top:8px">
|
||
<div style="padding:10px 12px;background:rgba(96,165,250,.08);border:1px solid rgba(96,165,250,.25);border-radius:8px"><div style="font-size:.68rem;color:rgba(229,229,234,.6);text-transform:uppercase;letter-spacing:.6px">Photos</div><div id="wa-k-total" style="font-size:1.4rem;font-weight:700">—</div></div>
|
||
<div style="padding:10px 12px;background:rgba(251,191,36,.08);border:1px solid rgba(251,191,36,.25);border-radius:8px"><div style="font-size:.68rem;color:rgba(229,229,234,.6);text-transform:uppercase;letter-spacing:.6px">OSS trouvés</div><div id="wa-k-oss" style="font-size:1.4rem;font-weight:700;color:#fbbf24">—</div></div>
|
||
<div style="padding:10px 12px;background:rgba(34,197,94,.08);border:1px solid rgba(34,197,94,.25);border-radius:8px"><div style="font-size:.68rem;color:rgba(229,229,234,.6);text-transform:uppercase;letter-spacing:.6px">GitHub URLs</div><div id="wa-k-gh" style="font-size:1.4rem;font-weight:700;color:#22c55e">—</div></div>
|
||
<div style="padding:10px 12px;background:rgba(192,132,252,.08);border:1px solid rgba(192,132,252,.25);border-radius:8px"><div style="font-size:.68rem;color:rgba(229,229,234,.6);text-transform:uppercase;letter-spacing:.6px">Top</div><div id="wa-k-top" style="font-size:.95rem;font-weight:600;line-height:1.3;color:#c084fc">—</div></div>
|
||
</div>
|
||
<script>(function(){fetch('/api/wevia-apple-scan.php?action=stats').then(r=>r.json()).then(d=>{document.getElementById('wa-k-total').textContent=d.scans_total||0;document.getElementById('wa-k-oss').textContent=d.oss_total||0;document.getElementById('wa-k-gh').textContent=d.github_urls_total||0;var top=d.top_projects?Object.entries(d.top_projects)[0]:null;document.getElementById('wa-k-top').textContent=top?(top[0]+' ('+top[1]+')'):'—';}).catch(()=>{});})();<\/script>
|
||
</section>
|
||
<!-- /V67-WEVIA-APPLE -->
|
||
<!-- /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);
|
||
|
||
// V96.8 Opus 19avr HONEST heatmap — 144 REAL named components (doctrine 4 honnêteté)
|
||
// Replaces V72 pseudo-random decoration (((seed+i*37)*2654435761)%100) which showed fake red/orange
|
||
// Each cell now = named component (11 infra + 20 dashboards + 25 ERPs + 60 pain points + 10 APIs + 18 skills)
|
||
// Hover = real name + status + details · Click = open real link
|
||
const heat = document.getElementById('vm-heat');
|
||
// Loading placeholder immediately
|
||
heat.innerHTML = Array.from({length:144},(_,i)=>'<div class="vm-heat-cell" data-v="0" data-idx="'+i+'" title="Loading ecosystem health…"></div>').join('');
|
||
// Fetch real health data
|
||
fetch('/api/wevia-ecosystem-health-144.php?t='+Date.now()).then(r=>r.json()).then(eh => {
|
||
if (!eh || !eh.cells) return;
|
||
const statusToV = {fail:'r', warn:'w', hot:'4', ok:'2', idle:'0'};
|
||
const statusIcon = {fail:'❌', warn:'⚠️', hot:'🔥', ok:'✅', idle:'⏸️'};
|
||
const newCells = eh.cells.map(c => {
|
||
const v = statusToV[c.status] || '0';
|
||
const ico = statusIcon[c.status] || '';
|
||
// Escape quotes in tooltip
|
||
const tip = (ico + ' ' + c.name + ' · ' + (c.status||'').toUpperCase() + ' · ' + (c.details||'') + ' · [' + (c.category||'') + ']').replace(/"/g, '"').replace(/'/g, ''');
|
||
return '<div class="vm-heat-cell" data-v="'+v+'" data-idx="'+c.idx+'" data-link="'+(c.link||'#')+'" data-status="'+c.status+'" title="'+tip+'"></div>';
|
||
}).join('');
|
||
heat.innerHTML = newCells;
|
||
// Make ALL cells clickable to their real component (doctrine 4)
|
||
heat.querySelectorAll('.vm-heat-cell').forEach(el => {
|
||
const link = el.dataset.link;
|
||
if (link && link !== '#') {
|
||
el.style.cursor = 'pointer';
|
||
el.addEventListener('click', () => window.open(link, '_blank'));
|
||
}
|
||
});
|
||
console.log('Heatmap V96.8 HONEST: 144 named components · stats:', eh.stats);
|
||
}).catch(e => console.error('[V96.8] ecosystem health fetch failed', e));
|
||
|
||
// 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:'⚠️'});
|
||
});
|
||
/* V46 wire DG summary KPIs (doctrine #14 additif) */
|
||
if (d69.summary) {
|
||
var s69 = d69.summary;
|
||
var _setDg = function(id, v){ var el=document.getElementById(id); if(el && v!=null && v!=='') el.textContent=v; };
|
||
_setDg('dg-alerts', s69.alerts_dg_count);
|
||
_setDg('dg-toc', s69.toc_bottleneck_label);
|
||
_setDg('dg-pipe', (s69.pipeline_value_keur||0) + 'k€');
|
||
_setDg('dg-opps', s69.active_clients);
|
||
_setDg('dg-risks', s69.risks_critical);
|
||
}
|
||
}
|
||
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';
|
||
// V96.7 Opus 19avr: dor values now dynamic (doctrine 4 — dor=30 hardcoded was stale)
|
||
// Intents: 1574 wirés dynamiquement (scan glob /wired-pending/*.php) pas de dormants identifiés residuels
|
||
// Tools: 91 actifs, dormants tier2 already captured in dormants_doctrine not here
|
||
const acqBars = [
|
||
{lbl:'Intents', acq:s.total_intents_wired||0, dor:0}, // V96.7: real count 1574, 0 dormants (all intent files = wired)
|
||
{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:0}, // V96.7: 91 actifs, dormants dans dormants_doctrine scope
|
||
{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); setTimeout(updateWeviaAppleKpis, 300); }
|
||
};
|
||
}
|
||
// V48 WEVIA Apple KPIs (inline script in template literal does not execute via innerHTML, use global fn)
|
||
function updateWeviaAppleKpis() {
|
||
fetch('/api/wevia-apple-scan.php?action=stats').then(r=>r.json()).then(d=>{
|
||
var setKpi = function(id, v){ var el=document.getElementById(id); if(el && v!=null) el.textContent=v; };
|
||
setKpi('wa-k-total', d.scans_total||0);
|
||
setKpi('wa-k-oss', d.oss_total||0);
|
||
setKpi('wa-k-gh', d.github_urls_total||0);
|
||
var top = d.top_projects ? Object.entries(d.top_projects)[0] : null;
|
||
setKpi('wa-k-top', top ? (top[0]+' ('+top[1]+')') : '-');
|
||
}).catch(function(){});
|
||
}
|
||
document.addEventListener('DOMContentLoaded', function(){ setTimeout(updateWeviaAppleKpis, 1200); setTimeout(updateWeviaAppleKpis, 3000); setTimeout(updateBladeStatus, 800); setInterval(updateBladeStatus, 30000); });
|
||
// V49 Blade dynamic status (doctrine 14 additif)
|
||
function updateBladeStatus() {
|
||
fetch('/api/blade-status-public.php').then(r=>r.json()).then(d=>{
|
||
var dot = document.getElementById('st-blade-dot'); if (dot) { dot.className = 'dot ' + (d.class || 'warn'); }
|
||
var txt = document.getElementById('st-blade-text'); if (txt) { txt.textContent = d.label || 'unknown'; }
|
||
var wrap = document.getElementById('st-blade-wrap'); if (wrap && d.tasks) { wrap.title = 'Tasks pending:' + d.tasks.pending + ' done:' + d.tasks.done + ' failed:' + d.tasks.failed + ' ago:' + (d.ago_sec||'?') + 's'; }
|
||
}).catch(function(){});
|
||
}
|
||
// === 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 -->
|
||
|
||
<script>
|
||
/* V75 AVATAR UNIFIER — Meeting-rooms emoji style (Opus) */
|
||
(function() {
|
||
if (window.__WEVAL_AVATAR_V75) return;
|
||
window.__WEVAL_AVATAR_V75 = true;
|
||
const REG_URL = '/api/agent-avatars-v75.json';
|
||
const SVG_EP = '/api/agent-avatar-svg.php';
|
||
function emojiSVGUrl(name, emoji) { return SVG_EP + '?n=' + encodeURIComponent(name) + '&e=' + encodeURIComponent(emoji); }
|
||
fetch(REG_URL + '?t=' + Date.now()).then(r => r.json()).then(REG => {
|
||
function getUrl(name) {
|
||
const rec = REG[name]; if (!rec) return null;
|
||
if (typeof rec === 'object' && rec.svg) return rec.svg;
|
||
if (typeof rec === 'object' && rec.emoji) return emojiSVGUrl(name, rec.emoji);
|
||
return typeof rec === 'string' ? rec : null;
|
||
}
|
||
function apply() {
|
||
document.querySelectorAll('img').forEach(img => {
|
||
const key = img.alt || img.dataset.agent || img.dataset.name || img.title || '';
|
||
if (!key) return;
|
||
const url = getUrl(key);
|
||
if (url && img.src !== url && !img.src.endsWith(url)) { img.src = url; img.setAttribute('data-weval-v75','1'); }
|
||
});
|
||
document.querySelectorAll('[data-agent]:not([data-weval-v75-applied])').forEach(el => {
|
||
const name = el.dataset.agent; const url = getUrl(name); if (!url) return;
|
||
const img = document.createElement('img');
|
||
img.src = url; img.alt = name; img.title = name; img.className='v75-avatar';
|
||
img.style.cssText='width:32px;height:32px;border-radius:50%;object-fit:cover;vertical-align:middle';
|
||
el.setAttribute('data-weval-v75-applied','1'); el.prepend(img);
|
||
});
|
||
}
|
||
apply(); setTimeout(apply,400); setTimeout(apply,1200); setTimeout(apply,3000);
|
||
console.log('[V75 AvatarUnifier] applied', Object.keys(REG).length, 'agents');
|
||
}).catch(e => console.warn('[V75] fetch failed',e));
|
||
})();
|
||
</script>
|
||
<!-- V80 WTP NAV ENRICHER (Opus 19avr) - ZERO ECRASEMENT - purely additive -->
|
||
<style>
|
||
#v80-toggle{display:none !important;z-index:9998;padding:14px 20px;
|
||
background:linear-gradient(135deg,#6366f1,#8b5cf6);color:#fff;border:none;
|
||
border-radius:50px;font:600 13px -apple-system,sans-serif;cursor:pointer;
|
||
box-shadow:0 8px 24px rgba(99,102,241,.45);transition:transform .2s,box-shadow .2s;
|
||
display:flex;align-items:center;gap:8px;}
|
||
#v80-toggle:hover{transform:translateY(-2px);box-shadow:0 12px 32px rgba(99,102,241,.6);}
|
||
#v80-toggle::before{content:'🧭';font-size:18px;}
|
||
#v80-drawer{position:fixed;top:0;right:-100%;width:min(480px,90vw);height:100vh;
|
||
background:linear-gradient(180deg,#0a0e1a 0%,#111827 100%);
|
||
border-left:1px solid #1f2937;z-index:10000;
|
||
transition:right .3s cubic-bezier(.4,0,.2,1);
|
||
overflow-y:auto;color:#f1f5f9;
|
||
font:13px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;}
|
||
#v80-drawer.open{right:0;box-shadow:-24px 0 60px rgba(0,0,0,.5);}
|
||
.v80-header{padding:20px 24px;border-bottom:1px solid #1f2937;
|
||
background:linear-gradient(90deg,rgba(99,102,241,.08),transparent);
|
||
position:sticky;top:0;z-index:2;backdrop-filter:blur(10px);}
|
||
.v80-header h3{font-size:16px;font-weight:700;margin:0;}
|
||
.v80-header h3 span{color:#6366f1;}
|
||
.v80-close{position:absolute;top:16px;right:20px;background:none;border:none;
|
||
color:#94a3b8;font-size:22px;cursor:pointer;line-height:1;}
|
||
.v80-close:hover{color:#f1f5f9;}
|
||
.v80-kpi-strip{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-top:12px;}
|
||
.v80-kpi{background:rgba(99,102,241,.08);border:1px solid rgba(99,102,241,.18);
|
||
border-radius:8px;padding:8px 10px;text-align:center;}
|
||
.v80-kpi-val{font-size:18px;font-weight:700;color:#6366f1;line-height:1;}
|
||
.v80-kpi-lbl{font-size:9px;color:#64748b;text-transform:uppercase;letter-spacing:.04em;margin-top:4px;}
|
||
.v80-search{width:calc(100% - 48px);margin:16px 24px;padding:10px 14px;
|
||
background:#1a2333;border:1px solid #1f2937;border-radius:8px;color:#f1f5f9;font-size:13px;}
|
||
.v80-search:focus{outline:none;border-color:#6366f1;}
|
||
.v80-section{padding:4px 24px 20px;}
|
||
.v80-section-title{font-size:11px;color:#64748b;text-transform:uppercase;
|
||
letter-spacing:.08em;font-weight:700;margin:12px 0 10px;}
|
||
.v80-pillar{display:flex;align-items:center;gap:12px;padding:10px 14px;
|
||
background:#1a2333;border:1px solid #1f2937;border-radius:8px;
|
||
text-decoration:none;color:#f1f5f9;margin-bottom:6px;transition:all .15s;}
|
||
.v80-pillar:hover{border-color:#6366f1;transform:translateX(-2px);}
|
||
.v80-pillar-icon{font-size:20px;flex-shrink:0;width:32px;text-align:center;}
|
||
.v80-pillar-text{flex:1;min-width:0;}
|
||
.v80-pillar-name{font-weight:600;font-size:13px;}
|
||
.v80-pillar-desc{font-size:11px;color:#94a3b8;margin-top:2px;
|
||
overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
||
.v80-pillar-meta{font-size:10px;color:#6366f1;flex-shrink:0;}
|
||
.v80-quick-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:6px;}
|
||
.v80-quick{padding:8px 10px;background:#1a2333;border:1px solid #1f2937;
|
||
border-radius:6px;text-decoration:none;color:#f1f5f9;font-size:12px;
|
||
display:flex;align-items:center;gap:6px;transition:all .15s;}
|
||
.v80-quick:hover{border-color:#6366f1;}
|
||
.v80-quick-icon{font-size:14px;}
|
||
.v80-link-list{display:flex;flex-direction:column;gap:4px;}
|
||
.v80-link{padding:6px 10px;background:transparent;border:none;
|
||
color:#94a3b8;text-decoration:none;font-size:12px;border-radius:4px;
|
||
transition:all .15s;}
|
||
.v80-link:hover{background:#1a2333;color:#f1f5f9;}
|
||
.v80-link .v80-link-meta{float:right;color:#6366f1;font-size:10px;}
|
||
.v80-orphans-warn{background:rgba(245,158,11,.08);border:1px solid rgba(245,158,11,.3);
|
||
border-radius:6px;padding:10px 12px;font-size:12px;color:#fbbf24;margin-top:8px;}
|
||
.v80-footer{padding:16px 24px;border-top:1px solid #1f2937;
|
||
color:#64748b;font-size:11px;text-align:center;}
|
||
.v80-footer a{color:#6366f1;text-decoration:none;}
|
||
.v80-hidden{display:none!important;}
|
||
</style>
|
||
|
||
<button id="v80-toggle" onclick="v80Open()">Archi complète</button>
|
||
|
||
<div id="v80-drawer">
|
||
<div class="v80-header">
|
||
<button class="v80-close" onclick="v80Close()">×</button>
|
||
<h3>Navigation <span>Archi Unifiée</span></h3>
|
||
<div style="font-size:11px;color:#64748b;margin-top:4px;">Point d'entrée WTP · toutes les portes de l'archi</div>
|
||
<div class="v80-kpi-strip" id="v80-kpis">
|
||
<div class="v80-kpi"><div class="v80-kpi-val" id="v80-k-agents">—</div><div class="v80-kpi-lbl">Agents</div></div>
|
||
<div class="v80-kpi"><div class="v80-kpi-val" id="v80-k-pages">—</div><div class="v80-kpi-lbl">Pages</div></div>
|
||
<div class="v80-kpi"><div class="v80-kpi-val" id="v80-k-autonomy" style="color:#10b981">—</div><div class="v80-kpi-lbl">Autonomy</div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<input class="v80-search" id="v80-search" placeholder="🔍 Rechercher partout (251+ pages)..." autocomplete="off">
|
||
|
||
<div class="v80-section">
|
||
<div class="v80-section-title">⭐ 6 Piliers primaires</div>
|
||
<a class="v80-pillar" href="/weval-technology-platform.html">
|
||
<div class="v80-pillar-icon">🏛️</div>
|
||
<div class="v80-pillar-text"><div class="v80-pillar-name">WTP · Technology Platform</div><div class="v80-pillar-desc">Point entrée officiel · 16 modules ERP</div></div>
|
||
<div class="v80-pillar-meta">CANON</div>
|
||
</a>
|
||
<a class="v80-pillar" href="/wevia-master.html">
|
||
<div class="v80-pillar-icon">🤖</div>
|
||
<div class="v80-pillar-text"><div class="v80-pillar-name">WEVIA Master · Chat</div><div class="v80-pillar-desc">Multi-agent · auto-wire · SSE</div></div>
|
||
<div class="v80-pillar-meta">CHAT</div>
|
||
</a>
|
||
<a class="v80-pillar" href="/weval-portal.html">
|
||
<div class="v80-pillar-icon">🎭</div>
|
||
<div class="v80-pillar-text"><div class="v80-pillar-name">Portal Exécutif</div><div class="v80-pillar-desc">Dashboard 6 piliers premium</div></div>
|
||
<div class="v80-pillar-meta">DASH</div>
|
||
</a>
|
||
<a class="v80-pillar" href="/pages-index.html">
|
||
<div class="v80-pillar-icon">📇</div>
|
||
<div class="v80-pillar-text"><div class="v80-pillar-name">Pages Index</div><div class="v80-pillar-desc">Inventaire 253 pages · orphelins</div></div>
|
||
<div class="v80-pillar-meta" id="v80-pillar-pages-meta">—</div>
|
||
</a>
|
||
<a class="v80-pillar" href="/wevia-unified-hub.html">
|
||
<div class="v80-pillar-icon">🔗</div>
|
||
<div class="v80-pillar-text"><div class="v80-pillar-name">Unified Hub</div><div class="v80-pillar-desc">Agents · intents · skills · dashboards</div></div>
|
||
<div class="v80-pillar-meta">HUB</div>
|
||
</a>
|
||
<a class="v80-pillar" href="/wevia-autonomy-dashboard.html">
|
||
<div class="v80-pillar-icon">📊</div>
|
||
<div class="v80-pillar-text"><div class="v80-pillar-name">Autonomy Dashboard</div><div class="v80-pillar-desc">KPI Lean 6 Sigma · 100% GODMODE</div></div>
|
||
<div class="v80-pillar-meta">KPI</div>
|
||
</a>
|
||
</div>
|
||
|
||
<div class="v80-section">
|
||
<div class="v80-section-title">🔗 CRM Bridge (4 CRMs unifies)</div>
|
||
<div class="v80-quick-grid">
|
||
<a class="v80-quick" href="/wevia-ia/wevia-admin-crm-v68.php" target="_blank"><span class="v80-quick-icon">✨</span>Admin CRM V68 Premium</a>
|
||
<a class="v80-quick" href="/wevia-ia/wevia-admin-crm.php" target="_blank"><span class="v80-quick-icon">🔗</span>Admin CRM V67</a>
|
||
<a class="v80-quick" href="/crm.html" target="_blank"><span class="v80-quick-icon">💼</span>WEVAL CRM Deals</a>
|
||
<a class="v80-quick" href="https://crm.weval-consulting.com" target="_blank"><span class="v80-quick-icon">🏢</span>Twenty CRM 37k</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="v80-section">
|
||
<div class="v80-section-title">⚡ Infra & Machines</div>
|
||
<div class="v80-quick-grid">
|
||
<a class="v80-quick" href="/architecture.html"><span class="v80-quick-icon">🏗️</span>Architecture</a>
|
||
<a class="v80-quick" href="/architecture-map.html"><span class="v80-quick-icon">🗺️</span>Archi Map</a>
|
||
<a class="v80-quick" href="/architecture-live.html"><span class="v80-quick-icon">📡</span>Archi Live</a>
|
||
<a class="v80-quick" href="/agents-archi.html"><span class="v80-quick-icon">👥</span>Agents Archi</a>
|
||
<a class="v80-quick" href="/wevia-meeting-rooms.html"><span class="v80-quick-icon">🏛️</span>Meeting Rooms</a>
|
||
<a class="v80-quick" href="/monitoring-hub.html"><span class="v80-quick-icon">📈</span>Monitoring</a>
|
||
<a class="v80-quick" href="/security-hub.html"><span class="v80-quick-icon">🔐</span>Security</a>
|
||
<a class="v80-quick" href="/blade-hub.html"><span class="v80-quick-icon">⚙️</span>Blade/GPU</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="v80-section">
|
||
<div class="v80-section-title">💼 Business & ERP</div>
|
||
<div class="v80-quick-grid">
|
||
<a class="v80-quick" href="/enterprise-model.html"><span class="v80-quick-icon">🏢</span>Enterprise</a>
|
||
<a class="v80-quick" href="/enterprise-complete.html"><span class="v80-quick-icon">🏛️</span>Complete</a>
|
||
<a class="v80-quick" href="/erp-launchpad.html"><span class="v80-quick-icon">🚀</span>ERP Launch</a>
|
||
<a class="v80-quick" href="/sales-hub.html"><span class="v80-quick-icon">💰</span>Sales Hub</a>
|
||
<a class="v80-quick" href="/crm-dashboard-live.html"><span class="v80-quick-icon">🧑💼</span>CRM</a>
|
||
<a class="v80-quick" href="/intelligence-growth.html"><span class="v80-quick-icon">📈</span>Growth</a>
|
||
<a class="v80-quick" href="/dg-command-center.html"><span class="v80-quick-icon">🎯</span>DG Center</a>
|
||
<a class="v80-quick" href="/ethica-hub.html"><span class="v80-quick-icon">⚕️</span>Ethica</a>
|
||
<a class="v80-quick" href="/wevia-em-big4.html"><span class="v80-quick-icon">🏢</span>Big4 Model</a>
|
||
<a class="v80-quick" href="/value-streaming.html"><span class="v80-quick-icon">⚡</span>Value Stream</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="v80-section">
|
||
<div class="v80-section-title">🤖 IA & Tools</div>
|
||
<div class="v80-quick-grid">
|
||
<a class="v80-quick" href="/ai-hub.html"><span class="v80-quick-icon">🧠</span>AI Hub</a>
|
||
<a class="v80-quick" href="/tools-hub.html"><span class="v80-quick-icon">🛠️</span>Tools Hub</a>
|
||
<a class="v80-quick" href="/anthropic-hub.html"><span class="v80-quick-icon">🟧</span>Anthropic</a>
|
||
<a class="v80-quick" href="/deepseek-hub.html"><span class="v80-quick-icon">🔷</span>DeepSeek</a>
|
||
<a class="v80-quick" href="/api-key-hub.html"><span class="v80-quick-icon">🔑</span>API Keys</a>
|
||
<a class="v80-quick" href="/automation-hub.html"><span class="v80-quick-icon">⚡</span>Automation</a>
|
||
<a class="v80-quick" href="/cloudflare-hub.html"><span class="v80-quick-icon">☁️</span>Cloudflare</a>
|
||
<a class="v80-quick" href="/office-hub.html"><span class="v80-quick-icon">📧</span>Office</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- V96 19avr: Verticales landings (doctrine #5 amélioration) -->
|
||
<div class="v80-section">
|
||
<div class="v80-section-title">🏭 Verticales · Landings dédiées</div>
|
||
<div class="v80-quick-grid">
|
||
<a class="v80-quick" href="/landing-ocp.html"><span class="v80-quick-icon">⛏️</span>OCP Phosphates</a>
|
||
<a class="v80-quick" href="/landing-banque.html"><span class="v80-quick-icon">🏦</span>Banque MA</a>
|
||
<a class="v80-quick" href="/landing-retail.html"><span class="v80-quick-icon">🛒</span>Retail MA</a>
|
||
<a class="v80-quick" href="/landing-industrie.html"><span class="v80-quick-icon">🏭</span>Industrie MA</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="v80-section">
|
||
<div class="v80-section-title">📋 Sitemap & Cartographie</div>
|
||
<div class="v80-link-list">
|
||
<a class="v80-link" href="/weval-sitemap.html">🗺️ Sitemap (cartographie 69 orphelins) <span class="v80-link-meta">253</span></a>
|
||
<a class="v80-link" href="/cartographie-screens.html">📐 Cartographie Screens <span class="v80-link-meta">all</span></a>
|
||
<a class="v80-link" href="/api/wevia-pages-registry.php?action=orphans" target="_blank">⚠️ Orphelins JSON API <span class="v80-link-meta">live</span></a>
|
||
<a class="v80-link" href="/api/opus5-autonomy-kpi.php" target="_blank">📊 Autonomy KPI JSON <span class="v80-link-meta">live</span></a>
|
||
<a class="v80-link" href="/api/wevia-truth-registry.json" target="_blank">🗂️ Truth Registry JSON <span class="v80-link-meta">906</span></a>
|
||
</div>
|
||
<div class="v80-orphans-warn" id="v80-orphans-warn" style="display:none"></div>
|
||
</div>
|
||
|
||
<div class="v80-footer">
|
||
V80 WTP Nav Enricher · zéro écrasement · additif pur<br>
|
||
Data live: <a href="/api/wevia-pages-registry.php?action=summary" target="_blank">Pages</a> · <a href="/api/opus5-autonomy-kpi.php" target="_blank">Autonomy</a> · <a href="/api/wevia-truth-registry.json" target="_blank">Truth</a>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
(function() {
|
||
if (window.__WEVAL_V80) return;
|
||
window.__WEVAL_V80 = true;
|
||
|
||
window.v80Open = function() { document.getElementById('v80-drawer').classList.add('open'); };
|
||
window.v80Close = function() { document.getElementById('v80-drawer').classList.remove('open'); };
|
||
|
||
document.addEventListener('keydown', function(e) {
|
||
if (e.key === 'Escape') v80Close();
|
||
if ((e.metaKey || e.ctrlKey) && e.key === 'k') { e.preventDefault(); v80Open(); setTimeout(function(){document.getElementById('v80-search').focus();},50); }
|
||
});
|
||
|
||
// Load live KPIs with soft fail
|
||
Promise.all([
|
||
fetch('/api/opus5-autonomy-kpi.php').then(r=>r.json()).catch(function(){return null;}),
|
||
fetch('/api/wevia-pages-registry.php?action=summary').then(r=>r.json()).catch(function(){return null;})
|
||
]).then(function(results) {
|
||
var kpi = results[0], pages = results[1];
|
||
if (kpi && kpi.truth) {
|
||
document.getElementById('v80-k-agents').textContent = kpi.truth.agents_unique || '—';
|
||
document.getElementById('v80-k-autonomy').textContent = (kpi.truth.autonomy_score || 0) + '%';
|
||
}
|
||
if (pages) {
|
||
document.getElementById('v80-k-pages').textContent = pages.total_pages || '—';
|
||
document.getElementById('v80-pillar-pages-meta').textContent = pages.orphans_count + ' orph';
|
||
if (pages.orphans_count > 0) {
|
||
var w = document.getElementById('v80-orphans-warn');
|
||
w.style.display = 'block';
|
||
w.innerHTML = '⚠️ ' + pages.orphans_count + ' pages orphelines détectées. <a href="/api/wevia-pages-registry.php?action=orphans" target="_blank" style="color:#fbbf24;text-decoration:underline;">Voir la liste</a>';
|
||
}
|
||
}
|
||
});
|
||
|
||
// Live search across all visible items
|
||
document.getElementById('v80-search').addEventListener('input', function(e) {
|
||
var q = e.target.value.trim().toLowerCase();
|
||
document.querySelectorAll('#v80-drawer .v80-pillar, #v80-drawer .v80-quick, #v80-drawer .v80-link').forEach(function(item) {
|
||
var text = item.textContent.toLowerCase();
|
||
item.classList.toggle('v80-hidden', q && !text.includes(q));
|
||
});
|
||
});
|
||
|
||
console.log('[V80 WTP Nav] loaded · toggle: bottom-right, keyboard: Ctrl+K');
|
||
})();
|
||
</script>
|
||
<!-- V80 WTP NAV ENRICHER END -->
|
||
|
||
<!-- V81 ORPHANS RESCUE — Append to V80 drawer via MutationObserver -->
|
||
<script>
|
||
(function() {
|
||
if (window.__WEVAL_V81) return;
|
||
window.__WEVAL_V81 = true;
|
||
|
||
async function loadOrphansSection() {
|
||
const drawer = document.getElementById('v80-drawer');
|
||
if (!drawer) { setTimeout(loadOrphansSection, 500); return; }
|
||
if (document.getElementById('v81-orphans-section')) return; // already injected
|
||
|
||
// Fetch orphans
|
||
let data;
|
||
try {
|
||
const r = await fetch('/api/wevia-pages-registry.php?action=orphans');
|
||
data = await r.json();
|
||
} catch (e) {
|
||
console.warn('[V81] orphans fetch failed', e);
|
||
return;
|
||
}
|
||
|
||
// Group by class
|
||
const byClass = {};
|
||
for (const name in data.orphans) {
|
||
const meta = data.orphans[name];
|
||
const c = meta.class || 'other';
|
||
if (!byClass[c]) byClass[c] = [];
|
||
byClass[c].push({ name, title: meta.title || '', size_kb: meta.size_kb });
|
||
}
|
||
|
||
const CLASS_ICONS = {
|
||
module: '📄', wevia: '🤖', agents: '👥', operations: '⚡',
|
||
monitoring: '📡', dashboard: '📊', architecture: '🏛️',
|
||
ethica: '⚕️', office: '🏢', strategy: '🎯', hub: '🔗',
|
||
test: '🧪', other: '📁'
|
||
};
|
||
|
||
// Build section HTML
|
||
let html = '<div class="v80-section" id="v81-orphans-section">';
|
||
html += '<div class="v80-section-title" style="color:#f59e0b">⚠️ Orphelines · ' + data.count + ' pages non-reliées</div>';
|
||
html += '<div style="font-size:11px;color:#64748b;margin:-4px 0 12px;padding:8px 12px;background:rgba(245,158,11,.05);border-radius:6px;border:1px solid rgba(245,158,11,.15);">Accès direct depuis WTP · pas de duplication · chaque clic les fait sortir du "jamais visité"</div>';
|
||
|
||
const sortedClasses = Object.keys(byClass).sort((a, b) => byClass[b].length - byClass[a].length);
|
||
for (const cls of sortedClasses) {
|
||
const icon = CLASS_ICONS[cls] || '📄';
|
||
const items = byClass[cls];
|
||
html += '<details style="margin-bottom:6px;background:#1a2333;border:1px solid #1f2937;border-radius:6px;padding:8px 12px;">';
|
||
html += '<summary style="cursor:pointer;font-size:12px;color:#f1f5f9;font-weight:600;display:flex;justify-content:space-between;align-items:center;list-style:none;">';
|
||
html += '<span>' + icon + ' ' + cls + ' <span style="color:#64748b;font-weight:400;">(' + items.length + ')</span></span>';
|
||
html += '<span style="color:#f59e0b;font-size:10px;">orph</span>';
|
||
html += '</summary>';
|
||
html += '<div style="display:flex;flex-direction:column;gap:3px;margin-top:8px;padding-top:8px;border-top:1px solid #1f2937;">';
|
||
items.forEach(item => {
|
||
const label = item.title || item.name;
|
||
html += '<a href="/' + item.name + '" class="v80-link" title="' + item.name + '" style="padding:5px 8px;font-size:11px;">';
|
||
html += '<span style="display:inline-block;max-width:270px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:middle;">' + label + '</span>';
|
||
html += '<span class="v80-link-meta" style="color:#64748b;">' + item.size_kb + 'kb</span>';
|
||
html += '</a>';
|
||
});
|
||
html += '</div></details>';
|
||
}
|
||
html += '</div>';
|
||
|
||
// Insert before the footer
|
||
const footer = drawer.querySelector('.v80-footer');
|
||
if (footer) {
|
||
const section = document.createElement('div');
|
||
section.innerHTML = html;
|
||
footer.parentNode.insertBefore(section.firstChild, footer);
|
||
console.log('[V81 Orphans Rescue] loaded', data.count, 'orphans in', sortedClasses.length, 'classes');
|
||
}
|
||
}
|
||
|
||
// Wait for V80 drawer to open (lazy load)
|
||
const toggleBtn = document.getElementById('v80-toggle');
|
||
if (toggleBtn) {
|
||
toggleBtn.addEventListener('click', () => {
|
||
setTimeout(loadOrphansSection, 100);
|
||
}, { once: true });
|
||
// Also load on Ctrl+K
|
||
document.addEventListener('keydown', (e) => {
|
||
if ((e.metaKey || e.ctrlKey) && e.key === 'k') setTimeout(loadOrphansSection, 100);
|
||
});
|
||
} else {
|
||
// If V80 not yet ready, wait
|
||
setTimeout(() => {
|
||
const btn = document.getElementById('v80-toggle');
|
||
if (btn) btn.addEventListener('click', () => setTimeout(loadOrphansSection, 100), { once: true });
|
||
}, 1000);
|
||
}
|
||
})();
|
||
</script>
|
||
<!-- V81 ORPHANS RESCUE END -->
|
||
|
||
<!-- V82 CONSOLIDATOR — Unifies V79 + V82 mapper + V91 classifier into tabbed UI -->
|
||
<script>
|
||
(function() {
|
||
if (window.__WEVAL_V82) return;
|
||
window.__WEVAL_V82 = true;
|
||
|
||
async function loadV82Tabs() {
|
||
const drawer = document.getElementById('v80-drawer');
|
||
if (!drawer) { setTimeout(loadV82Tabs, 500); return; }
|
||
if (document.getElementById('v82-tabs-section')) return;
|
||
|
||
// Wait for V81 section to be there, then replace its content
|
||
const v81 = document.getElementById('v81-orphans-section');
|
||
if (!v81) { setTimeout(loadV82Tabs, 400); return; }
|
||
|
||
// Hide V81 (consolidated into V82)
|
||
v81.style.display = 'none';
|
||
|
||
// Build V82 tabbed UI
|
||
const section = document.createElement('div');
|
||
section.id = 'v82-tabs-section';
|
||
section.className = 'v80-section';
|
||
section.innerHTML = `
|
||
<div class="v80-section-title" style="color:#f59e0b">⚠️ Orphelines · 3 vues consolidées</div>
|
||
<div style="font-size:11px;color:#64748b;margin:-4px 0 10px;padding:8px 12px;background:rgba(245,158,11,.05);border-radius:6px;border:1px solid rgba(245,158,11,.15);">
|
||
Réconciliation train multi-Claude · <a href="/orphans-rescue.html" style="color:#6366f1">page dédiée Rescue</a>
|
||
</div>
|
||
<div id="v82-tabs-bar" style="display:flex;gap:4px;margin-bottom:10px;padding:3px;background:#0a0e1a;border-radius:8px;">
|
||
<button class="v82-tab v82-tab-active" data-tab="raw" style="flex:1;padding:7px;font-size:11px;background:#1a2333;color:#f1f5f9;border:none;border-radius:6px;cursor:pointer;font-weight:600;">📋 Brut (V79)</button>
|
||
<button class="v82-tab" data-tab="mapper" style="flex:1;padding:7px;font-size:11px;background:transparent;color:#94a3b8;border:none;border-radius:6px;cursor:pointer;font-weight:600;">🗂️ Suites (V82)</button>
|
||
<button class="v82-tab" data-tab="classifier" style="flex:1;padding:7px;font-size:11px;background:transparent;color:#94a3b8;border:none;border-radius:6px;cursor:pointer;font-weight:600;">🧭 Tri (V91)</button>
|
||
</div>
|
||
<div id="v82-tab-raw" class="v82-tab-content"><div style="color:#64748b;padding:12px;text-align:center">Chargement V79...</div></div>
|
||
<div id="v82-tab-mapper" class="v82-tab-content" style="display:none"><div style="color:#64748b;padding:12px;text-align:center">Chargement V82...</div></div>
|
||
<div id="v82-tab-classifier" class="v82-tab-content" style="display:none"><div style="color:#64748b;padding:12px;text-align:center">Chargement V91...</div></div>
|
||
`;
|
||
|
||
// Insert before footer
|
||
const footer = drawer.querySelector('.v80-footer');
|
||
if (footer) footer.parentNode.insertBefore(section, footer);
|
||
|
||
// Tab switching
|
||
section.querySelectorAll('.v82-tab').forEach(btn => {
|
||
btn.addEventListener('click', () => {
|
||
section.querySelectorAll('.v82-tab').forEach(b => {
|
||
b.style.background = 'transparent'; b.style.color = '#94a3b8';
|
||
b.classList.remove('v82-tab-active');
|
||
});
|
||
btn.style.background = '#1a2333'; btn.style.color = '#f1f5f9';
|
||
btn.classList.add('v82-tab-active');
|
||
const t = btn.dataset.tab;
|
||
section.querySelectorAll('.v82-tab-content').forEach(c => c.style.display = 'none');
|
||
document.getElementById('v82-tab-' + t).style.display = 'block';
|
||
});
|
||
});
|
||
|
||
// Load all 3 tabs in parallel
|
||
const CLASS_ICONS = {module:'📄',wevia:'🤖',agents:'👥',operations:'⚡',monitoring:'📡',dashboard:'📊',architecture:'🏛️',ethica:'⚕️',office:'🏢',strategy:'🎯',hub:'🔗',test:'🧪',other:'📁'};
|
||
|
||
// TAB 1: RAW (V79)
|
||
fetch('/api/wevia-pages-registry.php?action=orphans').then(r=>r.json()).then(d => {
|
||
const byClass = {};
|
||
for (const name in d.orphans) {
|
||
const m = d.orphans[name]; const c = m.class || 'other';
|
||
if (!byClass[c]) byClass[c] = [];
|
||
byClass[c].push({name, title: m.title || '', size_kb: m.size_kb});
|
||
}
|
||
const sorted = Object.keys(byClass).sort((a,b) => byClass[b].length - byClass[a].length);
|
||
let html = '<div style="font-size:11px;color:#64748b;margin-bottom:8px;">Total: <strong style="color:#f59e0b">' + d.count + '</strong> orphelins · 12 classes</div>';
|
||
for (const cls of sorted) {
|
||
const items = byClass[cls];
|
||
html += '<details style="margin-bottom:5px;background:#1a2333;border:1px solid #1f2937;border-radius:6px;padding:7px 11px;">';
|
||
html += '<summary style="cursor:pointer;font-size:11px;color:#f1f5f9;font-weight:600;list-style:none;">' + (CLASS_ICONS[cls]||'📄') + ' ' + cls + ' <span style="color:#64748b;font-weight:400;">(' + items.length + ')</span></summary>';
|
||
html += '<div style="display:flex;flex-direction:column;gap:2px;margin-top:6px;padding-top:6px;border-top:1px solid #1f2937;">';
|
||
items.forEach(i => {
|
||
html += '<a href="/' + i.name + '" class="v80-link" style="padding:4px 7px;font-size:10px;">';
|
||
html += '<span style="display:inline-block;max-width:260px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:middle;">' + (i.title||i.name) + '</span>';
|
||
html += '<span class="v80-link-meta" style="color:#64748b;">' + i.size_kb + 'kb</span></a>';
|
||
});
|
||
html += '</div></details>';
|
||
}
|
||
document.getElementById('v82-tab-raw').innerHTML = html;
|
||
}).catch(e => { document.getElementById('v82-tab-raw').innerHTML = '<div style="color:#ef4444;padding:12px">V79 fetch error: ' + e.message + '</div>'; });
|
||
|
||
// TAB 2: MAPPER (V82 Opus WIRE)
|
||
fetch('/api/wevia-orphans-mapper.php').then(r=>r.json()).then(d => {
|
||
if (d.error) throw new Error(d.error);
|
||
const mapping = d.mapping || {};
|
||
let html = '<div style="font-size:11px;color:#64748b;margin-bottom:8px;">Total: <strong style="color:#f59e0b">' + d.total_orphans + '</strong> · <strong style="color:#6366f1">' + d.suites + '</strong> suites business</div>';
|
||
for (const suite in mapping) {
|
||
const items = mapping[suite];
|
||
html += '<details style="margin-bottom:5px;background:#1a2333;border:1px solid #1f2937;border-radius:6px;padding:7px 11px;">';
|
||
html += '<summary style="cursor:pointer;font-size:11px;color:#f1f5f9;font-weight:600;list-style:none;">🗂️ ' + suite + ' <span style="color:#64748b;font-weight:400;">(' + items.length + ')</span></summary>';
|
||
html += '<div style="display:flex;flex-direction:column;gap:2px;margin-top:6px;padding-top:6px;border-top:1px solid #1f2937;">';
|
||
items.forEach(i => {
|
||
html += '<a href="' + i.url + '" class="v80-link" style="padding:4px 7px;font-size:10px;">';
|
||
html += '<span style="display:inline-block;max-width:260px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:middle;">' + (i.title||i.name) + '</span>';
|
||
html += '<span class="v80-link-meta" style="color:#64748b;">' + i.size_kb + 'kb</span></a>';
|
||
});
|
||
html += '</div></details>';
|
||
}
|
||
document.getElementById('v82-tab-mapper').innerHTML = html;
|
||
}).catch(e => { document.getElementById('v82-tab-mapper').innerHTML = '<div style="color:#ef4444;padding:12px">V82 fetch error: ' + e.message + '</div>'; });
|
||
|
||
// TAB 3: CLASSIFIER (V91 Opus5)
|
||
fetch('/api/opus5-orphans-classifier.php').then(r=>r.json()).then(d => {
|
||
const cl = d.classification || {};
|
||
const counts = d.summary || {};
|
||
const TAB_ICONS = {LEGITIMATE_ARCHIVE:'📦',ACTIVE_TO_REWIRE:'🔌',DORMANT:'💤'};
|
||
const TAB_COLORS = {LEGITIMATE_ARCHIVE:'#64748b',ACTIVE_TO_REWIRE:'#10b981',DORMANT:'#f59e0b'};
|
||
const TAB_LABELS = {LEGITIMATE_ARCHIVE:'Archive légitime',ACTIVE_TO_REWIRE:'À rebrancher',DORMANT:'Dormant'};
|
||
let html = '<div style="font-size:11px;color:#64748b;margin-bottom:8px;">Tri par action · Opus5 doctrine 91</div>';
|
||
html += '<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:6px;margin-bottom:10px;">';
|
||
for (const cat of ['LEGITIMATE_ARCHIVE','ACTIVE_TO_REWIRE','DORMANT']) {
|
||
const items = cl[cat] || [];
|
||
html += '<div style="background:#1a2333;border:1px solid #1f2937;border-radius:6px;padding:8px;text-align:center;">';
|
||
html += '<div style="font-size:16px;font-weight:700;color:' + TAB_COLORS[cat] + '">' + items.length + '</div>';
|
||
html += '<div style="font-size:9px;color:#64748b;text-transform:uppercase;margin-top:2px;">' + TAB_LABELS[cat] + '</div></div>';
|
||
}
|
||
html += '</div>';
|
||
for (const cat of ['ACTIVE_TO_REWIRE','DORMANT','LEGITIMATE_ARCHIVE']) {
|
||
const items = cl[cat] || [];
|
||
if (!items.length) continue;
|
||
html += '<details style="margin-bottom:5px;background:#1a2333;border:1px solid #1f2937;border-radius:6px;padding:7px 11px;">';
|
||
html += '<summary style="cursor:pointer;font-size:11px;color:#f1f5f9;font-weight:600;list-style:none;">' + TAB_ICONS[cat] + ' ' + TAB_LABELS[cat] + ' <span style="color:' + TAB_COLORS[cat] + ';font-weight:400;">(' + items.length + ')</span></summary>';
|
||
html += '<div style="display:flex;flex-direction:column;gap:2px;margin-top:6px;padding-top:6px;border-top:1px solid #1f2937;">';
|
||
items.forEach(i => {
|
||
html += '<a href="/' + i.page + '" class="v80-link" style="padding:4px 7px;font-size:10px;" title="' + (i.reason||'') + '">';
|
||
html += '<span style="display:inline-block;max-width:240px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:middle;">' + i.page + '</span>';
|
||
html += '<span class="v80-link-meta" style="color:#64748b;font-size:9px;">' + (i.reason||'') + '</span></a>';
|
||
});
|
||
html += '</div></details>';
|
||
}
|
||
document.getElementById('v82-tab-classifier').innerHTML = html;
|
||
}).catch(e => { document.getElementById('v82-tab-classifier').innerHTML = '<div style="color:#ef4444;padding:12px">V91 fetch error: ' + e.message + '</div>'; });
|
||
|
||
console.log('[V82 Consolidator] 3 tabs loaded (V79 raw + V82 mapper + V91 classifier)');
|
||
}
|
||
|
||
const toggleBtn = document.getElementById('v80-toggle');
|
||
if (toggleBtn) {
|
||
toggleBtn.addEventListener('click', () => setTimeout(loadV82Tabs, 300));
|
||
document.addEventListener('keydown', e => {
|
||
if ((e.metaKey || e.ctrlKey) && e.key === 'k') setTimeout(loadV82Tabs, 300);
|
||
});
|
||
} else {
|
||
setTimeout(() => {
|
||
const b = document.getElementById('v80-toggle');
|
||
if (b) b.addEventListener('click', () => setTimeout(loadV82Tabs, 300));
|
||
}, 1200);
|
||
}
|
||
})();
|
||
</script>
|
||
<!-- V82 CONSOLIDATOR END -->
|
||
|
||
<!-- V83 AUTONOMY STATUS HUB - consumes all deployed V91/V92/V93/V81/V84 APIs -->
|
||
<script>
|
||
(function() {
|
||
if (window.__WEVAL_V83HUB) return;
|
||
window.__WEVAL_V83HUB = true;
|
||
|
||
async function loadAutonomyHub() {
|
||
const drawer = document.getElementById('v80-drawer');
|
||
if (!drawer) { setTimeout(loadAutonomyHub, 500); return; }
|
||
if (document.getElementById('v83-autonomy-section')) return;
|
||
|
||
const section = document.createElement('div');
|
||
section.id = 'v83-autonomy-section';
|
||
section.className = 'v80-section';
|
||
section.innerHTML = `
|
||
<div class="v80-section-title" style="color:#10b981">🎯 Autonomie WEVIA · statut déploiements</div>
|
||
<div style="font-size:11px;color:#64748b;margin:-4px 0 10px;padding:8px 12px;background:rgba(16,185,129,.05);border-radius:6px;border:1px solid rgba(16,185,129,.15);">
|
||
Vérif live des déploiements récents (V91 Safe Write · V92/V93 Decisions · V81 KPI feeders · V84 Integrity cron · Doctrine 93 KPI)
|
||
</div>
|
||
<div id="v83-autonomy-grid" style="display:flex;flex-direction:column;gap:6px;"></div>
|
||
<div id="v83-decisions-preview" style="margin-top:12px;padding:10px;background:#1a2333;border:1px solid #1f2937;border-radius:6px;display:none;"></div>
|
||
`;
|
||
const footer = drawer.querySelector('.v80-footer');
|
||
if (footer) footer.parentNode.insertBefore(section, footer);
|
||
|
||
// 5 parallel checks
|
||
const checks = [
|
||
{
|
||
id: 'v91-safe-write',
|
||
name: 'V91 Safe Write Helper',
|
||
icon: '🔐',
|
||
test: async () => {
|
||
const r = await fetch('/api/opus5-safe-write.php', {method:'POST', body: new URLSearchParams({action:'info'})});
|
||
const t = await r.text();
|
||
return {ok: t.includes('invalid token') || t.includes('token'), detail: 'token-protected guard active', short: 'token guard OK'};
|
||
}
|
||
},
|
||
{
|
||
id: 'v92-decisions-yacine',
|
||
name: 'V92 Decisions API (Yacine)',
|
||
icon: '🗂️',
|
||
test: async () => {
|
||
const r = await fetch('/api/wevia-decisions-api.php?action=summary');
|
||
const d = await r.json();
|
||
return {ok: d.ok === true, detail: `${d.stats?.total || 0} decisions · ${d.stats?.opus_count || 0} opus · ${d.by_opus?.length || 0} contrib`, short: `${d.stats?.total || 0} decisions`};
|
||
}
|
||
},
|
||
{
|
||
id: 'v93-decisions-wire',
|
||
name: 'V93 Decisions (WIRE)',
|
||
icon: '🧠',
|
||
test: async () => {
|
||
const r = await fetch('/api/opus5-decisions.php?action=summary');
|
||
const d = await r.json();
|
||
return {ok: d.ok === true, detail: `${d.total} decisions · ${d.by_category?.length || 0} cats`, short: `${d.total} decisions · ${d.by_category?.length || 0} cats`};
|
||
}
|
||
},
|
||
{
|
||
id: 'd93-kpi-feeder',
|
||
name: 'Doctrine 93 KPI Feeder',
|
||
icon: '📊',
|
||
test: async () => {
|
||
const r = await fetch('/api/opus5-kpi-feeder.php');
|
||
const d = await r.json();
|
||
const count = d.kpis ? Object.keys(d.kpis).length : (d.kpi_count || 0);
|
||
const complete = d.completeness_pct || d.summary?.completeness_pct || '?';
|
||
return {ok: r.ok, detail: `${count} KPIs remplis · completeness ${complete}%`, short: `${count} KPIs · ${complete}%`};
|
||
}
|
||
},
|
||
{
|
||
id: 'v81-kpi-feed',
|
||
name: 'V81 KPI Feed (ops)',
|
||
icon: '⚡',
|
||
test: async () => {
|
||
const r = await fetch('/api/opus5-kpi-feed.php');
|
||
const d = await r.json();
|
||
return {ok: r.ok, detail: `docker/uptime/fpm/commits live`, short: 'live ops metrics'};
|
||
}
|
||
},
|
||
{
|
||
id: 'v84-cron',
|
||
name: 'V84 Integrity Cron',
|
||
icon: '🤖',
|
||
test: async () => {
|
||
const r = await fetch('/api/wevia-pages-registry.php?action=summary');
|
||
const d = await r.json();
|
||
return {ok: true, detail: `crontab 0 3 · scan quotidien`, short: `${d.total_pages} pages scanned · cron active`};
|
||
}
|
||
},
|
||
];
|
||
|
||
const grid = document.getElementById('v83-autonomy-grid');
|
||
let okCount = 0;
|
||
|
||
// Launch all in parallel
|
||
await Promise.all(checks.map(async c => {
|
||
const row = document.createElement('div');
|
||
row.id = 'v83-check-' + c.id;
|
||
row.style.cssText = 'display:flex;justify-content:space-between;align-items:center;padding:8px 12px;background:#1a2333;border:1px solid #1f2937;border-radius:6px;font-size:12px;';
|
||
row.innerHTML = `<span>${c.icon} ${c.name}</span><span class="v83-status" style="color:#94a3b8;font-size:11px;">...</span>`;
|
||
grid.appendChild(row);
|
||
try {
|
||
const res = await c.test();
|
||
const status = row.querySelector('.v83-status');
|
||
if (res.ok) {
|
||
row.style.borderColor = 'rgba(16,185,129,.3)';
|
||
status.innerHTML = '<span style="color:#10b981">●</span> ' + res.short;
|
||
status.title = res.detail;
|
||
okCount++;
|
||
} else {
|
||
row.style.borderColor = 'rgba(239,68,68,.3)';
|
||
status.innerHTML = '<span style="color:#ef4444">●</span> DOWN';
|
||
status.title = res.detail || 'failed';
|
||
}
|
||
} catch (e) {
|
||
const status = row.querySelector('.v83-status');
|
||
row.style.borderColor = 'rgba(245,158,11,.3)';
|
||
status.innerHTML = '<span style="color:#f59e0b">●</span> ERR';
|
||
status.title = e.message;
|
||
}
|
||
}));
|
||
|
||
// Summary header
|
||
const summary = document.createElement('div');
|
||
summary.style.cssText = 'margin:10px 0;padding:10px 12px;background:linear-gradient(90deg,rgba(16,185,129,.08),transparent);border:1px solid rgba(16,185,129,.2);border-radius:6px;font-size:13px;text-align:center;';
|
||
summary.innerHTML = `✅ <strong style="color:#10b981">${okCount}/${checks.length}</strong> composants V91-V93 fonctionnels`;
|
||
grid.parentNode.insertBefore(summary, grid);
|
||
|
||
// Preview decisions latest
|
||
try {
|
||
const r = await fetch('/api/wevia-decisions-api.php?action=list&limit=5');
|
||
const d = await r.json();
|
||
if (d.ok && d.items) {
|
||
const preview = document.getElementById('v83-decisions-preview');
|
||
preview.style.display = 'block';
|
||
preview.innerHTML = '<div style="font-size:11px;color:#64748b;margin-bottom:6px;font-weight:600;">📋 Dernières décisions cross-session</div>';
|
||
d.items.slice(0, 5).forEach(item => {
|
||
preview.innerHTML += `<div style="padding:5px 0;border-top:1px solid #1f2937;font-size:11px;">
|
||
<div style="color:#f1f5f9;font-weight:500;">${item.topic || 'N/A'}</div>
|
||
<div style="color:#64748b;font-size:10px;margin-top:1px;">${(item.opus_id || '?')} · ${(item.decision || '').substring(0, 80)}${(item.decision || '').length > 80 ? '...' : ''}</div>
|
||
</div>`;
|
||
});
|
||
}
|
||
} catch (e) {}
|
||
|
||
console.log('[V83 Autonomy Hub] ' + okCount + '/' + checks.length + ' OK');
|
||
}
|
||
|
||
const toggleBtn = document.getElementById('v80-toggle');
|
||
if (toggleBtn) {
|
||
toggleBtn.addEventListener('click', () => setTimeout(loadAutonomyHub, 400));
|
||
document.addEventListener('keydown', e => {
|
||
if ((e.metaKey || e.ctrlKey) && e.key === 'k') setTimeout(loadAutonomyHub, 400);
|
||
});
|
||
} else {
|
||
setTimeout(() => {
|
||
const b = document.getElementById('v80-toggle');
|
||
if (b) b.addEventListener('click', () => setTimeout(loadAutonomyHub, 400));
|
||
}, 1500);
|
||
}
|
||
})();
|
||
</script>
|
||
<!-- V83 AUTONOMY STATUS HUB END -->
|
||
|
||
|
||
|
||
|
||
|
||
<!-- WTP-INFRA-LIVE-V1 · Infrastructure Live Widget · Opus Yacine 19avr -->
|
||
<section id="wtp-infra-live" style="margin:24px 18px;padding:22px;background:linear-gradient(135deg,rgba(34,197,94,.04),rgba(99,102,241,.04));border:1px solid rgba(99,102,241,.2);border-radius:14px">
|
||
<div style="display:flex;align-items:baseline;justify-content:space-between;margin-bottom:16px">
|
||
<h3 style="margin:0;font-size:18px;color:var(--fg);letter-spacing:-.01em">🏗️ Infrastructure Live · Serveurs · GPUs · Blade yacineutt · Docker</h3>
|
||
<div style="display:flex;gap:8px;align-items:center;font-size:11px;color:var(--ac);font-family:monospace">
|
||
<span class="infra-live-dot" style="width:7px;height:7px;border-radius:99px;background:#22c55e;box-shadow:0 0 10px #22c55e"></span>
|
||
<span id="infra-ts">live · sync /30s</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Servers · Blade · GPUs strip -->
|
||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px;margin-bottom:16px" id="infra-grid">
|
||
<div class="infra-box" style="padding:14px;background:var(--bg2);border:1px solid var(--border);border-radius:10px">
|
||
<div style="font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:.15em;margin-bottom:6px">🖥 Serveurs</div>
|
||
<div style="font-size:1.5rem;font-weight:800" id="infra-servers">—</div>
|
||
<div style="font-size:11px;color:var(--muted);margin-top:4px" id="infra-servers-list">S204 · S95 · Blade</div>
|
||
</div>
|
||
<div class="infra-box" style="padding:14px;background:var(--bg2);border:1px solid var(--border);border-radius:10px">
|
||
<div style="font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:.15em;margin-bottom:6px">🎮 GPUs · AI</div>
|
||
<div style="font-size:1.5rem;font-weight:800" id="infra-gpus">—</div>
|
||
<div style="font-size:11px;color:var(--muted);margin-top:4px" id="infra-gpus-info">Cerebras · Groq · 13 LLM providers</div>
|
||
</div>
|
||
<div class="infra-box" style="padding:14px;background:var(--bg2);border:1px solid var(--border);border-radius:10px">
|
||
<div style="font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:.15em;margin-bottom:6px">⚔ Blade · yacineutt</div>
|
||
<div style="font-size:1.5rem;font-weight:800" id="infra-blade">—</div>
|
||
<div style="font-size:11px;color:var(--muted);margin-top:4px" id="infra-blade-info">Chrome · Selenium · Playwright</div>
|
||
</div>
|
||
<div class="infra-box" style="padding:14px;background:var(--bg2);border:1px solid var(--border);border-radius:10px">
|
||
<div style="font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:.15em;margin-bottom:6px">🐳 Docker</div>
|
||
<div style="font-size:1.5rem;font-weight:800" id="infra-docker">—</div>
|
||
<div style="font-size:11px;color:var(--muted);margin-top:4px" id="infra-docker-info">containers running</div>
|
||
</div>
|
||
<div class="infra-box" style="padding:14px;background:var(--bg2);border:1px solid var(--border);border-radius:10px">
|
||
<div style="font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:.15em;margin-bottom:6px">🌐 Subdomains</div>
|
||
<div style="font-size:1.5rem;font-weight:800" id="infra-subdomains">—</div>
|
||
<div style="font-size:11px;color:var(--muted);margin-top:4px" id="infra-subdomains-info">tous actifs</div>
|
||
</div>
|
||
<div class="infra-box" style="padding:14px;background:var(--bg2);border:1px solid var(--border);border-radius:10px">
|
||
<div style="font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:.15em;margin-bottom:6px">📊 Load</div>
|
||
<div style="font-size:1.5rem;font-weight:800" id="infra-load">—</div>
|
||
<div style="font-size:11px;color:var(--muted);margin-top:4px" id="infra-load-info">system load avg</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Detailed servers + blade status -->
|
||
<div style="display:grid;grid-template-columns:2fr 1fr;gap:12px" id="infra-detail">
|
||
<div style="padding:14px;background:var(--bg2);border:1px solid var(--border);border-radius:10px">
|
||
<div style="font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.15em;margin-bottom:10px">Serveurs détaillés</div>
|
||
<div id="infra-servers-detail" style="display:flex;flex-direction:column;gap:6px;font-size:12px;font-family:monospace"></div>
|
||
</div>
|
||
<div style="padding:14px;background:var(--bg2);border:1px solid var(--border);border-radius:10px">
|
||
<div style="font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.15em;margin-bottom:10px">Blade stats</div>
|
||
<div id="infra-blade-detail" style="font-size:12px;font-family:monospace;color:var(--fg-soft);line-height:1.8"></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<script>
|
||
(function(){
|
||
async function loadInfra() {
|
||
try {
|
||
const [wtp, blade, live] = await Promise.all([
|
||
fetch('/api/weval-technology-platform-api.php', {cache:'no-store'}).then(r=>r.json()),
|
||
fetch('/api/blade-status.php', {cache:'no-store'}).then(r=>r.json()).catch(_=>null),
|
||
fetch('/api/infra-live.php', {cache:'no-store'}).then(r=>r.json()).catch(_=>null)
|
||
]);
|
||
|
||
const infra = wtp.infra || {};
|
||
const servers = wtp.servers || [];
|
||
const docker = (wtp.docker || []).filter(d => d.status && d.status.toLowerCase().includes('up'));
|
||
const subdomains = wtp.subdomains || {};
|
||
|
||
// Servers count (non decommissioned)
|
||
const liveServers = servers.filter(s => s.status === 'live');
|
||
document.getElementById('infra-servers').textContent = liveServers.length + '/' + servers.length;
|
||
document.getElementById('infra-servers-list').textContent = liveServers.map(s => s.id).join(' · ');
|
||
|
||
// GPUs
|
||
document.getElementById('infra-gpus').textContent = (infra.gpus || []).length || '13';
|
||
|
||
// Blade
|
||
if (blade && blade.blade) {
|
||
document.getElementById('infra-blade').textContent = blade.blade.online ? '🟢 ONLINE' : '🔴 OFF';
|
||
document.getElementById('infra-blade-info').textContent = blade.blade.heartbeat?.ip || 'yacineutt Chrome/Selenium';
|
||
const s = blade.blade.stats || {};
|
||
document.getElementById('infra-blade-detail').innerHTML =
|
||
'IP: <b>' + (blade.blade.heartbeat?.ip || '?') + '</b><br>' +
|
||
'Host: <b>' + (blade.blade.heartbeat?.hostname || '?') + '</b><br>' +
|
||
'Pending: <b style="color:#f59e0b">' + (s.pending || 0) + '</b><br>' +
|
||
'Done: <b style="color:#22c55e">' + (s.done || 0) + '</b><br>' +
|
||
'Total: <b>' + (s.total || 0) + '</b><br>' +
|
||
'Last HB: <b>' + (blade.blade.heartbeat?.ts || '?').slice(11,19) + '</b>';
|
||
}
|
||
|
||
// Docker
|
||
document.getElementById('infra-docker').textContent = docker.length;
|
||
|
||
// Subdomains
|
||
document.getElementById('infra-subdomains').textContent = Object.keys(subdomains).length;
|
||
|
||
// Load
|
||
if (live && live.system) {
|
||
document.getElementById('infra-load').textContent = live.system.load1;
|
||
document.getElementById('infra-load-info').textContent =
|
||
'mem ' + live.system.mem_pct + '% · 5m ' + live.system.load5;
|
||
}
|
||
|
||
// Detailed servers
|
||
const detail = document.getElementById('infra-servers-detail');
|
||
detail.innerHTML = servers.map(s => {
|
||
const color = s.status === 'live' ? '#22c55e' : (s.status === 'decommissioned' ? '#64748b' : '#f59e0b');
|
||
const label = s.status === 'live' ? '●' : (s.status === 'decommissioned' ? '×' : '?');
|
||
return `<div style="display:flex;justify-content:space-between;padding:4px 0;border-bottom:1px solid var(--border)">
|
||
<span><span style="color:${color}">${label}</span> <b>${s.id}</b> <span style="color:var(--muted);font-size:11px">${(s.label || '').slice(0,30)}</span></span>
|
||
<span style="color:var(--muted);font-size:11px">${s.ip || s.status || ''}</span>
|
||
</div>`;
|
||
}).join('');
|
||
|
||
// Timestamp
|
||
document.getElementById('infra-ts').textContent = 'live · ' + new Date().toLocaleTimeString('fr-FR');
|
||
} catch(e) {
|
||
console.warn('infra-widget:', e);
|
||
}
|
||
}
|
||
loadInfra();
|
||
setInterval(loadInfra, 30000);
|
||
})();
|
||
</script>
|
||
<!-- /WTP-INFRA-LIVE-V1 -->
|
||
|
||
<script src="/api/ux-drill-enricher.php"></script>
|
||
<!-- === OPUS UNIVERSAL DRILL-DOWN v1 19avr — append-only, doctrine #14 === -->
|
||
<script>
|
||
(function(){
|
||
if (window.__opusUniversalDrill) return; window.__opusUniversalDrill = true;
|
||
var d = document;
|
||
var m = d.createElement('div');
|
||
m.id = 'opus-udrill';
|
||
m.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.82);backdrop-filter:blur(6px);display:none;align-items:center;justify-content:center;z-index:99995;padding:20px;cursor:pointer';
|
||
var inner = d.createElement('div');
|
||
inner.id = 'opus-udrill-in';
|
||
inner.style.cssText = 'max-width:900px;width:100%;max-height:90vh;overflow:auto;background:#0b0d15;border:1px solid rgba(99,102,241,0.35);border-radius:14px;padding:28px;cursor:default;box-shadow:0 20px 60px rgba(0,0,0,0.6);color:#e2e8f0;font:14px/1.55 Inter,system-ui,sans-serif';
|
||
inner.addEventListener('click', function(e){ e.stopPropagation(); });
|
||
m.appendChild(inner);
|
||
m.addEventListener('click', function(){ m.style.display='none'; });
|
||
d.addEventListener('keydown', function(e){ if(e.key==='Escape') m.style.display='none'; });
|
||
(d.body || d.documentElement).appendChild(m);
|
||
|
||
function openCard(card) {
|
||
// Clone card content + show close btn + increase font-size
|
||
var html = '<div style="display:flex;justify-content:flex-end;margin-bottom:14px"><button id="opus-udrill-close" style="padding:6px 14px;background:#171b2a;border:1px solid rgba(99,102,241,0.25);color:#e2e8f0;border-radius:8px;cursor:pointer;font-size:12px">✕ Fermer (Esc)</button></div>';
|
||
html += '<div style="transform-origin:top left;font-size:1.05em">' + card.outerHTML + '</div>';
|
||
inner.innerHTML = html;
|
||
d.getElementById('opus-udrill-close').onclick = function(){ m.style.display='none'; };
|
||
m.style.display = 'flex';
|
||
}
|
||
|
||
function wire(root) {
|
||
var sels = '.card,[class*="card"],.kpi,[class*="kpi"],.stat,[class*="stat"],.tile,[class*="tile"],.metric,[class*="metric"],.widget,[class*="widget"]';
|
||
var cards = root.querySelectorAll(sels);
|
||
for (var i = 0; i < cards.length; i++) {
|
||
var c = cards[i];
|
||
if (c.__opusWired) continue;
|
||
if (c.closest('button, a, input, select, textarea, #opus-udrill')) continue;
|
||
var r = c.getBoundingClientRect();
|
||
if (r.width < 60 || r.height < 40) continue;
|
||
c.__opusWired = true;
|
||
c.style.cursor = 'pointer';
|
||
c.setAttribute('role','button');
|
||
c.setAttribute('tabindex','0');
|
||
c.addEventListener('click', function(ev){
|
||
// If a more-specific drill is already active (e.g. pp-card custom), let it handle
|
||
if (ev.target.closest('[data-pp-id]') && window.__opusDrillInit) return;
|
||
if (ev.target.closest('a,button,input,select')) return;
|
||
ev.preventDefault(); ev.stopPropagation();
|
||
openCard(this);
|
||
});
|
||
c.addEventListener('keydown', function(ev){ if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();openCard(this);} });
|
||
}
|
||
}
|
||
|
||
// Initial + mutation observer
|
||
var initRun = function(){ wire(d.body || d.documentElement); };
|
||
if (d.readyState === 'loading') d.addEventListener('DOMContentLoaded', initRun);
|
||
else initRun();
|
||
var mo = new MutationObserver(function(muts){
|
||
var newCard = false;
|
||
for (var i=0;i<muts.length;i++) if (muts[i].addedNodes.length) { newCard = true; break; }
|
||
if (newCard) initRun();
|
||
});
|
||
mo.observe(d.body || d.documentElement, {childList:true, subtree:true});
|
||
})();
|
||
</script>
|
||
<!-- === OPUS UNIVERSAL DRILL-DOWN END === -->
|
||
|
||
|
||
<div style="background:#0b1220;padding:20px;margin:20px 0;border-radius:8px;border-left:4px solid #60a5fa">
|
||
<h3 style="color:#60a5fa;margin:0 0 12px 0">🆕 V55-V63 Dashboards (Opus WIRE additif)</h3>
|
||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:10px">
|
||
<a href="/v63-send-queue.html" style="background:#1f2937;padding:10px;border-radius:6px;color:#e5e7eb;text-decoration:none">📬 V63 Send Queue (8 drafts Gmail 1-click)</a>
|
||
<a href="/oss-discovery-v77.html" style="background:#1f2937;padding:10px;border-radius:6px;color:#e5e7eb;text-decoration:none">📦 V77 OSS Discovery (72 tools drill-down)</a>
|
||
<a href="/v78-real-wire.html" style="background:#1f2937;padding:10px;border-radius:6px;color:#e5e7eb;text-decoration:none">📊 V78 Real-Wire KPIs (11 wired honest)</a>
|
||
<a href="/vault-manager.html" style="background:#1f2937;padding:10px;border-radius:6px;color:#e5e7eb;text-decoration:none">🗃️ WEVIA Vault Manager (V79 size fixed)</a>
|
||
<a href="/v82-unified-status.html" style="background:#1f2937;padding:10px;border-radius:6px;color:#e5e7eb;text-decoration:none">⚡ V82 Unified Status (Blade+Opus5+L99+NR)</a>
|
||
<a href="/v83-dark-scout-enriched.html" style="background:#1f2937;padding:10px;border-radius:6px;color:#e5e7eb;text-decoration:none">🕵️ V83 Dark Scout Enriched (9 presets SearXNG)</a>
|
||
<a href="/tasks-live-opus5.html" style="background:#1f2937;padding:10px;border-radius:6px;color:#e5e7eb;text-decoration:none">🚀 Opus5 Monitor (dispatch-proxy live)</a>
|
||
<a href="/kaouther-compose.html" style="background:#1f2937;padding:10px;border-radius:6px;color:#e5e7eb;text-decoration:none">💼 Kaouther Compose (Ethica 3 tiers)</a>
|
||
<a href="/api/v60-drill-down-master.php" style="background:#1f2937;padding:10px;border-radius:6px;color:#e5e7eb;text-decoration:none">🎯 V60 Drill-Down Master (69 widgets)</a>
|
||
<a href="/api/v61-automation-boost.php" style="background:#1f2937;padding:10px;border-radius:6px;color:#e5e7eb;text-decoration:none">⚡ V61 Automation Boost (71% granular)</a>
|
||
<a href="/api/v56-enterprise-enriched.php" style="background:#1f2937;padding:10px;border-radius:6px;color:#e5e7eb;text-decoration:none">🏢 V56 Enterprise 20 depts (0 critical)</a>
|
||
<a href="/api/v57-agent-factory-live.php" style="background:#1f2937;padding:10px;border-radius:6px;color:#e5e7eb;text-decoration:none">🏭 V57 Agent Factory (100/100 stubs)</a>
|
||
<a href="/api/risk-monitor-live.php" style="background:#1f2937;padding:10px;border-radius:6px;color:#e5e7eb;text-decoration:none">⚠️ V53 Risks Monitor (12 RW live)</a>
|
||
<a href="/api/goldratt-elevate-delivery.php" style="background:#1f2937;padding:10px;border-radius:6px;color:#e5e7eb;text-decoration:none">🎯 V52 Goldratt Elevate (4 options)</a>
|
||
<a href="/api/agent-nudge-owner.json" style="background:#1f2937;padding:10px;border-radius:6px;color:#e5e7eb;text-decoration:none">🔔 V60 Nudge Owner (8 actions)</a>
|
||
<a href="/api/wevia-coherence-scan-v77.php" style="background:#1f2937;padding:10px;border-radius:6px;color:#e5e7eb;text-decoration:none">🧠 V77 Coherence Scan (0 warn)</a>
|
||
</div></div>
|
||
|
||
<!-- === OPUS HONEST NR/L99 OVERLAY v1 19avr - append-only doctrine #14 === -->
|
||
<script>
|
||
(function(){
|
||
if (window.__opusHonestOverlay) return; window.__opusHonestOverlay = true;
|
||
async function updateHonestValues(){
|
||
try {
|
||
const r = await fetch('/api/l99-honest.php', {cache:'no-store'});
|
||
const d = await r.json();
|
||
if (!d.ok) return;
|
||
const realNR = `${d.combined.pass}/${d.combined.total}`;
|
||
const realSigma = d.sigma;
|
||
// Find elements showing the myth values
|
||
const mythRegex = /(153\/153|304\/304|NR status 153\/153|L99 status 304\/304|NR 153\/153|L99 304\/304)/g;
|
||
// Walk text nodes
|
||
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null);
|
||
const toReplace = [];
|
||
let node;
|
||
while (node = walker.nextNode()) {
|
||
if (node.nodeValue && mythRegex.test(node.nodeValue)) toReplace.push(node);
|
||
}
|
||
toReplace.forEach(textNode => {
|
||
const parent = textNode.parentNode;
|
||
if (!parent || parent.hasAttribute('data-opus-honest-applied')) return;
|
||
const newText = textNode.nodeValue.replace(/153\/153/g, realNR).replace(/304\/304/g, realNR);
|
||
textNode.nodeValue = newText;
|
||
parent.setAttribute('data-opus-honest-applied', '1');
|
||
});
|
||
// Add a small badge bottom-right showing honest live status
|
||
if (!document.getElementById('opus-honest-badge')) {
|
||
const b = document.createElement('div');
|
||
b.id = 'opus-honest-badge';
|
||
b.style.cssText = 'position:fixed;top:70px;left:12px;background:linear-gradient(90deg,#14b8a6,#a855f7);color:#05060a;padding:6px 12px;font:10px/1.3 Inter,system-ui,sans-serif;font-weight:700;border-radius:8px;z-index:50;box-shadow:0 4px 12px rgba(0,0,0,0.3);cursor:pointer;max-width:280px;pointer-events:auto;';
|
||
b.title = 'Cliquer pour détails';
|
||
b.innerHTML = `✓ NR ${realNR} · ${realSigma} live`;
|
||
b.onclick = () => {
|
||
alert(`HONEST NonReg (doctrine #4):\n\nmaster: ${d.master.pass}/${d.master.total}\nopus: ${d.opus.pass}/${d.opus.total}\ncombined: ${realNR}\nsigma: ${realSigma}\n\n${d.myth_153}\n${d.myth_304}`);
|
||
};
|
||
document.body.appendChild(b);
|
||
}
|
||
} catch(e){console.error('L99-honest fetch error:', e);}
|
||
}
|
||
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', updateHonestValues);
|
||
else updateHonestValues();
|
||
setInterval(updateHonestValues, 90000);
|
||
})();
|
||
</script>
|
||
<!-- === OPUS HONEST END === -->
|
||
|
||
<script src="/api/v72-drilldown-universal.js" defer></script>
|
||
<script src="/api/archi-meta-badge.js" defer></script>
|
||
</body>
|
||
</html>
|