Duriin-API/public/admin/assets/js/events.js

137 lines
4.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// events page — list + title edit + detach-and-delete, with filters
// filter/sort/pagination state persists to url query params
// depends on: app.js
let eventOffset = 0;
let currentEvent = null;
function getEventFilters() {
return {
keyword: document.getElementById("ef-keyword").value.trim(),
min_articles: document.getElementById("ef-min").value.trim(),
from: document.getElementById("ef-from").value,
to: document.getElementById("ef-to").value,
sort: document.getElementById("ef-sort").value,
};
}
function syncUrl() {
const f = getEventFilters();
queryWrite({
...f,
// default sort isn't worth surfacing in the url
sort: f.sort && f.sort !== "created_desc" ? f.sort : "",
offset: eventOffset > 0 ? eventOffset : "",
});
}
async function loadEvents() {
const f = getEventFilters();
const params = new URLSearchParams({ limit: PAGE, offset: eventOffset });
if (f.keyword) params.set("keyword", f.keyword);
if (f.min_articles) params.set("min_articles", f.min_articles);
if (f.from) params.set("from", f.from + "T00:00:00");
if (f.to) params.set("to", f.to + "T23:59:59");
if (f.sort) params.set("sort", f.sort);
const data = await api(`/admin/api/events?${params}`);
const tbody = document.getElementById("eventTable");
tbody.innerHTML = data.rows.map(r => `
<tr>
<td style="color:var(--muted-dark); font-size:12px">${r.id}</td>
<td><span class="truncate" title="${escapeHtml(r.title)}">${escapeHtml(r.title)}</span></td>
<td>${r.article_count}</td>
<td style="color:var(--muted-dark); white-space:nowrap; font-size:12px">${r.created_at ? r.created_at.slice(0, 16) : "—"}</td>
<td><button onclick='openEvent(${r.id}, ${JSON.stringify(r.title)})'>Edit</button></td>
</tr>
`).join("");
const total = data.total;
document.getElementById("ePageInfo").textContent =
`${eventOffset + 1}${Math.min(eventOffset + PAGE, total)} of ${total.toLocaleString()}`;
document.getElementById("ePrevBtn").disabled = eventOffset === 0;
document.getElementById("eNextBtn").disabled = eventOffset + PAGE >= total;
}
function openEvent(id, title) {
currentEvent = { id, title };
document.getElementById("em-title").value = title;
document.getElementById("eventOverlay").classList.add("open");
}
document.addEventListener("DOMContentLoaded", () => {
// restore filter state from url
queryApplyToInputs({
"ef-keyword": "keyword",
"ef-min": "min_articles",
"ef-from": "from",
"ef-to": "to",
"ef-sort": "sort",
});
eventOffset = parseInt(queryGet("offset"), 10) || 0;
document.getElementById("eSearchBtn").onclick = () => {
eventOffset = 0;
syncUrl();
loadEvents();
};
document.getElementById("ePrevBtn").onclick = () => {
eventOffset = Math.max(0, eventOffset - PAGE);
syncUrl();
loadEvents();
};
document.getElementById("eNextBtn").onclick = () => {
eventOffset += PAGE;
syncUrl();
loadEvents();
};
document.querySelector(".filters").addEventListener("keydown", e => {
if (e.key === "Enter") {
eventOffset = 0;
syncUrl();
loadEvents();
}
});
document.getElementById("eCancelBtn").onclick = () =>
document.getElementById("eventOverlay").classList.remove("open");
document.getElementById("eSaveBtn").onclick = async () => {
if (!currentEvent) return;
try {
await api(`/admin/api/events/${currentEvent.id}`, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ title: document.getElementById("em-title").value }),
});
document.getElementById("eventOverlay").classList.remove("open");
toast("Saved");
loadEvents();
} catch (e) { toast("Save failed", true); }
};
document.getElementById("eDeleteBtn").onclick = async () => {
if (!currentEvent) return;
if (!confirm(`Delete event #${currentEvent.id}? Articles will be detached but not deleted.`)) return;
try {
await api(`/admin/api/events/${currentEvent.id}`, { method: "DELETE" });
document.getElementById("eventOverlay").classList.remove("open");
toast("Event deleted");
loadEvents();
loadGlobalStats();
} catch (e) { toast("Delete failed", true); }
};
loadEvents();
});