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:
@@ -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<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 ? (
|
||||
|
||||
Reference in New Issue
Block a user