🎯 HUB v2 · AGENTS CONSOLIDÉS VRAIMENT · 270 agents uniques dédupliqués sur 9 sources: agent_avatars_v2 148 + agent_avatars_v1 136 + wevia_agents_registry 47 + paperclip_agility_v71 12 + api_agent_files 6 + agent_stubs 50 + claude_subagents 65 + qdrant_agents_vectorized 0 + paperclip_opt_catalog 10 = 474 total avec overlaps · fin dispersion · explique écart 950 (marketing) vs 126 (ancien) vs 270 (vrai) · doctrines 0 (gap à remplir) · brains 11 · dashboards 92 · APIs 644 [Opus Yacine]
Some checks failed
WEVAL NonReg / nonreg (push) Has been cancelled
Some checks failed
WEVAL NonReg / nonreg (push) Has been cancelled
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>WEVIA Unified Hub · 1 page pour tout</title>
|
||||
<title>WEVIA Unified Hub v2 · tout dédupliqué</title>
|
||||
<style>
|
||||
:root{--bg:#0a0e1a;--panel:#141933;--panel2:#1a2140;--border:#263161;--text:#e4e8f7;--muted:#8b95b8;--accent:#00d4b4;--green:#10b981;--amber:#f59e0b;--red:#ef4444;--blue:#6ba3ff;--purple:#c084fc}
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
@@ -8,44 +8,46 @@ body{font-family:-apple-system,Segoe UI,Roboto,sans-serif;background:var(--bg);c
|
||||
.layout{display:grid;grid-template-columns:240px 1fr;min-height:100vh}
|
||||
.sidebar{background:var(--panel);border-right:1px solid var(--border);padding:20px 16px;position:sticky;top:0;height:100vh;overflow-y:auto}
|
||||
.sidebar h3{font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:1px;margin:16px 0 8px 0;font-weight:600}
|
||||
.sidebar a{display:block;padding:8px 12px;color:var(--text);text-decoration:none;font-size:13px;border-radius:6px;margin-bottom:2px}
|
||||
.sidebar a{display:block;padding:8px 12px;color:var(--text);text-decoration:none;font-size:13px;border-radius:6px;margin-bottom:2px;cursor:pointer}
|
||||
.sidebar a:hover{background:var(--panel2);color:var(--accent)}
|
||||
.sidebar a.active{background:var(--accent);color:#000;font-weight:600}
|
||||
.sidebar .logo{font-size:18px;font-weight:800;color:#fff;margin-bottom:18px;display:flex;align-items:center;gap:8px}
|
||||
.main{padding:24px 30px}
|
||||
.sidebar .logo{font-size:18px;font-weight:800;color:#fff;margin-bottom:18px}
|
||||
.main{padding:24px 30px;max-width:1500px}
|
||||
.header{display:flex;justify-content:space-between;align-items:center;padding:18px 24px;background:linear-gradient(135deg,#1e1b4b,#312e81);border-radius:12px;margin-bottom:20px;border:1px solid var(--border)}
|
||||
.header h1{color:#fff;font-size:22px}
|
||||
.header .sub{color:rgba(255,255,255,.7);font-size:11.5px;margin-top:2px}
|
||||
.header .score{padding:6px 14px;background:rgba(16,185,129,.25);color:#6ee7b7;border:1px solid rgba(16,185,129,.5);border-radius:99px;font-size:12px;font-weight:700}
|
||||
.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:10px;margin-bottom:22px}
|
||||
.kpi{background:var(--panel);border:1px solid var(--border);border-radius:10px;padding:14px 16px;position:relative;transition:all .2s;cursor:pointer}
|
||||
.kpi{background:var(--panel);border:1px solid var(--border);border-radius:10px;padding:14px 16px;position:relative;cursor:pointer;transition:all .2s}
|
||||
.kpi:hover{border-color:var(--accent);transform:translateY(-2px)}
|
||||
.kpi::before{content:'';position:absolute;top:0;left:0;width:4px;height:100%;background:var(--accent);border-radius:4px 0 0 4px}
|
||||
.kpi.agents::before{background:var(--purple)}
|
||||
.kpi.intents::before{background:var(--blue)}
|
||||
.kpi.skills::before{background:var(--green)}
|
||||
.kpi.brains::before{background:var(--amber)}
|
||||
.kpi.dashboards::before{background:#ec4899}
|
||||
.kpi.apis::before{background:#14b8a6}
|
||||
.kpi.agents::before{background:var(--purple)}.kpi.intents::before{background:var(--blue)}.kpi.skills::before{background:var(--green)}.kpi.brains::before{background:var(--amber)}.kpi.dashboards::before{background:#ec4899}.kpi.apis::before{background:#14b8a6}
|
||||
.kpi .l{font-size:10.5px;color:var(--muted);text-transform:uppercase;letter-spacing:.5px;margin-bottom:4px;font-weight:600}
|
||||
.kpi .v{font-size:28px;font-weight:800;color:var(--text);font-family:'SF Mono',monospace;line-height:1}
|
||||
.kpi .s{font-size:10.5px;color:var(--muted);margin-top:4px}
|
||||
.section{background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:18px 22px;margin-bottom:18px}
|
||||
.section h2{font-size:15px;font-weight:600;color:var(--accent);margin-bottom:12px;padding-bottom:10px;border-bottom:1px dashed var(--border);display:flex;justify-content:space-between;align-items:center}
|
||||
.section h2 .count{font-family:monospace;background:rgba(0,212,180,.15);color:var(--accent);padding:3px 9px;border-radius:4px;font-size:12px}
|
||||
.tabs{display:flex;gap:6px;margin-bottom:14px;border-bottom:1px solid var(--border);padding-bottom:0}
|
||||
.sources-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:8px;margin:14px 0}
|
||||
.source-box{background:var(--panel2);border:1px solid var(--border);border-radius:6px;padding:10px 12px}
|
||||
.source-box .n{font-size:10.5px;color:var(--muted);text-transform:uppercase;letter-spacing:.5px;font-weight:600;margin-bottom:4px}
|
||||
.source-box .c{font-size:22px;font-weight:800;color:var(--accent);font-family:monospace;line-height:1}
|
||||
.explanation{padding:14px;background:rgba(0,212,180,.05);border:1px solid rgba(0,212,180,.2);border-radius:8px;margin-bottom:14px;font-size:12.5px;color:var(--text);line-height:1.6}
|
||||
.explanation b{color:var(--accent)}
|
||||
.tabs{display:flex;gap:6px;margin-bottom:14px;border-bottom:1px solid var(--border);padding-bottom:0;flex-wrap:wrap}
|
||||
.tab{padding:8px 14px;cursor:pointer;font-size:12px;color:var(--muted);border-radius:6px 6px 0 0;border:1px solid transparent;font-weight:500}
|
||||
.tab:hover{color:var(--text)}
|
||||
.tab.active{color:var(--accent);border-color:var(--border);border-bottom-color:var(--panel);background:var(--panel2);font-weight:700}
|
||||
.tab .count{font-family:monospace;background:rgba(0,212,180,.12);color:var(--accent);padding:1px 6px;border-radius:3px;margin-left:6px;font-size:10.5px}
|
||||
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:10px;max-height:500px;overflow-y:auto;padding-right:4px}
|
||||
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:10px;max-height:600px;overflow-y:auto;padding-right:4px}
|
||||
.grid::-webkit-scrollbar{width:6px}
|
||||
.grid::-webkit-scrollbar-track{background:var(--bg)}
|
||||
.grid::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}
|
||||
.card{background:var(--panel2);border:1px solid var(--border);border-radius:8px;padding:11px 14px;transition:border-color .15s}
|
||||
.card:hover{border-color:var(--accent)}
|
||||
.card .name{font-size:12.5px;font-weight:700;color:var(--accent);font-family:monospace;margin-bottom:3px}
|
||||
.card .meta{font-size:10.5px;color:var(--muted);display:flex;gap:10px;flex-wrap:wrap}
|
||||
.card .name{font-size:12.5px;font-weight:700;color:var(--accent);margin-bottom:3px;display:flex;align-items:center;gap:6px}
|
||||
.card .name .icon{font-size:16px}
|
||||
.card .meta{font-size:10.5px;color:var(--muted);display:flex;gap:6px;flex-wrap:wrap;margin-top:3px}
|
||||
.card .desc{font-size:11.5px;color:var(--text);margin-top:6px;line-height:1.35}
|
||||
.badge{font-size:10px;padding:2px 6px;border-radius:3px;font-weight:600}
|
||||
.badge.green{background:rgba(16,185,129,.2);color:#6ee7b7}
|
||||
@@ -53,341 +55,300 @@ body{font-family:-apple-system,Segoe UI,Roboto,sans-serif;background:var(--bg);c
|
||||
.badge.purple{background:rgba(192,132,252,.2);color:#d8b4fe}
|
||||
.badge.amber{background:rgba(245,158,11,.2);color:#fbbf24}
|
||||
.badge.red{background:rgba(239,68,68,.2);color:#fca5a5}
|
||||
.sources-pills{display:flex;gap:3px;flex-wrap:wrap;margin-top:4px}
|
||||
.sources-pills .pill{font-size:9.5px;padding:1px 6px;border-radius:3px;background:rgba(0,212,180,.1);color:var(--accent);border:1px solid rgba(0,212,180,.2)}
|
||||
.search{width:100%;padding:8px 12px;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:5px;font-size:12.5px;margin-bottom:10px}
|
||||
.search:focus{outline:none;border-color:var(--accent)}
|
||||
.legend-row{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;background:var(--panel2);border-radius:6px;margin-bottom:6px;font-size:12px}
|
||||
.legend-row{display:flex;justify-content:space-between;align-items:center;padding:7px 12px;background:var(--panel2);border-radius:5px;margin-bottom:4px;font-size:12px}
|
||||
.legend-row .b{font-family:monospace;color:var(--accent);font-weight:700}
|
||||
.footer{text-align:center;margin-top:20px;font-size:10.5px;color:var(--muted);padding:16px}
|
||||
.footer a{color:#93c5fd;text-decoration:none;margin:0 8px}
|
||||
.hidden{display:none}
|
||||
a.card-link{text-decoration:none;color:inherit;display:block}
|
||||
@media (max-width: 900px) {
|
||||
.layout{grid-template-columns:1fr}
|
||||
.sidebar{position:relative;height:auto;padding:12px}
|
||||
}
|
||||
@media (max-width: 900px){.layout{grid-template-columns:1fr}.sidebar{position:relative;height:auto;padding:12px}}
|
||||
</style></head>
|
||||
<body>
|
||||
|
||||
<div class="layout">
|
||||
<aside class="sidebar">
|
||||
<div class="logo">🧠 WEVIA Hub</div>
|
||||
|
||||
<div class="logo">🧠 WEVIA Hub v2</div>
|
||||
<h3>Vue consolidée</h3>
|
||||
<a href="#summary" class="active" onclick="show('summary', event)">📊 Summary</a>
|
||||
<a href="#agents" onclick="show('agents', event)">🤖 Agents</a>
|
||||
<a href="#intents" onclick="show('intents', event)">🎯 Intents</a>
|
||||
<a href="#skills" onclick="show('skills', event)">⚡ Skills</a>
|
||||
<a href="#brains" onclick="show('brains', event)">🧠 Brains</a>
|
||||
<a href="#dashboards" onclick="show('dashboards', event)">📊 Dashboards</a>
|
||||
|
||||
<h3>Principaux outils</h3>
|
||||
<a onclick="show('summary', event)" class="active">📊 Summary</a>
|
||||
<a onclick="show('agents', event)">🤖 Agents</a>
|
||||
<a onclick="show('intents', event)">🎯 Intents</a>
|
||||
<a onclick="show('skills', event)">⚡ Skills</a>
|
||||
<a onclick="show('brains', event)">🧠 Brains</a>
|
||||
<a onclick="show('dashboards', event)">📊 Dashboards</a>
|
||||
<h3>Outils</h3>
|
||||
<a href="/wevia-autonomy-dashboard.html">🏆 Autonomy Dashboard</a>
|
||||
<a href="/wevia-master.html">💬 WEVIA Master Chat</a>
|
||||
<a href="/wevia-business-visual-studio.html">🎬 BVS Studio</a>
|
||||
<a href="/visual-management.html">📊 Visual Mgmt</a>
|
||||
<a href="/skills/">📦 Skills Explorer</a>
|
||||
<a href="/wevia-training.html">🎓 Training</a>
|
||||
<a href="/weval-technology-platform.html">🎯 WTP</a>
|
||||
<a href="/l99.html">📐 L99</a>
|
||||
|
||||
<h3>Raccourcis</h3>
|
||||
<a href="/dashboards-hub.html">🔗 All Dashboards</a>
|
||||
<a href="/agents-hub.html">🤖 Agents Hub</a>
|
||||
<a href="/api/wevia-neurorag-api.php?action=status" target="_blank">🔌 NeuroRAG API</a>
|
||||
<a href="/api/arena-intent-registry.json" target="_blank">📜 Arena Registry</a>
|
||||
</aside>
|
||||
|
||||
<main class="main">
|
||||
<div class="header">
|
||||
<div>
|
||||
<h1>🧠 WEVIA Unified Hub</h1>
|
||||
<div class="sub">Une page pour voir agents · intents · skills · brains · dashboards · APIs</div>
|
||||
</div>
|
||||
<div><h1>🧠 WEVIA Unified Hub v2 · dédupliqué</h1>
|
||||
<div class="sub">TOUTES les sources agrégées · déduplication par nom · finie la dispersion</div></div>
|
||||
<div class="score">● GODMODE 100/100</div>
|
||||
</div>
|
||||
|
||||
<div class="kpis" id="kpis-row"></div>
|
||||
<div class="kpis" id="kpis"></div>
|
||||
|
||||
<!-- SUMMARY VIEW -->
|
||||
<!-- SUMMARY -->
|
||||
<div id="view-summary" class="view">
|
||||
<div class="section">
|
||||
<h2>📊 Agents répartition par source</h2>
|
||||
<div id="agents-breakdown"></div>
|
||||
<h2>🤖 Agents · sources agrégées</h2>
|
||||
<div class="explanation">
|
||||
<b>Les KPI montrés dans les autres dashboards viennent de sources différentes :</b><br>
|
||||
• <b>950</b> (wevia-training.html / WTP) = chiffre marketing <i>paperclip+ ecosystem reference</i><br>
|
||||
• <b>990</b> (paperclip dashboard) = tools-registry 619 + ancien count<br>
|
||||
• <b>126</b> (ancien unified hub) = 3 sources seulement<br>
|
||||
<b>→ Vrai count : <span style="color:var(--accent);font-weight:700" id="dedup-count">…</span> agents uniques dédupliqués sur 7+ sources (total avec overlaps : <span id="overlap-count">…</span>)</b>
|
||||
</div>
|
||||
<div class="sources-grid" id="agent-sources"></div>
|
||||
</div>
|
||||
<div class="section">
|
||||
<h2>🎯 Intents par status</h2>
|
||||
<div id="intents-status"></div>
|
||||
<h2>🎯 Intents · status + domaine</h2>
|
||||
<div id="intents-summary"></div>
|
||||
</div>
|
||||
<div class="section">
|
||||
<h2>🎯 Intents par domaine</h2>
|
||||
<div id="intents-domains"></div>
|
||||
</div>
|
||||
<div class="section">
|
||||
<h2>📊 Dashboards par catégorie</h2>
|
||||
<div id="dashboards-cats"></div>
|
||||
<h2>⚡ Skills · 5 sources</h2>
|
||||
<div id="skills-summary"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AGENTS VIEW -->
|
||||
<!-- AGENTS -->
|
||||
<div id="view-agents" class="view hidden">
|
||||
<div class="section">
|
||||
<h2>🤖 Tous les agents <span class="count" id="agents-count">—</span></h2>
|
||||
<div class="tabs">
|
||||
<div class="tab active" onclick="tabAgents('all', event)">Tous<span class="count" id="a-all">—</span></div>
|
||||
<div class="tab" onclick="tabAgents('api_files', event)">API files<span class="count" id="a-api_files">—</span></div>
|
||||
<div class="tab" onclick="tabAgents('agent_stubs', event)">Agent stubs<span class="count" id="a-agent_stubs">—</span></div>
|
||||
<div class="tab" onclick="tabAgents('claude_code_subagents', event)">Claude sub-agents<span class="count" id="a-claude_code_subagents">—</span></div>
|
||||
</div>
|
||||
<h2>🤖 269 agents uniques (sources agrégées) <span class="count" id="ag-total">—</span></h2>
|
||||
<div class="tabs" id="ag-tabs"></div>
|
||||
<input type="text" class="search" id="search-agents" placeholder="🔎 Filter agents..." oninput="filterAgents()">
|
||||
<div class="grid" id="agents-grid"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- INTENTS VIEW -->
|
||||
<!-- INTENTS -->
|
||||
<div id="view-intents" class="view hidden">
|
||||
<div class="section">
|
||||
<h2>🎯 Tous les intents <span class="count" id="intents-count">—</span></h2>
|
||||
<div class="tabs" id="intent-tabs"></div>
|
||||
<input type="text" class="search" id="search-intents" placeholder="🔎 Filter intents (name, trigger, domain)..." oninput="filterIntents()">
|
||||
<h2>🎯 Intents <span class="count" id="int-total">—</span></h2>
|
||||
<div class="tabs" id="int-tabs"></div>
|
||||
<input type="text" class="search" id="search-intents" placeholder="🔎 Filter intents..." oninput="filterIntents()">
|
||||
<div class="grid" id="intents-grid"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SKILLS VIEW -->
|
||||
<!-- SKILLS -->
|
||||
<div id="view-skills" class="view hidden">
|
||||
<div class="section">
|
||||
<h2>⚡ Skills multi-source <span class="count" id="skills-count">—</span></h2>
|
||||
<div id="skills-content"></div>
|
||||
<h2>⚡ Skills 15 509 <span class="count">5 sources</span></h2>
|
||||
<div id="skills-view"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- BRAINS VIEW -->
|
||||
<!-- BRAINS -->
|
||||
<div id="view-brains" class="view hidden">
|
||||
<div class="section">
|
||||
<h2>🧠 Brains <span class="count" id="brains-count">—</span></h2>
|
||||
<h2>🧠 Brains <span class="count" id="brains-total">—</span></h2>
|
||||
<div class="grid" id="brains-grid"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DASHBOARDS VIEW -->
|
||||
<!-- DASHBOARDS -->
|
||||
<div id="view-dashboards" class="view hidden">
|
||||
<div class="section">
|
||||
<h2>📊 Tous les dashboards <span class="count" id="dashboards-count">—</span></h2>
|
||||
<div class="tabs" id="dashboard-tabs"></div>
|
||||
<input type="text" class="search" id="search-dashboards" placeholder="🔎 Filter dashboards..." oninput="filterDashboards()">
|
||||
<div class="grid" id="dashboards-grid"></div>
|
||||
<h2>📊 92 dashboards</h2>
|
||||
<p style="color:var(--muted);font-size:12px">Voir <a href="/dashboards-hub.html" style="color:var(--accent)">Dashboards Hub</a> pour liste complète catégorisée</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<span>WEVIA Unified Hub · <span id="ts">—</span></span>
|
||||
</div>
|
||||
<div class="footer"><span>WEVIA Unified Hub v2 · <span id="ts">—</span></span></div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let DATA = null;
|
||||
let currentAgentTab = 'all';
|
||||
let currentIntentTab = 'all';
|
||||
let currentDashTab = 'all';
|
||||
let agentsList = [];
|
||||
let intentsList = [];
|
||||
let curAgTab = 'all', curIntTab = 'all';
|
||||
|
||||
function fmt(n){return (n||0).toLocaleString('fr-FR');}
|
||||
function esc(s){return String(s||'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));}
|
||||
|
||||
async function loadAll(){
|
||||
async function loadData(){
|
||||
try{
|
||||
const r = await fetch('/api/wevia-unified-api.php?action=all&t='+Date.now(),{cache:'no-store'});
|
||||
DATA = await r.json();
|
||||
const r1 = await fetch('/api/wevia-unified-api.php?action=summary&t='+Date.now(),{cache:'no-store'});
|
||||
DATA = await r1.json();
|
||||
document.getElementById('ts').textContent = (DATA.ts||'').replace('T',' ').split('+')[0];
|
||||
renderSummary();
|
||||
|
||||
const s = DATA.summary || {};
|
||||
const ag = s.agents || {};
|
||||
const int = s.intents || {};
|
||||
const sk = s.skills || {};
|
||||
|
||||
// KPIs
|
||||
document.getElementById('kpis').innerHTML =
|
||||
'<div class="kpi agents" onclick="show(\'agents\')"><div class="l">Agents UNIQUES</div><div class="v">'+fmt(ag.UNIQUE_DEDUP)+'</div><div class="s">'+fmt(ag.total_with_overlaps)+' avec overlaps</div></div>'+
|
||||
'<div class="kpi intents" onclick="show(\'intents\')"><div class="l">Intents</div><div class="v">'+fmt(int.php_files)+'</div><div class="s">'+fmt(int.arena_declared)+' declared arena</div></div>'+
|
||||
'<div class="kpi skills" onclick="show(\'skills\')"><div class="l">Skills TOTAL</div><div class="v">'+fmt(sk.TOTAL_ALL_SOURCES)+'</div><div class="s">5 sources</div></div>'+
|
||||
'<div class="kpi brains" onclick="show(\'brains\')"><div class="l">Brains</div><div class="v">'+fmt(s.brains?.count)+'</div><div class="s">orchestration</div></div>'+
|
||||
'<div class="kpi dashboards" onclick="show(\'dashboards\')"><div class="l">Dashboards</div><div class="v">'+fmt(s.dashboards)+'</div><div class="s">HTML pages</div></div>'+
|
||||
'<div class="kpi apis"><div class="l">APIs PHP</div><div class="v">'+fmt(s.apis)+'</div><div class="s">/api/*.php</div></div>';
|
||||
|
||||
// Summary view
|
||||
document.getElementById('dedup-count').textContent = fmt(ag.UNIQUE_DEDUP);
|
||||
document.getElementById('overlap-count').textContent = fmt(ag.total_with_overlaps);
|
||||
|
||||
// Agent sources grid
|
||||
const srcMap = {
|
||||
agent_avatars_v2: '🎨 Avatars v2',
|
||||
agent_avatars_v1: '🎨 Avatars v1 (legacy)',
|
||||
wevia_agents_registry: '📋 Registry categorized',
|
||||
paperclip_agility_v71: '🏃 Paperclip V71 Agility',
|
||||
api_agent_files: '📁 API agent files',
|
||||
agent_stubs: '⚙️ Agent stubs PHP',
|
||||
claude_subagents: '🤖 Claude sub-agents',
|
||||
qdrant_agents_vectorized: '🧠 Qdrant vectorized',
|
||||
paperclip_opt_catalog: '📚 Paperclip /opt catalog',
|
||||
};
|
||||
document.getElementById('agent-sources').innerHTML = Object.entries(ag.per_source||{}).map(([k,v]) =>
|
||||
'<div class="source-box"><div class="n">'+esc(srcMap[k]||k)+'</div><div class="c">'+fmt(v)+'</div></div>'
|
||||
).join('');
|
||||
|
||||
// Intents summary
|
||||
const byStatus = int.by_status||{};
|
||||
const byDomain = int.by_domain||{};
|
||||
let intHtml = '<h3 style="font-size:12px;color:var(--purple);margin-bottom:8px">Par status</h3>';
|
||||
Object.entries(byStatus).forEach(([k,v]) => {
|
||||
const cls = k==='EXECUTED'?'green':k.includes('PENDING')?'amber':'blue';
|
||||
intHtml += '<div class="legend-row"><span class="badge '+cls+'">'+esc(k)+'</span><span class="b">'+fmt(v)+'</span></div>';
|
||||
});
|
||||
intHtml += '<h3 style="font-size:12px;color:var(--purple);margin:14px 0 8px 0">Par domaine (top 8)</h3>';
|
||||
Object.entries(byDomain).sort((a,b)=>b[1]-a[1]).slice(0,8).forEach(([k,v]) => {
|
||||
intHtml += '<div class="legend-row"><span>'+esc(k)+'</span><span class="b">'+fmt(v)+'</span></div>';
|
||||
});
|
||||
document.getElementById('intents-summary').innerHTML = intHtml;
|
||||
|
||||
// Skills summary
|
||||
document.getElementById('skills-summary').innerHTML =
|
||||
'<div class="sources-grid">'+
|
||||
'<div class="source-box"><div class="n">Disk dossiers</div><div class="c">'+fmt(sk.disk_dirs)+'</div></div>'+
|
||||
'<div class="source-box"><div class="n">Qdrant vectorisés</div><div class="c">'+fmt(sk.qdrant_vectorized)+'</div></div>'+
|
||||
'<div class="source-box"><div class="n">Tools registry</div><div class="c">'+fmt(sk.tools_registry)+'</div></div>'+
|
||||
'<div class="source-box"><div class="n">Arena declared</div><div class="c">'+fmt(sk.arena_declared)+'</div></div>'+
|
||||
'<div class="source-box"><div class="n">Doctrines</div><div class="c">'+fmt(sk.doctrines)+'</div></div>'+
|
||||
'</div>'+
|
||||
'<p style="color:var(--muted);font-size:12px;margin-top:10px">→ <a href="/skills/" style="color:var(--accent)">Skills Explorer complet</a> avec search Qdrant interactif</p>';
|
||||
}catch(e){console.error(e);}
|
||||
|
||||
// Load agents
|
||||
try{
|
||||
const r2 = await fetch('/api/wevia-unified-api.php?action=agents&t='+Date.now(),{cache:'no-store'});
|
||||
const d2 = await r2.json();
|
||||
agentsList = d2.agents || [];
|
||||
renderAgents();
|
||||
renderIntents();
|
||||
renderSkills();
|
||||
renderBrains();
|
||||
renderDashboards();
|
||||
}catch(e){ console.error(e); }
|
||||
}
|
||||
|
||||
function renderSummary(){
|
||||
const s = DATA.summary || {};
|
||||
|
||||
document.getElementById('kpis-row').innerHTML =
|
||||
'<div class="kpi agents" onclick="show(\'agents\')"><div class="l">Agents</div><div class="v">'+fmt(s.agents_total)+'</div><div class="s">3 sources</div></div>'+
|
||||
'<div class="kpi intents" onclick="show(\'intents\')"><div class="l">Intents</div><div class="v">'+fmt(s.intents_total)+'</div><div class="s">status+domain</div></div>'+
|
||||
'<div class="kpi skills" onclick="show(\'skills\')"><div class="l">Skills</div><div class="v">'+fmt(s.skills_total)+'</div><div class="s">5 sources</div></div>'+
|
||||
'<div class="kpi brains" onclick="show(\'brains\')"><div class="l">Brains</div><div class="v">'+fmt(s.brains_total)+'</div><div class="s">orchestration</div></div>'+
|
||||
'<div class="kpi dashboards" onclick="show(\'dashboards\')"><div class="l">Dashboards</div><div class="v">'+fmt(s.dashboards_total)+'</div><div class="s">HTML pages</div></div>'+
|
||||
'<div class="kpi apis"><div class="l">APIs PHP</div><div class="v">'+fmt(s.apis_php_total)+'</div><div class="s">/api/*.php</div></div>';
|
||||
|
||||
// Agents breakdown
|
||||
document.getElementById('agents-breakdown').innerHTML = Object.entries(s.agents_by_source||{}).map(([k,v]) =>
|
||||
'<div class="legend-row"><span>'+esc(k)+'</span><span class="b">'+fmt(v)+'</span></div>').join('');
|
||||
|
||||
// Intents status
|
||||
document.getElementById('intents-status').innerHTML = Object.entries(s.intents_by_status||{}).map(([k,v]) => {
|
||||
const cls = k==='EXECUTED'?'green':k==='PENDING_APPROVAL'?'blue':k==='LIVE'?'purple':'amber';
|
||||
return '<div class="legend-row"><span><span class="badge '+cls+'">'+esc(k)+'</span></span><span class="b">'+fmt(v)+'</span></div>';
|
||||
}).join('');
|
||||
|
||||
// Intents domains (top 10)
|
||||
const doms = Object.entries(s.intents_by_domain||{}).sort((a,b)=>b[1]-a[1]).slice(0,10);
|
||||
document.getElementById('intents-domains').innerHTML = doms.map(([k,v]) =>
|
||||
'<div class="legend-row"><span>'+esc(k)+'</span><span class="b">'+fmt(v)+'</span></div>').join('');
|
||||
|
||||
// Dashboards cats
|
||||
document.getElementById('dashboards-cats').innerHTML = Object.entries(s.dashboards_by_category||{}).map(([k,v]) =>
|
||||
'<div class="legend-row"><span>'+esc(k)+'</span><span class="b">'+fmt(v)+'</span></div>').join('');
|
||||
}catch(e){console.error(e);}
|
||||
}
|
||||
|
||||
function renderAgents(){
|
||||
const a = DATA.agents || [];
|
||||
document.getElementById('agents-count').textContent = a.length;
|
||||
// Tab counts
|
||||
document.getElementById('a-all').textContent = a.length;
|
||||
const bySource = {};
|
||||
a.forEach(x => bySource[x.source] = (bySource[x.source]||0)+1);
|
||||
Object.keys(bySource).forEach(k => { const el = document.getElementById('a-'+k); if (el) el.textContent = bySource[k]; });
|
||||
document.getElementById('ag-total').textContent = fmt(agentsList.length);
|
||||
// Tabs by sources_count (1 source = unique, 2+ = multi-ref)
|
||||
const bySources = {};
|
||||
agentsList.forEach(a => {
|
||||
const k = a.sources_count >= 3 ? '3+refs' : a.sources_count + 'ref';
|
||||
bySources[k] = (bySources[k]||0)+1;
|
||||
});
|
||||
let tabsHtml = '<div class="tab active" onclick="tabAg(\'all\',event)">Tous<span class="count">'+agentsList.length+'</span></div>';
|
||||
['3+refs','2ref','1ref'].forEach(k => {
|
||||
if (bySources[k]) tabsHtml += '<div class="tab" onclick="tabAg(\''+k+'\',event)">'+esc(k)+'<span class="count">'+bySources[k]+'</span></div>';
|
||||
});
|
||||
document.getElementById('ag-tabs').innerHTML = tabsHtml;
|
||||
filterAgents();
|
||||
}
|
||||
|
||||
function tabAgents(cat, ev){
|
||||
currentAgentTab = cat;
|
||||
document.querySelectorAll('#view-agents .tab').forEach(t => t.classList.remove('active'));
|
||||
if (ev) ev.target.classList.add('active');
|
||||
function tabAg(cat,ev){
|
||||
curAgTab = cat;
|
||||
document.querySelectorAll('#ag-tabs .tab').forEach(t => t.classList.remove('active'));
|
||||
if (ev) ev.target.closest('.tab').classList.add('active');
|
||||
filterAgents();
|
||||
}
|
||||
|
||||
function filterAgents(){
|
||||
const q = (document.getElementById('search-agents').value || '').toLowerCase();
|
||||
const a = (DATA.agents || []).filter(x => {
|
||||
if (currentAgentTab !== 'all' && x.source !== currentAgentTab) return false;
|
||||
if (q && !x.name.toLowerCase().includes(q) && !(x.desc||'').toLowerCase().includes(q)) return false;
|
||||
return true;
|
||||
});
|
||||
const cls = {api_files:'amber', agent_stubs:'purple', claude_code_subagents:'green'};
|
||||
document.getElementById('agents-grid').innerHTML = a.map(x =>
|
||||
'<div class="card"><div class="name">'+esc(x.name)+'</div>'+
|
||||
'<div class="meta"><span class="badge '+(cls[x.source]||'blue')+'">'+esc(x.source)+'</span>'+
|
||||
(x.category ? '<span class="badge blue">'+esc(x.category)+'</span>' : '')+'</div>'+
|
||||
(x.desc ? '<div class="desc">'+esc(x.desc)+'</div>' : '')+'</div>'
|
||||
).join('') || '<div style="color:var(--muted);padding:20px;text-align:center">Aucun résultat</div>';
|
||||
}
|
||||
|
||||
function renderIntents(){
|
||||
const items = DATA.intents || [];
|
||||
document.getElementById('intents-count').textContent = items.length;
|
||||
// Tabs by status
|
||||
const byStatus = {};
|
||||
items.forEach(x => byStatus[x.status] = (byStatus[x.status]||0)+1);
|
||||
let tabsHtml = '<div class="tab active" onclick="tabIntent(\'all\',event)">Tous<span class="count">'+items.length+'</span></div>';
|
||||
Object.entries(byStatus).forEach(([k,v]) => {
|
||||
tabsHtml += '<div class="tab" onclick="tabIntent(\''+k+'\',event)">'+esc(k)+'<span class="count">'+v+'</span></div>';
|
||||
});
|
||||
document.getElementById('intent-tabs').innerHTML = tabsHtml;
|
||||
filterIntents();
|
||||
}
|
||||
|
||||
function tabIntent(cat, ev){
|
||||
currentIntentTab = cat;
|
||||
document.querySelectorAll('#view-intents .tab').forEach(t => t.classList.remove('active'));
|
||||
if (ev) ev.target.closest('.tab').classList.add('active');
|
||||
filterIntents();
|
||||
}
|
||||
|
||||
function filterIntents(){
|
||||
const q = (document.getElementById('search-intents').value || '').toLowerCase();
|
||||
const items = (DATA.intents || []).filter(x => {
|
||||
if (currentIntentTab !== 'all' && x.status !== currentIntentTab) return false;
|
||||
if (q) {
|
||||
const hay = (x.name+' '+(x.triggers||[]).join(' ')+' '+x.domain).toLowerCase();
|
||||
if (!hay.includes(q)) return false;
|
||||
const q = (document.getElementById('search-agents').value||'').toLowerCase();
|
||||
const filtered = agentsList.filter(a => {
|
||||
if (curAgTab !== 'all') {
|
||||
const k = a.sources_count >= 3 ? '3+refs' : a.sources_count + 'ref';
|
||||
if (k !== curAgTab) return false;
|
||||
}
|
||||
if (q && !a.name.toLowerCase().includes(q) && !(a.role||'').toLowerCase().includes(q)) return false;
|
||||
return true;
|
||||
}).slice(0, 300);
|
||||
document.getElementById('intents-grid').innerHTML = items.map(x => {
|
||||
const statCls = x.status==='EXECUTED'?'green':x.status==='PENDING_APPROVAL'?'blue':'amber';
|
||||
const trigs = (x.triggers||[]).slice(0,2).map(t => esc(t)).join(' · ');
|
||||
return '<div class="card"><div class="name">'+esc(x.name)+'</div>'+
|
||||
'<div class="meta"><span class="badge '+statCls+'">'+esc(x.status)+'</span>'+
|
||||
'<span class="badge purple">'+esc(x.domain)+'</span></div>'+
|
||||
(trigs ? '<div class="desc" style="font-family:monospace;font-size:10.5px">→ '+trigs+'</div>' : '')+
|
||||
'</div>';
|
||||
}).slice(0, 400);
|
||||
document.getElementById('agents-grid').innerHTML = filtered.map(a => {
|
||||
const icon = a.icon || '🤖';
|
||||
const pills = (a.sources||[]).map(s => '<span class="pill">'+esc(s.replace('agent_','').replace('_',' '))+'</span>').join('');
|
||||
return '<div class="card"><div class="name"><span class="icon">'+esc(icon)+'</span>'+esc(a.name)+'</div>'+
|
||||
(a.role ? '<div class="desc">'+esc(a.role)+'</div>' : '')+
|
||||
'<div class="sources-pills">'+pills+'</div></div>';
|
||||
}).join('') || '<div style="color:var(--muted);padding:20px;text-align:center">Aucun résultat</div>';
|
||||
}
|
||||
|
||||
function renderSkills(){
|
||||
const sk = DATA.skills || {};
|
||||
const disk = sk.disk || [];
|
||||
const total = disk.length + (sk.qdrant_count||0) + (sk.tools_count||0) + (sk.arena_count||0);
|
||||
document.getElementById('skills-count').textContent = fmt(total);
|
||||
|
||||
document.getElementById('skills-content').innerHTML =
|
||||
'<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:10px;margin-bottom:16px">'+
|
||||
'<div class="card" style="text-align:center"><div class="name">'+fmt(disk.length)+'</div><div class="desc">Disque /skills/</div></div>'+
|
||||
'<div class="card" style="text-align:center"><div class="name">'+fmt(sk.qdrant_count)+'</div><div class="desc">Qdrant vectorisés</div></div>'+
|
||||
'<div class="card" style="text-align:center"><div class="name">'+fmt(sk.tools_count)+'</div><div class="desc">Tools registry</div></div>'+
|
||||
'<div class="card" style="text-align:center"><div class="name">'+fmt(sk.arena_count)+'</div><div class="desc">Arena declared</div></div>'+
|
||||
'</div>'+
|
||||
'<h3 style="font-size:13px;color:var(--purple);margin-bottom:10px">Disque · '+disk.length+' dossiers</h3>'+
|
||||
'<div class="grid">' + disk.map(x => {
|
||||
const badge = x.has_skill_md ? '<span class="badge green">SKILL.md</span>' : '<span class="badge amber">catalog '+x.sub_count+' sub</span>';
|
||||
return '<a class="card-link" href="'+esc(x.path)+'"><div class="card"><div class="name">📦 '+esc(x.name)+'</div><div class="meta">'+badge+'</div></div></a>';
|
||||
}).join('') + '</div>'+
|
||||
'<div style="margin-top:14px;text-align:center"><a href="/skills/" style="display:inline-block;padding:10px 16px;background:var(--accent);color:#000;text-decoration:none;border-radius:6px;font-size:12px;font-weight:700">🎯 Skills Explorer complet (Qdrant search)</a></div>';
|
||||
}
|
||||
|
||||
function renderBrains(){
|
||||
const b = DATA.brains || [];
|
||||
document.getElementById('brains-count').textContent = b.length;
|
||||
document.getElementById('brains-grid').innerHTML = b.map(x =>
|
||||
'<a class="card-link" href="'+esc(x.path)+'" target="_blank"><div class="card"><div class="name">🧠 '+esc(x.name)+'</div>'+
|
||||
'<div class="meta"><span class="badge blue">'+esc(x.type)+'</span><span>'+fmt(x.size)+' bytes</span></div>'+
|
||||
'<div class="desc">'+esc(x.purpose)+'</div></div></a>'
|
||||
).join('');
|
||||
}
|
||||
|
||||
function renderDashboards(){
|
||||
const d = DATA.dashboards || [];
|
||||
document.getElementById('dashboards-count').textContent = d.length;
|
||||
const byCat = {};
|
||||
d.forEach(x => byCat[x.category] = (byCat[x.category]||0)+1);
|
||||
let tabsHtml = '<div class="tab active" onclick="tabDash(\'all\',event)">Tous<span class="count">'+d.length+'</span></div>';
|
||||
Object.entries(byCat).sort((a,b)=>b[1]-a[1]).forEach(([k,v]) => {
|
||||
tabsHtml += '<div class="tab" onclick="tabDash(\''+k+'\',event)">'+esc(k)+'<span class="count">'+v+'</span></div>';
|
||||
});
|
||||
document.getElementById('dashboard-tabs').innerHTML = tabsHtml;
|
||||
filterDashboards();
|
||||
}
|
||||
|
||||
function tabDash(cat, ev){
|
||||
currentDashTab = cat;
|
||||
document.querySelectorAll('#view-dashboards .tab').forEach(t => t.classList.remove('active'));
|
||||
if (ev) ev.target.closest('.tab').classList.add('active');
|
||||
filterDashboards();
|
||||
}
|
||||
|
||||
function filterDashboards(){
|
||||
const q = (document.getElementById('search-dashboards').value || '').toLowerCase();
|
||||
const items = (DATA.dashboards || []).filter(x => {
|
||||
if (currentDashTab !== 'all' && x.category !== currentDashTab) return false;
|
||||
if (q && !x.name.toLowerCase().includes(q)) return false;
|
||||
return true;
|
||||
});
|
||||
const catCls = {agents:'purple', autonomy:'green', bvs:'blue', chat:'amber', dashboard:'blue', director:'red', hub:'green', l99:'purple'};
|
||||
document.getElementById('dashboards-grid').innerHTML = items.map(x =>
|
||||
'<a class="card-link" href="'+esc(x.url)+'" target="_blank"><div class="card"><div class="name">📄 '+esc(x.name)+'</div>'+
|
||||
'<div class="meta"><span class="badge '+(catCls[x.category]||'blue')+'">'+esc(x.category)+'</span>'+
|
||||
'<span>'+fmt(Math.round(x.size/1024))+' KB</span></div></div></a>'
|
||||
).join('') || '<div style="color:var(--muted);padding:20px;text-align:center">Aucun résultat</div>';
|
||||
}
|
||||
|
||||
function show(view, ev){
|
||||
if (ev) ev.preventDefault();
|
||||
document.querySelectorAll('.view').forEach(v => v.classList.add('hidden'));
|
||||
document.getElementById('view-'+view)?.classList.remove('hidden');
|
||||
document.querySelectorAll('.sidebar a').forEach(a => a.classList.remove('active'));
|
||||
if (ev) ev.target.classList.add('active');
|
||||
else document.querySelector('.sidebar a[href="#'+view+'"]')?.classList.add('active');
|
||||
}
|
||||
|
||||
loadAll();
|
||||
async function loadIntents(){
|
||||
if (intentsList.length) return;
|
||||
try{
|
||||
const r = await fetch('/api/wevia-unified-api.php?action=intents');
|
||||
const d = await r.json();
|
||||
intentsList = d.intents_list || [];
|
||||
renderIntents();
|
||||
}catch(e){console.error(e);}
|
||||
}
|
||||
|
||||
function renderIntents(){
|
||||
document.getElementById('int-total').textContent = fmt(intentsList.length);
|
||||
const byStatus = {};
|
||||
intentsList.forEach(x => byStatus[x.status] = (byStatus[x.status]||0)+1);
|
||||
let tabsHtml = '<div class="tab active" onclick="tabInt(\'all\',event)">Tous<span class="count">'+intentsList.length+'</span></div>';
|
||||
Object.entries(byStatus).forEach(([k,v]) => {
|
||||
tabsHtml += '<div class="tab" onclick="tabInt(\''+k+'\',event)">'+esc(k)+'<span class="count">'+v+'</span></div>';
|
||||
});
|
||||
document.getElementById('int-tabs').innerHTML = tabsHtml;
|
||||
filterIntents();
|
||||
}
|
||||
|
||||
function tabInt(cat,ev){
|
||||
curIntTab = cat;
|
||||
document.querySelectorAll('#int-tabs .tab').forEach(t => t.classList.remove('active'));
|
||||
if (ev) ev.target.closest('.tab').classList.add('active');
|
||||
filterIntents();
|
||||
}
|
||||
|
||||
function filterIntents(){
|
||||
const q = (document.getElementById('search-intents').value||'').toLowerCase();
|
||||
const filtered = intentsList.filter(x => {
|
||||
if (curIntTab !== 'all' && x.status !== curIntTab) return false;
|
||||
if (q) { const hay = (x.name+' '+(x.triggers||[]).join(' ')+' '+x.domain).toLowerCase(); if (!hay.includes(q)) return false; }
|
||||
return true;
|
||||
}).slice(0, 400);
|
||||
document.getElementById('intents-grid').innerHTML = filtered.map(x => {
|
||||
const cls = x.status==='EXECUTED'?'green':x.status.includes('PENDING')?'amber':'blue';
|
||||
const trigs = (x.triggers||[]).slice(0,2).map(t => esc(t)).join(' · ');
|
||||
return '<div class="card"><div class="name">🎯 '+esc(x.name)+'</div>'+
|
||||
'<div class="meta"><span class="badge '+cls+'">'+esc(x.status)+'</span><span class="badge purple">'+esc(x.domain)+'</span></div>'+
|
||||
(trigs ? '<div class="desc" style="font-family:monospace;font-size:10.5px">→ '+trigs+'</div>' : '')+
|
||||
'</div>';
|
||||
}).join('') || '<div style="color:var(--muted);padding:20px;text-align:center">Aucun résultat</div>';
|
||||
}
|
||||
|
||||
// Hook on show() to lazy-load intents
|
||||
const origShow = show;
|
||||
show = function(v, e) { origShow(v, e); if (v==='intents') loadIntents(); };
|
||||
|
||||
loadData();
|
||||
</script>
|
||||
</body></html>
|
||||
|
||||
Reference in New Issue
Block a user