feat(library): sort control on the pressings grid

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) <noreply@anthropic.com>
This commit is contained in:
Jonathan
2026-07-14 20:49:43 +02:00
parent 2c3d6e77d5
commit 9e5d5d3af4
+25 -3
View File
@@ -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 AZ", cmp: (a, b) => a.album.localeCompare(b.album) },
{ key: "artist", label: "Artist AZ", cmp: (a, b) => a.artist.localeCompare(b.artist) || (a.year ?? "").localeCompare(b.year ?? "") },
];
export function LibraryClient() {
const [albums, setAlbums] = useState<Album[]>([]);
const [q, setQ] = useState("");
const [sort, setSort] = useState<SortKey>("added");
const [open, setOpen] = useState<Album | null>(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 (
<div>
@@ -70,6 +84,14 @@ export function LibraryClient() {
<span>Filter</span>
<input aria-label="filter library" placeholder="Artist or album…" value={q} onChange={(e) => setQ(e.target.value)} />
</label>
<label className="field">
<span>Sort</span>
<select aria-label="sort library" value={sort} onChange={(e) => setSort(e.target.value as SortKey)}>
{SORTS.map((s) => (
<option key={s.key} value={s.key}>{s.label}</option>
))}
</select>
</label>
</form>
{albums.length === 0 ? (