feat(web): restyle Artists, Discover, Wanted, discography + preview (Phase 2)

Convert the remaining pages onto the Pressing Plant system: PageHead
component, shared list-row / toggle / small+ghost button / tracklist classes,
and page wrappers trimmed now that the shell provides masthead + nav. All
logic and test hooks (aria-labels, button text) preserved. Verified in the
real app across both themes (Playwright): artists/discover/wanted/discography
render cohesively with real data.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jonathan
2026-07-12 14:27:43 +02:00
parent 7f1fe52a7c
commit ead5f33bcc
12 changed files with 404 additions and 204 deletions
+53 -28
View File
@@ -1,6 +1,7 @@
"use client";
import { useEffect, useState } from "react";
import { PageHead } from "../../_ui/page-head";
type Release = {
id: string;
@@ -14,10 +15,10 @@ type Release = {
};
type Detail = { id: string; name: string; autoMonitorFuture: boolean; showAllTypes: boolean; releases: Release[] };
function badge(r: Release): string {
if (r.currentQualityClass !== null) return `Have (q${r.currentQualityClass})`;
if (r.monitored) return r.state === "wanted" ? "Wanted" : r.state;
return "Dormant";
function badge(r: Release): { label: string; cls: string } {
if (r.currentQualityClass !== null) return { label: `Have q${r.currentQualityClass}`, cls: "chip done" };
if (r.monitored) return { label: r.state === "wanted" ? "Wanted" : r.state, cls: "chip working" };
return { label: "Dormant", cls: "chip" };
}
export function DiscographyClient({ id }: { id: string }) {
@@ -53,32 +54,56 @@ export function DiscographyClient({ id }: { id: string }) {
refresh();
}
if (!detail) return <p>Loading</p>;
if (!detail) return <p className="empty">Loading</p>;
return (
<div>
<h1>{detail.name}</h1>
<label>
<input
type="checkbox"
aria-label="show all release types"
checked={detail.showAllTypes}
onChange={toggleShowAll}
/>{" "}
Show all release types (live, compilations, singles)
</label>
<ul>
{detail.releases.map((r) => (
<li key={r.id}>
<strong>{r.album}</strong>
{r.firstReleaseDate ? ` (${r.firstReleaseDate.slice(0, 4)})` : ""}
{r.primaryType ? ` · ${r.primaryType}` : ""}
{r.secondaryTypes.length ? ` [${r.secondaryTypes.join(", ")}]` : ""} · <em>{badge(r)}</em>{" "}
<label>
<input type="checkbox" aria-label={`monitor ${r.album}`} checked={r.monitored} onChange={() => toggleMonitor(r)} /> monitor
</label>{" "}
<button onClick={() => searchNow(r)}>Search now</button>
</li>
))}
<PageHead title={detail.name} eyebrow="Discography" />
<div className="tool-row">
<label className="toggle">
<input
type="checkbox"
aria-label="show all release types"
checked={detail.showAllTypes}
onChange={toggleShowAll}
/>
Show all types (live, compilations, singles)
</label>
</div>
<ul className="list">
{detail.releases.map((r) => {
const b = badge(r);
return (
<li key={r.id} className="list-row">
<div className="main">
<div className="rtitle">
{r.album}
{r.firstReleaseDate ? <span className="dim"> ({r.firstReleaseDate.slice(0, 4)})</span> : null}
</div>
<div className="rmeta">
{r.primaryType ? <span>{r.primaryType}</span> : null}
{r.secondaryTypes.length ? (
<>
<span className="dot">·</span>
<span>{r.secondaryTypes.join(", ")}</span>
</>
) : null}
</div>
</div>
<div className="actions">
<span className={b.cls}>{b.label}</span>
<label className="toggle">
<input type="checkbox" aria-label={`monitor ${r.album}`} checked={r.monitored} onChange={() => toggleMonitor(r)} />
monitor
</label>
<button className="btn sm ghost" onClick={() => searchNow(r)}>
Search now
</button>
</div>
</li>
);
})}
</ul>
</div>
);