async function renderLearningAnalytics() {
const content = document.getElementById('pageContent');
content.innerHTML = `
Skill Scores
Score Trends
Skill Details
`;
try {
const [skillData, trendData] = await Promise.all([api.getSkillAnalytics(), api.getTrendAnalytics()]);
const skills = skillData.skills || [];
const trends = trendData.trends || {};
const grid = document.getElementById('skillScoresGrid');
if (grid) {
if (skills.length === 0) {
grid.innerHTML = `๐
No skill data yet
Skill evaluations will appear here as they accumulate
`;
} else {
const topSkills = skills.sort((a, b) => (b.score || 0) - (a.score || 0)).slice(0, 6);
grid.innerHTML = topSkills.map(s => {
const pct = Math.round((s.score || 0));
return `
${escapeHtml(s.name)}
${s.evals || 0} evaluations
`;
}).join('');
}
}
const trendSection = document.getElementById('trendCharts');
if (trendSection) {
const trendEntries = Object.entries(trends).slice(0, 4);
if (trendEntries.length === 0) {
trendSection.innerHTML = `๐
No trend data
Trends will appear after multiple evaluations
`;
} else {
trendSection.innerHTML = trendEntries.map(([name, history]) => {
const vals = (history || []).slice(-10);
const maxVal = Math.max(...vals, 0.01);
const chartHeight = 120;
return `
${escapeHtml(name)}
${vals.map((v, i) => {
const h = (v / maxVal) * (chartHeight - 25);
return `
`;
}).join('')}
${vals.length} data points
Avg: ${(vals.reduce((a, b) => a + b, 0) / vals.length).toFixed(0)}%
`;
}).join('');
}
}
const details = document.getElementById('skillDetailsList');
if (details) {
if (skills.length === 0) {
details.innerHTML = '';
} else {
details.innerHTML = skills.map(s => {
const pct = Math.round((s.score || 0));
return `
${escapeHtml(s.name)}
${s.evals || 0} evaluations ยท Best: ${(s.best || 0).toFixed(0)}%
${pct}%
`;
}).join('');
}
}
} catch (err) {
showToast('Failed to load analytics: ' + err.message, 'error');
}
}