Redesign Library view to match mockup spec

- Replace band-name header + tab structure (By Date / Search) with a
  unified Library view: title, inline search input, filter pills
  (All / instrument / Commented), and date-group headers
- Session rows now use the recording-row card style (play circle,
  mono filename, recording count)
- Move mini waveform bars from session list to individual recording
  rows in SessionPage, where they correspond to a single track
- Fix Invalid Date by appending T12:00:00 when parsing date-only
  ISO strings in both BandPage and SessionPage
- Update tests: drop tab assertions (TC-07), add Library heading
  (TC-08) and filter pill (TC-09) checks, update upload button label

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Mistral Vibe
2026-04-06 18:37:49 +02:00
parent aa889579a0
commit b09094658c
3 changed files with 332 additions and 519 deletions

View File

@@ -43,14 +43,13 @@ const renderBandPage = () =>
// ── Tests ─────────────────────────────────────────────────────────────────────
describe("BandPage — cleanliness (TC-01 to TC-07)", () => {
describe("BandPage — Library view (TC-01 to TC-09)", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("TC-01: does not render a member list", async () => {
renderBandPage();
// Allow queries to settle
await new Promise((r) => setTimeout(r, 50));
expect(screen.queryByText(/members/i)).toBeNull();
});
@@ -70,7 +69,6 @@ describe("BandPage — cleanliness (TC-01 to TC-07)", () => {
it("TC-04: renders sessions grouped by date", async () => {
renderBandPage();
// Sessions appear after the query resolves
const sessionEl = await screen.findByText("Late Night Jam");
expect(sessionEl).toBeTruthy();
});
@@ -81,17 +79,30 @@ describe("BandPage — cleanliness (TC-01 to TC-07)", () => {
expect(btn).toBeTruthy();
});
it("TC-06: renders the + New Song button", async () => {
it("TC-06: renders the + Upload button", async () => {
renderBandPage();
const btn = await screen.findByText(/\+ new song/i);
const btn = await screen.findByText(/\+ upload/i);
expect(btn).toBeTruthy();
});
it("TC-07: renders both By Date and Search tabs", async () => {
it("TC-07: does not render By Date / Search tabs", async () => {
renderBandPage();
const byDate = await screen.findByText(/by date/i);
const search = await screen.findByText(/^search$/i);
expect(byDate).toBeTruthy();
expect(search).toBeTruthy();
await new Promise((r) => setTimeout(r, 50));
expect(screen.queryByText(/by date/i)).toBeNull();
expect(screen.queryByText(/^search$/i)).toBeNull();
});
it("TC-08: renders the Library heading", async () => {
renderBandPage();
const heading = await screen.findByText("Library");
expect(heading).toBeTruthy();
});
it("TC-09: renders filter pills including All and Guitar", async () => {
renderBandPage();
const allPill = await screen.findByText("all");
const guitarPill = await screen.findByText("guitar");
expect(allPill).toBeTruthy();
expect(guitarPill).toBeTruthy();
});
});