(function () { 'use strict'; const d = window.DBN_DASHBOARD || {}; const api = (d.apiBase || '/api/dashboard') + '/feedback.php'; const $totals = document.getElementById('fbTotals'); const $byTool = document.getElementById('fbByTool'); const $byEngine = document.getElementById('fbByEngine'); const $recent = document.getElementById('fbRecent'); const $refresh = document.getElementById('fbRefresh'); const safe = s => String(s == null ? '' : s).replace(/[&<>"]/g, c => ({ '&':'&','<':'<','>':'>','"':'"' }[c])); function pct(pos, total) { return total > 0 ? Math.round((pos / total) * 100) : 0; } function totalsHtml(t) { return '' + '
' + t.total + '
Total
' + '
' + t.positive + '
Positive
' + '
' + t.negative + '
Negative
' + '
' + pct(t.positive, t.total) + '%
Approval
'; } function rowsHtml(rows, nameKey, withMeta) { const head = '
' + (nameKey === 'tool' ? 'Tool' : 'Engine') + '
Total
Up
Down
' + (withMeta ? 'Last' : 'Approval') + '
'; if (!rows.length) return head + '
No feedback yet.
'; const body = rows.map(r => { const p = pct(r.positive, r.total); const right = withMeta ? '
' + safe(r.last_at || '—') + '
' : '
' + '
' + p + '%
'; return '
' + '
' + safe(r[nameKey]) + '
' + '
' + r.total + '
' + '
' + r.positive + '
' + '
' + r.negative + '
' + right + '
'; }).join(''); return head + body; } function recentHtml(rows) { if (!rows.length) return '
No notes yet.
'; return rows.map(n => { const isPos = n.rating === 'positive'; return '
' + '
' + '' + safe(n.tool) + '' + '' + (isPos ? '\u{1F44D}' : '\u{1F44E}') + '' + (n.engine ? '' + safe(n.engine) + '' : '') + '' + safe(n.created_at || '') + '' + '
' + '
' + safe(n.missed_or_wrong) + '
' + '
'; }).join(''); } function render(data) { $totals.innerHTML = totalsHtml(data.totals || { total: 0, positive: 0, negative: 0 }); $byTool.innerHTML = rowsHtml(data.by_tool || [], 'tool', true); $byEngine.innerHTML = rowsHtml(data.by_engine || [], 'engine', false); $recent.innerHTML = recentHtml(data.recent || []); } async function load() { $totals.innerHTML = '
'; try { const r = await fetch(api, { credentials: 'same-origin' }); const data = await r.json(); if (!data.ok) throw new Error(data.message || 'Could not load feedback'); render(data); } catch (e) { $totals.innerHTML = '
Could not load: ' + safe(e.message) + '
'; $byTool.innerHTML = ''; $byEngine.innerHTML = ''; $recent.innerHTML = ''; } } if ($refresh) $refresh.addEventListener('click', load); load(); })();