first commit
This commit is contained in:
commit
74229b4471
4 files changed
+967
No files matched your search
@@ -0,0 +1,134 @@
|
||||
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);
|
||||
Reference in new issue
Block a user