Aims_nsight / static /js /dashboard.js
cyberai-1
Clean history without old model artifacts
5af9683
Raw
History Blame Contribute Delete
6.77 kB
/* ── AIMS Insight – Dashboard JS ── */
let donutChart = null;
let barChart = null;
/* ───────────────────────────────
FILTER RESET
─────────────────────────────── */
function resetFilters() {
document.getElementById('startDate').value = '';
document.getElementById('endDate').value = '';
document.getElementById('filterTopic').value = 'all';
document.getElementById('filterSentiment').value = 'all';
setTimeout(() => {
loadData();
}, 50);
}
/* ───────────────────────────────
LOAD DATA FROM API
─────────────────────────────── */
async function loadData() {
const params = new URLSearchParams({
start_date: document.getElementById('startDate').value,
end_date: document.getElementById('endDate').value,
topic: document.getElementById('filterTopic').value,
sentiment: document.getElementById('filterSentiment').value
});
try {
const res = await fetch(`/api/dashboard-data?${params}`);
const data = await res.json();
updateKPIs(data);
updateDonut(data.sentiment);
updateBar(data.topics);
updateTable(data.recent);
} catch (e) {
console.error('Dashboard load error:', e);
}
}
/* ───────────────────────────────
KPI UPDATE
─────────────────────────────── */
function updateKPIs(data) {
document.getElementById('kpiTotal').textContent = data.total;
document.getElementById('kpiSatisfaction').textContent = data.satisfaction + '%';
document.getElementById('kpiNeutral').textContent = data.sentiment.Neutral;
document.getElementById('kpiNegative').textContent = data.sentiment.Negative;
}
/* ───────────────────────────────
DONUT CHART
─────────────────────────────── */
function updateDonut(sentiment) {
const ctx = document.getElementById('donutChart').getContext('2d');
const dataArr = [
sentiment.Positive,
sentiment.Neutral,
sentiment.Negative
];
if (donutChart) donutChart.destroy();
donutChart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ['Positive', 'Neutral', 'Negative'],
datasets: [{
data: dataArr,
backgroundColor: ['#28a745', '#6c757d', '#dc3545'],
borderWidth: 3,
borderColor: '#fff',
hoverOffset: 6
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
cutout: '65%',
plugins: {
legend: {
position: 'bottom',
labels: { padding: 14, font: { size: 12 } }
}
}
}
});
}
/* ───────────────────────────────
BAR CHART
─────────────────────────────── */
function updateBar(topics) {
const ctx = document.getElementById('barChart').getContext('2d');
const sorted = Object.entries(topics).sort((a, b) => b[1] - a[1]);
const labels = sorted.map(([k]) => k);
const values = sorted.map(([, v]) => v);
const colors = values.map((_, i) => {
const opacity = 1 - (i / Math.max(values.length, 1)) * 0.5;
return `rgba(128,0,0,${opacity})`;
});
if (barChart) barChart.destroy();
barChart = new Chart(ctx, {
type: 'bar',
data: {
labels,
datasets: [{
label: 'Feedbacks',
data: values,
backgroundColor: colors,
borderRadius: 6,
borderSkipped: false
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false }
},
scales: {
y: {
beginAtZero: true,
ticks: { stepSize: 1, font: { size: 11 } },
grid: { color: 'rgba(0,0,0,.04)' }
},
x: {
ticks: { font: { size: 11 } },
grid: { display: false }
}
}
}
});
}
/* ───────────────────────────────
TABLE UPDATE
─────────────────────────────── */
function updateTable(rows) {
const tbody = document.getElementById('recentTable');
if (!rows || rows.length === 0) {
tbody.innerHTML = `
<tr>
<td colspan="4" style="text-align:center; color:#aaa; padding:30px">
No data available for these filters.
</td>
</tr>`;
return;
}
tbody.innerHTML = rows.map(r => {
const label =
r.sentiment_label === 'Positive' ? 'badge-positive'
: r.sentiment_label === 'Negative' ? 'badge-negative'
: 'badge-neutral';
const labelFR =
r.sentiment_label === 'Positive' ? 'Positive'
: r.sentiment_label === 'Negative' ? 'Negative'
: 'Neutral';
const date = r.created_at ? r.created_at.slice(0, 16) : 'β€”';
const comment = (r.comment_text || '').length > 80
? r.comment_text.slice(0, 80) + '…'
: r.comment_text;
return `
<tr>
<td style="color:#888; font-size:12px; white-space:nowrap">${date}</td>
<td>
<span style="background:#f3f0ef; padding:3px 9px;
border-radius:6px; font-size:12px; font-weight:600; color:#555">
${r.topic}
</span>
</td>
<td class="comment-cell">${comment}</td>
<td><span class="badge ${label}">${labelFR}</span></td>
</tr>`;
}).join('');
}
/* ───────────────────────────────
USER DROPDOWN MENU (PROFILE)
─────────────────────────────── */
function toggleUserMenu() {
const menu = document.getElementById("userMenu");
if (menu) menu.classList.toggle("show");
}
// close dropdown when clicking outside
document.addEventListener("click", function (e) {
const badge = document.querySelector(".user-badge");
const menu = document.getElementById("userMenu");
if (!badge || !menu) return;
if (!badge.contains(e.target)) {
menu.classList.remove("show");
}
});
/* ───────────────────────────────
INIT
─────────────────────────────── */
document.addEventListener('DOMContentLoaded', loadData);