From 46dd8df55cdcfd8b5c6c4b4a3482f4a49c33b2eb Mon Sep 17 00:00:00 2001 From: nils Date: Fri, 17 Jul 2026 18:59:15 +0200 Subject: [PATCH] Album filters: star threshold as interactive stars control Replaces the three fixed star chips with the Stars component as a min-average selector, combinable with the verdict chips --- README.md | 6 +++--- frontend/src/pages/AlbumPage.jsx | 29 ++++++++++++++++------------- frontend/src/styles.css | 16 ++++++++++++++++ 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 527a0df..65969bb 100644 --- a/README.md +++ b/README.md @@ -112,8 +112,8 @@ The prebuilt image is at `git.draic.info/nils/photos` (single image contains `server`, `worker`, and the built frontend). To build your own instead: ```sh -docker build -t registry.example.com/you/photos:0.4.2 . -docker push registry.example.com/you/photos:0.4.2 +docker build -t registry.example.com/you/photos:0.4.3 . +docker push registry.example.com/you/photos:0.4.3 ``` Install the chart, pointing it at your existing Postgres and S3: @@ -121,7 +121,7 @@ Install the chart, pointing it at your existing Postgres and S3: ```sh helm install photos deploy/chart \ --set image.repository=git.draic.info/nils/photos \ - --set image.tag=0.4.2 \ + --set image.tag=0.4.3 \ --set publicUrl=https://photos.example.com \ --set ingress.host=photos.example.com \ --set config.oidcIssuer=https://auth.example.com \ diff --git a/frontend/src/pages/AlbumPage.jsx b/frontend/src/pages/AlbumPage.jsx index 9217e91..214377e 100644 --- a/frontend/src/pages/AlbumPage.jsx +++ b/frontend/src/pages/AlbumPage.jsx @@ -23,9 +23,6 @@ const FEEDBACK_FILTERS = [ { key: 'accept', label: '👍' }, { key: 'reject', label: '👎' }, { key: 'undecided', label: 'Undecided' }, - { key: 'star3', label: '★ 3+' }, - { key: 'star4', label: '★ 4+' }, - { key: 'star5', label: '★ 5' }, ] // Expiry convention, in one place for the create form and the row editor: @@ -432,28 +429,27 @@ export default function AlbumPage() { return ratings.reduce((sum, r) => sum + r.rating, 0) / ratings.length } - // Filter over client feedback: verdicts, or minimum average star rating. + // Two combinable filter dimensions over client feedback: verdict chips + // plus a minimum-average-stars threshold (0 = off). const [filter, setFilter] = useState('all') - const matchesFilter = (p, key) => { + const [minStars, setMinStars] = useState(0) + const matchesVerdict = (p, key) => { if (key === 'all') return true const verdicts = feedback[p.id]?.verdicts || [] if (key === 'accept') return verdicts.some((v) => v.verdict === 'accept') if (key === 'reject') return verdicts.some((v) => v.verdict === 'reject') - if (key === 'undecided') return verdicts.length === 0 - return (avgRating(p.id) ?? 0) >= Number(key.slice(4)) + return verdicts.length === 0 } - const shown = ready.filter((p) => matchesFilter(p, filter)) + const shown = ready.filter( + (p) => matchesVerdict(p, filter) && (minStars === 0 || (avgRating(p.id) ?? 0) >= minStars), + ) const filterCounts = useMemo(() => { - const counts = { all: ready.length, accept: 0, reject: 0, undecided: 0, star3: 0, star4: 0, star5: 0 } + const counts = { all: ready.length, accept: 0, reject: 0, undecided: 0 } for (const p of ready) { const verdicts = feedback[p.id]?.verdicts || [] if (verdicts.some((v) => v.verdict === 'accept')) counts.accept += 1 if (verdicts.some((v) => v.verdict === 'reject')) counts.reject += 1 if (verdicts.length === 0) counts.undecided += 1 - const avg = avgRating(p.id) ?? 0 - if (avg >= 3) counts.star3 += 1 - if (avg >= 4) counts.star4 += 1 - if (avg >= 5) counts.star5 += 1 } return counts }, [detail]) @@ -566,6 +562,13 @@ export default function AlbumPage() { {f.label} {filterCounts[f.key]} ))} + 0 ? ' active' : ''}`} + title="Minimum average rating — click a star to filter, click it again to clear" + > + setMinStars(n)} small /> + {minStars > 0 && ≥ {minStars}} + )} diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 8e769ec..6929a26 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -675,6 +675,22 @@ progress { justify-content: flex-start; margin: 0 0 0.75rem; } +.filter-stars { + display: inline-flex; + align-items: center; + gap: 0.35rem; + background: var(--panel); + border: 1px solid var(--panel-2); + border-radius: 999px; + padding: 0.1rem 0.7rem; + font-size: 0.85rem; +} +.filter-stars.active { + border-color: var(--accent); +} +.filter-stars .star { + cursor: pointer; +} /* transient action feedback (keyboard votes that navigate away) */ .action-flash {