From 9e5d5d3af47f946fb78435f76f1b5f0ce0124634 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Tue, 14 Jul 2026 20:49:43 +0200 Subject: [PATCH] feat(library): sort control on the pressings grid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a Sort dropdown to /library next to the filter: Recently added (default, importedAt desc — the prior order), Release date (album year, newest first, unknown-year last), Album A–Z, Artist A–Z. Client-side sort of the already-fetched albums; importedAt was already on the route, just declared on the client type now. Co-Authored-By: Claude Opus 4.8 (1M context) --- web/src/app/library/library-client.tsx | 28 +++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/web/src/app/library/library-client.tsx b/web/src/app/library/library-client.tsx index b0cf157..c064073 100644 --- a/web/src/app/library/library-client.tsx +++ b/web/src/app/library/library-client.tsx @@ -14,6 +14,7 @@ type Album = { album: string; format: string; qualityClass: number; + importedAt: string; rgMbid: string | null; year: string | null; primaryType: string | null; @@ -21,9 +22,19 @@ type Album = { trackNames: string[]; }; +type SortKey = "added" | "release" | "album" | "artist"; +const SORTS: { key: SortKey; label: string; cmp: (a: Album, b: Album) => number }[] = [ + { key: "added", label: "Recently added", cmp: (a, b) => b.importedAt.localeCompare(a.importedAt) }, + // Release date, newest first; albums with an unknown year sort to the bottom. + { key: "release", label: "Release date", cmp: (a, b) => (b.year ?? "0000").localeCompare(a.year ?? "0000") || a.album.localeCompare(b.album) }, + { key: "album", label: "Album A–Z", cmp: (a, b) => a.album.localeCompare(b.album) }, + { key: "artist", label: "Artist A–Z", cmp: (a, b) => a.artist.localeCompare(b.artist) || (a.year ?? "").localeCompare(b.year ?? "") }, +]; + export function LibraryClient() { const [albums, setAlbums] = useState([]); const [q, setQ] = useState(""); + const [sort, setSort] = useState("added"); const [open, setOpen] = useState(null); const [confirmDelete, setConfirmDelete] = useState(false); @@ -56,9 +67,12 @@ export function LibraryClient() { const shown = useMemo(() => { const needle = q.trim().toLowerCase(); - if (!needle) return albums; - return albums.filter((a) => `${a.artist} ${a.album}`.toLowerCase().includes(needle)); - }, [albums, q]); + const filtered = needle + ? albums.filter((a) => `${a.artist} ${a.album}`.toLowerCase().includes(needle)) + : albums; + const cmp = (SORTS.find((s) => s.key === sort) ?? SORTS[0]).cmp; + return [...filtered].sort(cmp); + }, [albums, q, sort]); return (
@@ -70,6 +84,14 @@ export function LibraryClient() { Filter setQ(e.target.value)} /> + {albums.length === 0 ? (