window.addEventListener('DOMContentLoaded', () => { bootstrapAdmin(); }); const API_BASE = "http://10.0.0.66:7455/api"; // Small helper: fetch JSON, throw on non-2xx so callers can catch() it. async function getJSON(path) { const res = await fetch(`${API_BASE}${path}`); if (!res.ok) throw new Error(`${path} responded ${res.status}`); return res.json(); } function timeAgo(isoString) { const diffMs = Date.now() - new Date(isoString).getTime(); const mins = Math.floor(diffMs / 60000); if (mins < 60) return `${mins}m ago`; const hours = Math.floor(mins / 60); if (hours < 24) return `${hours}h ago`; return `${Math.floor(hours / 24)}d ago`; } function escapeHtml(str) { const div = document.createElement("div"); div.textContent = str; return div.innerHTML; } // ---------- News ---------- async function loadNews() { const el = document.getElementById("news-list"); try { const posts = await getJSON("/news?limit=10"); if (posts.length === 0) { el.innerHTML = `

No news yet.

`; return; } el.innerHTML = posts.map(post => `

${escapeHtml(post.title)}${timeAgo(post.created_at)}

${escapeHtml(post.content)}

`).join(""); } catch (err) { console.error("Failed to load news:", err); el.innerHTML = `

Couldn't load news right now.

`; } } // ---------- Media ---------- async function loadMedia() { const el = document.getElementById("media-grid"); try { const items = await getJSON("/media?limit=8"); if (items.length === 0) { el.innerHTML = `

No media yet.

`; return; } el.innerHTML = items.map(item => `

${escapeHtml(item.title)}

${item.media_type === "video" ? `` : `${escapeHtml(item.title)}` }
`).join(""); } catch (err) { console.error("Failed to load media:", err); el.innerHTML = `

Couldn't load media right now.

`; } } // ---------- Overview (current users / recent activity / general stats) ---------- async function loadOverview() { const usersEl = document.getElementById("current-users"); const activityEl = document.getElementById("recent-activity"); const generalEl = document.getElementById("general-stats"); try { const data = await getJSON("/stats/overview"); usersEl.innerHTML = data.currentUsers.length ? data.currentUsers.map(u => `
${escapeHtml(u.username)}now
`).join("") : `
No one online
`; activityEl.innerHTML = data.recentActivity.length ? data.recentActivity.map(a => `
${escapeHtml(a.description)}${timeAgo(a.created_at)}
`).join("") : `
No recent activity
`; const { users, reports, build } = data.general; generalEl.innerHTML = `

Users

Total: ${users.total}
Recent: ${users.mostRecent ? escapeHtml(users.mostRecent.username) : "—"}

Reports

Named: ${reports.named}
Associated: ${reports.associated}
Logged: ${reports.logged} time(s)

Build

Pipeline: ${build.pipeline}
Build: ${build.buildNumber}
Version: ${build.version}

`; } catch (err) { console.error("Failed to load overview stats:", err); usersEl.innerHTML = `
no active users!
`; activityEl.innerHTML = `
no activity!
`; generalEl.innerHTML = `

no statistics!

`; } } function loadAll() { loadNews(); loadMedia(); loadOverview(); } async function bootstrapAdmin() { try { await loadAll(); } finally { document.body.classList.remove('is-loading'); } setInterval(loadOverview, 30000); } // Keep "current users" / "recent activity" feeling live without a full reload. setInterval(loadOverview, 30000);