Files
web/main.js
T
2026-07-08 15:34:08 -05:00

134 lines
4.8 KiB
JavaScript

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 = `<div class="item-card"><p class="item-content">No news yet.</p></div>`;
return;
}
el.innerHTML = posts.map(post => `
<div class="item-card">
<p class="item-title">${escapeHtml(post.title)}<span class="item-timestamp">${timeAgo(post.created_at)}</span></p>
<p class="item-content">${escapeHtml(post.content)}</p>
</div>
`).join("");
} catch (err) {
console.error("Failed to load news:", err);
el.innerHTML = `<div class="item-card"><p class="item-content">Couldn't load news right now.</p></div>`;
}
}
// ---------- Media ----------
async function loadMedia() {
const el = document.getElementById("media-grid");
try {
const items = await getJSON("/media?limit=8");
if (items.length === 0) {
el.innerHTML = `<div class="media-item"><p class="media-title">No media yet.</p></div>`;
return;
}
el.innerHTML = items.map(item => `
<div class="media-item">
<p class="media-title">${escapeHtml(item.title)}</p>
${item.media_type === "video"
? `<video class="img-embed" src="${escapeHtml(item.url)}" controls style="width:100%;height:auto;background:#000;"></video>`
: `<img class="img-embed" src="${escapeHtml(item.url)}" alt="${escapeHtml(item.title)}" style="width:100%;height:auto;object-fit:cover;">`
}
</div>
`).join("");
} catch (err) {
console.error("Failed to load media:", err);
el.innerHTML = `<div class="media-item"><p class="media-title">Couldn't load media right now.</p></div>`;
}
}
// ---------- 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 => `
<div class="stat-row"><span>${escapeHtml(u.username)}</span><span class="val">now</span></div>
`).join("")
: `<div class="stat-row"><span>No one online</span><span class="val"></span></div>`;
activityEl.innerHTML = data.recentActivity.length
? data.recentActivity.map(a => `
<div class="stat-row"><span>${escapeHtml(a.description)}</span><span class="val">${timeAgo(a.created_at)}</span></div>
`).join("")
: `<div class="stat-row"><span>No recent activity</span><span class="val"></span></div>`;
const { users, reports, build } = data.general;
generalEl.innerHTML = `
<div class="stat-card">
<h4>Users</h4>
<p>Total: ${users.total}<br>Recent: ${users.mostRecent ? escapeHtml(users.mostRecent.username) : "—"}</p>
</div>
<div class="stat-card">
<h4>Reports</h4>
<p>Named: ${reports.named}<br>Associated: ${reports.associated}<br>Logged: ${reports.logged} time(s)</p>
</div>
<div class="stat-card">
<h4>Build</h4>
<p>Pipeline: ${build.pipeline}<br>Build: ${build.buildNumber}<br>Version: ${build.version}</p>
</div>
`;
} catch (err) {
console.error("Failed to load overview stats:", err);
usersEl.innerHTML = `<div class="stat-row"><span>no active users!</span><span class="val"></span></div>`;
activityEl.innerHTML = `<div class="stat-row"><span>no activity!</span><span class="val"></span></div>`;
generalEl.innerHTML = `<div class="stat-card"><h4>no statistics!</h4></div>`;
}
}
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);