← back to Ventura Corridor
feat(news): vertical filter dropdown on /news.html
8172d9365b6263f73b5aac5527992b9ffa337d87 · 2026-05-07 22:39:06 -0700 · SteveStudio2
Adds an "All categories" select next to the search and sort controls
on /news.html. Categories are populated from the rendered data so the
dropdown stays in sync with whatever the API returned (uses prefix
match so picking "amenity: restaurant" matches that exact subcategory,
"shop" matches every shop:* subcategory).
Pure client-side filter, no server change — /api/news/recent already
returns category per row.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Files touched
Diff
commit 8172d9365b6263f73b5aac5527992b9ffa337d87
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Thu May 7 22:39:06 2026 -0700
feat(news): vertical filter dropdown on /news.html
Adds an "All categories" select next to the search and sort controls
on /news.html. Categories are populated from the rendered data so the
dropdown stays in sync with whatever the API returned (uses prefix
match so picking "amenity: restaurant" matches that exact subcategory,
"shop" matches every shop:* subcategory).
Pure client-side filter, no server change — /api/news/recent already
returns category per row.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
public/news.html | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/public/news.html b/public/news.html
index e078bd6..4e07741 100644
--- a/public/news.html
+++ b/public/news.html
@@ -76,6 +76,9 @@ select{background:var(--noir-rise);color:var(--ink);border:1px solid var(--rule)
<div class="controls">
<div class="left">
<input type="search" id="q" placeholder="Filter by business, title, or text…" autocomplete="off">
+ <select id="vertical">
+ <option value="">All categories</option>
+ </select>
<select id="sort">
<option value="recent">Most recent</option>
<option value="biz">By business A→Z</option>
@@ -105,6 +108,7 @@ const truncate = (s, n) => {
function render() {
const q = (document.getElementById('q').value || '').toLowerCase().trim();
const sort = document.getElementById('sort').value;
+ const vertical = document.getElementById('vertical').value || '';
let rows = ROWS;
if (q) {
rows = rows.filter(r =>
@@ -113,6 +117,9 @@ function render() {
(r.summary||r.excerpt||'').toLowerCase().includes(q)
);
}
+ if (vertical) {
+ rows = rows.filter(r => (r.category || '').toLowerCase().startsWith(vertical.toLowerCase()));
+ }
rows = rows.slice();
if (sort === 'biz') {
rows.sort((a, b) => (a.business_name||'').localeCompare(b.business_name||''));
@@ -161,6 +168,16 @@ async function load() {
const fresh = ROWS.filter(r => new Date(r.fetched_at).getTime() > week).length;
document.getElementById('s-fresh').textContent = fresh;
document.getElementById('status').textContent = ROWS.length + ' total';
+
+ // Populate vertical dropdown from data
+ const cats = [...new Set(ROWS.map(r => r.category).filter(Boolean))].sort();
+ const sel = document.getElementById('vertical');
+ cats.forEach(c => {
+ const opt = document.createElement('option');
+ opt.value = c; opt.textContent = c;
+ sel.appendChild(opt);
+ });
+
render();
} catch (e) {
document.getElementById('status').textContent = 'load error';
@@ -170,6 +187,7 @@ async function load() {
document.getElementById('q').addEventListener('input', () => render());
document.getElementById('sort').addEventListener('change', () => render());
+document.getElementById('vertical').addEventListener('change', () => render());
load();
</script>
</body>
← b8a21eb feat(news): news counts in homepage build-status strip
·
back to Ventura Corridor
·
feat(coverage): ideas_accepted count in homepage build-statu c9a00d6 →