(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.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 || '—') + '
'
: '';
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();
})();