add filtering and sorting features; enhance events and articles pages with URL state persistence

This commit is contained in:
ImBenji
2026-04-23 23:03:11 +01:00
parent bec6763191
commit 194442ec4c
8 changed files with 274 additions and 16 deletions
+51 -7
View File
@@ -1,5 +1,6 @@
// articles page — list + keyword/status/source filtering + edit modal
// depends on: app.js (api, toast, escapeHtml, PAGE, badgeHtml)
// filter state is persisted to the url query so reload/share keeps it.
// depends on: app.js (api, toast, escapeHtml, PAGE, badgeHtml, query*)
let articleOffset = 0;
let currentArticle = null;
@@ -8,12 +9,17 @@ let currentArticle = null;
async function loadSources() {
const sources = await api("/admin/api/sources");
const sel = document.getElementById("f-source");
const current = queryGet("source");
sources.forEach(s => {
const opt = document.createElement("option");
opt.value = s;
opt.textContent = s;
sel.appendChild(opt);
});
if (current) sel.value = current;
}
@@ -28,6 +34,16 @@ function getFilters() {
}
// sync current filters + offset into url so the state is bookmarkable
function syncUrl() {
const f = getFilters();
queryWrite({
...f,
offset: articleOffset > 0 ? articleOffset : "",
});
}
async function loadArticles() {
const f = getFilters();
const params = new URLSearchParams({ limit: PAGE, offset: articleOffset });
@@ -88,14 +104,42 @@ async function openArticle(id) {
}
document.addEventListener("DOMContentLoaded", () => {
document.addEventListener("DOMContentLoaded", async () => {
document.getElementById("searchBtn").onclick = () => { articleOffset = 0; loadArticles(); };
document.getElementById("prevBtn").onclick = () => { articleOffset = Math.max(0, articleOffset - PAGE); loadArticles(); };
document.getElementById("nextBtn").onclick = () => { articleOffset += PAGE; loadArticles(); };
// restore filter inputs from the current url
queryApplyToInputs({
"f-keyword": "keyword",
"f-status": "content_status",
"f-from": "from",
"f-to": "to",
});
articleOffset = parseInt(queryGet("offset"), 10) || 0;
document.getElementById("searchBtn").onclick = () => {
articleOffset = 0;
syncUrl();
loadArticles();
};
document.getElementById("prevBtn").onclick = () => {
articleOffset = Math.max(0, articleOffset - PAGE);
syncUrl();
loadArticles();
};
document.getElementById("nextBtn").onclick = () => {
articleOffset += PAGE;
syncUrl();
loadArticles();
};
document.querySelector(".filters").addEventListener("keydown", e => {
if (e.key === "Enter") { articleOffset = 0; loadArticles(); }
if (e.key === "Enter") {
articleOffset = 0;
syncUrl();
loadArticles();
}
});
@@ -140,6 +184,6 @@ document.addEventListener("DOMContentLoaded", () => {
}
};
loadSources();
await loadSources(); // wait for dropdown to render the saved source selection
loadArticles();
});