add filtering and sorting features; enhance events and articles pages with URL state persistence
This commit is contained in:
@@ -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();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user