Some checks failed
WEVAL NonReg / nonreg (push) Has been cancelled
NEW: - /learning-dashboard.html (8.2KB) · UX premium dashboard - /api/learning-analytics.php (3.3KB) · PG aggregator - dashboards-index enriched · section Learning & Analytics METRICS LIVE (24h window): - Hero: total_learned, success_pct, bots_count, conversations, sessions, avg_ms - Per chatbot: total queries, success rate, progress bar colored - Intents distribution: count, success rate per intent - Latest 10 learnings: time, chatbot, intent, outcome badge AUTO-REFRESH 15s · fetch /api/learning-analytics.php Color-coded cards: excellent (>=90pct green), good (>=70 blue), ok (>=50 orange), low (<50 red) Data sources: - ai_learning_log (table v20) avec experience jsonb + outcome_success - wevia_conversations (table v18) conversations count + sessions distinct Validation LIVE: - HTTP 200 on /learning-dashboard.html (9870b served) - API returns structured JSON in <200ms - 6 rows learned · 83pct success · 2 bots used (wevia-master 100pct, blade-ai 0pct) - 10 conversations · 9 sessions Doctrine: - Point verite unique (1 dashboard = tout apprentissage) - UX PREMIUM (gradients, badges colorés, progress bars, refresh pulse) - RELIER toutes pages (dashboards-index -> learning-dashboard) - Zero regression - Additif pur
158 lines
8.0 KiB
HTML
158 lines
8.0 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="fr"><head><meta charset="UTF-8"><title>🧠 Learning Analytics · WEVIA</title>
|
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
<style>
|
|
*{margin:0;padding:0;box-sizing:border-box;font-family:-apple-system,'Segoe UI',sans-serif}
|
|
body{background:linear-gradient(135deg,#0b0d15 0%,#1a1f3a 100%);color:#e2e8f0;min-height:100vh;padding:20px}
|
|
.container{max-width:1400px;margin:0 auto}
|
|
h1{font-family:'Orbitron',sans-serif;font-weight:900;
|
|
background:linear-gradient(135deg,#10b981,#06b6d4,#8b5cf6);
|
|
-webkit-background-clip:text;-webkit-text-fill-color:transparent;
|
|
font-size:2rem;margin-bottom:6px;letter-spacing:1px;text-transform:uppercase}
|
|
.subtitle{color:#94a3b8;margin-bottom:20px;font-size:0.9rem}
|
|
.hero-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:14px;margin-bottom:24px}
|
|
.stat-card{background:linear-gradient(135deg,rgba(16,185,129,0.08),rgba(6,182,212,0.08));
|
|
border:1px solid rgba(16,185,129,0.2);border-radius:14px;padding:16px;
|
|
backdrop-filter:blur(12px);text-align:center}
|
|
.stat-val{font-family:'Orbitron',sans-serif;font-size:2.2rem;font-weight:900;line-height:1}
|
|
.stat-val.green{color:#22c55e}
|
|
.stat-val.blue{color:#06b6d4}
|
|
.stat-val.purple{color:#a855f7}
|
|
.stat-val.pink{color:#ec4899}
|
|
.stat-val.orange{color:#fb923c}
|
|
.stat-lbl{font-size:0.7rem;color:#64748b;text-transform:uppercase;letter-spacing:1.5px;margin-top:6px}
|
|
.section{background:rgba(15,23,42,0.8);border:1px solid rgba(100,116,139,0.15);
|
|
border-radius:14px;padding:18px;margin-bottom:16px}
|
|
.section h2{color:#06b6d4;font-size:1rem;font-weight:800;margin-bottom:12px;text-transform:uppercase;letter-spacing:1.5px}
|
|
.chatbots-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:10px}
|
|
.bot-card{background:rgba(11,13,21,0.8);border:1px solid rgba(100,116,139,0.15);
|
|
border-left:4px solid #06b6d4;border-radius:8px;padding:12px}
|
|
.bot-card.excellent{border-left-color:#22c55e}
|
|
.bot-card.good{border-left-color:#06b6d4}
|
|
.bot-card.ok{border-left-color:#f59e0b}
|
|
.bot-card.low{border-left-color:#ef4444}
|
|
.bot-name{font-weight:800;color:#e2e8f0;font-size:0.85rem;margin-bottom:6px}
|
|
.bot-stats{font-size:0.7rem;color:#94a3b8;display:flex;justify-content:space-between}
|
|
.bot-stats b{color:#e2e8f0}
|
|
.progress{background:rgba(100,116,139,0.15);border-radius:6px;height:4px;margin-top:6px;overflow:hidden}
|
|
.progress-bar{height:100%;background:linear-gradient(90deg,#22c55e,#06b6d4);transition:width 0.3s}
|
|
table{width:100%;border-collapse:collapse;font-size:0.78rem}
|
|
th{text-align:left;color:#06b6d4;padding:8px;border-bottom:1px solid rgba(100,116,139,0.2);text-transform:uppercase;font-size:0.7rem;letter-spacing:1px}
|
|
td{padding:8px;border-bottom:1px solid rgba(100,116,139,0.08);color:#cbd5e1}
|
|
.badge{display:inline-block;padding:2px 8px;border-radius:10px;font-size:0.68rem;font-weight:700}
|
|
.badge.success{background:rgba(34,197,94,0.2);color:#22c55e}
|
|
.badge.partial{background:rgba(251,146,60,0.2);color:#fb923c}
|
|
.footer{margin-top:30px;padding:14px;text-align:center;color:#64748b;font-size:0.8rem;
|
|
border-top:1px solid rgba(100,116,139,0.15)}
|
|
.footer a{color:#10b981;text-decoration:none;margin:0 8px}
|
|
.refresh-pulse{display:inline-block;width:8px;height:8px;background:#22c55e;border-radius:50%;animation:pulse 2s ease infinite;margin-right:6px;vertical-align:middle}
|
|
@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.3}}
|
|
.loading{text-align:center;color:#64748b;padding:30px;font-size:0.9rem}
|
|
</style></head><body>
|
|
<div class="container">
|
|
<h1>🧠 Learning Analytics · WEVIA</h1>
|
|
<p class="subtitle">
|
|
<span class="refresh-pulse"></span>
|
|
Source: ai_learning_log + wevia_conversations · Auto-refresh 15s · 24h window
|
|
</p>
|
|
|
|
<section class="hero-grid" id="hero">
|
|
<div class="stat-card"><div class="stat-val green" id="stat-learned">—</div><div class="stat-lbl">Learned 24h</div></div>
|
|
<div class="stat-card"><div class="stat-val blue" id="stat-success">—</div><div class="stat-lbl">Success Rate</div></div>
|
|
<div class="stat-card"><div class="stat-val purple" id="stat-bots">—</div><div class="stat-lbl">Chatbots Actifs</div></div>
|
|
<div class="stat-card"><div class="stat-val pink" id="stat-conv">—</div><div class="stat-lbl">Conversations</div></div>
|
|
<div class="stat-card"><div class="stat-val orange" id="stat-sessions">—</div><div class="stat-lbl">Sessions</div></div>
|
|
<div class="stat-card"><div class="stat-val green" id="stat-avg-ms">—</div><div class="stat-lbl">Avg Latency</div></div>
|
|
</section>
|
|
|
|
<section class="section">
|
|
<h2>📊 Stats par chatbot (24h)</h2>
|
|
<div id="chatbots-grid" class="chatbots-grid"><div class="loading">Chargement...</div></div>
|
|
</section>
|
|
|
|
<section class="section">
|
|
<h2>🎯 Intents Distribution (24h)</h2>
|
|
<table id="intents-table"><thead><tr><th>Intent</th><th>Count</th><th>Success</th><th>Rate</th></tr></thead>
|
|
<tbody><tr><td colspan="4" class="loading">Chargement...</td></tr></tbody>
|
|
</table>
|
|
</section>
|
|
|
|
<section class="section">
|
|
<h2>🕐 Latest learnings (10)</h2>
|
|
<table id="latest-table"><thead><tr><th>Time</th><th>Chatbot</th><th>Intent</th><th>Outcome</th></tr></thead>
|
|
<tbody><tr><td colspan="4" class="loading">Chargement...</td></tr></tbody>
|
|
</table>
|
|
</section>
|
|
|
|
<div class="footer">
|
|
<a href="/">Home</a> · <a href="/dashboards-index.html">Dashboards</a> · <a href="/wevia-multiagent-dashboard.html">Multi-Agent</a> · <a href="/api/learning-analytics.php" target="_blank">JSON</a>
|
|
<br><br>Opus session v22 · Cross-chatbot learning visualized
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
async function refresh() {
|
|
try {
|
|
const r = await fetch('/api/learning-analytics.php', {cache:'no-store'});
|
|
const d = await r.json();
|
|
|
|
// Hero
|
|
document.getElementById('stat-learned').textContent = d.summary.total_learned || 0;
|
|
document.getElementById('stat-success').textContent = (d.summary.success_pct || 0) + '%';
|
|
document.getElementById('stat-bots').textContent = d.summary.bots_count || 0;
|
|
document.getElementById('stat-conv').textContent = d.summary.conversations || 0;
|
|
document.getElementById('stat-sessions').textContent = d.summary.sessions || 0;
|
|
document.getElementById('stat-avg-ms').textContent = (d.summary.avg_ms || 0) + 'ms';
|
|
|
|
// Chatbots grid
|
|
const grid = document.getElementById('chatbots-grid');
|
|
if (d.chatbots && d.chatbots.length) {
|
|
grid.innerHTML = d.chatbots.map(b => {
|
|
const rate = b.success_pct || 0;
|
|
const cls = rate >= 90 ? 'excellent' : (rate >= 70 ? 'good' : (rate >= 50 ? 'ok' : 'low'));
|
|
return `<div class="bot-card ${cls}">
|
|
<div class="bot-name">${b.chatbot}</div>
|
|
<div class="bot-stats"><span>${b.total} queries</span><b>${rate}%</b></div>
|
|
<div class="progress"><div class="progress-bar" style="width:${rate}%"></div></div>
|
|
</div>`;
|
|
}).join('');
|
|
} else {
|
|
grid.innerHTML = '<div class="loading">Pas de data 24h</div>';
|
|
}
|
|
|
|
// Intents
|
|
const itbody = document.querySelector('#intents-table tbody');
|
|
if (d.intents && d.intents.length) {
|
|
itbody.innerHTML = d.intents.map(i => `<tr>
|
|
<td><b>${i.intent || '?'}</b></td>
|
|
<td>${i.count}</td>
|
|
<td>${i.success}</td>
|
|
<td>${i.rate}%</td>
|
|
</tr>`).join('');
|
|
} else {
|
|
itbody.innerHTML = '<tr><td colspan="4" class="loading">Pas de data</td></tr>';
|
|
}
|
|
|
|
// Latest
|
|
const lbody = document.querySelector('#latest-table tbody');
|
|
if (d.latest && d.latest.length) {
|
|
lbody.innerHTML = d.latest.map(l => {
|
|
const badge = l.outcome ? '<span class="badge success">success</span>' : '<span class="badge partial">partial</span>';
|
|
const time = (l.learned_at || '').substring(11, 19);
|
|
return `<tr><td>${time}</td><td>${l.chatbot || '?'}</td><td>${l.intent || 'query'}</td><td>${badge}</td></tr>`;
|
|
}).join('');
|
|
} else {
|
|
lbody.innerHTML = '<tr><td colspan="4" class="loading">Pas de data</td></tr>';
|
|
}
|
|
} catch (e) {
|
|
console.error('refresh err', e);
|
|
}
|
|
}
|
|
refresh();
|
|
setInterval(refresh, 15000);
|
|
</script>
|
|
|
|
<!-- WTP_UDOCK_V1 -->
|
|
<script src="/wtp-unified-dock.js" defer></script>
|
|
</body></html>
|