(() => {
async function loadStats() {
const res = await fetch("/api/dashboard/stats");
const data = await res.json();
document.getElementById("s-total").textContent =
data.total_conversations;
document.getElementById("s-rate").textContent =
`${data.resolution_rate}%`;
document.getElementById("s-appt").textContent =
data.appointments_booked;
document.getElementById("s-hand").textContent = data.handed_off;
document.getElementById("s-unans").textContent =
data.unanswered_count;
document.getElementById("s-leads").textContent =
data.leads_complete;
const unansBody = document.getElementById("unans-body");
unansBody.innerHTML = "";
(data.recent_unanswered || []).forEach((q) => {
const tr = document.createElement("tr");
tr.innerHTML = `
${q.id} |
#${q.conversation_id} |
${escapeHtml(q.question)} |
${formatWhen(q.created_at)} |
`;
unansBody.appendChild(tr);
});
}
async function loadConversations() {
const res = await fetch("/api/conversations");
const rows = await res.json();
const body = document.getElementById("conv-body");
body.innerHTML = "";
rows.forEach((c) => {
const status = c.handed_off
? 'handoff'
: c.appointment_booked
? 'booked'
: `${c.status}`;
const tr = document.createElement("tr");
tr.innerHTML = `
#${c.id} |
${c.channel} |
${c.language} |
${escapeHtml(c.lead_name || "—")} |
${status} |
${c.message_count} |
${formatWhen(c.created_at)} |
Open
·
Export CSV
|
`;
body.appendChild(tr);
});
}
function formatWhen(value) {
try {
return new Date(value).toLocaleString();
} catch (_) {
return value;
}
}
function escapeHtml(text) {
return String(text)
.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">");
}
loadStats();
loadConversations();
})();