diff --git a/web/src/app/page.tsx b/web/src/app/page.tsx index 96e4e34..caa6e3f 100644 --- a/web/src/app/page.tsx +++ b/web/src/app/page.tsx @@ -1,11 +1,5 @@ import { Queue } from "./queue"; export default function Home() { - return ( -
-

Lyra

-

Artists · Discover · Wanted · Settings

- -
- ); + return ; } diff --git a/web/src/app/queue.tsx b/web/src/app/queue.tsx index cc2b566..af4dec4 100644 --- a/web/src/app/queue.tsx +++ b/web/src/app/queue.tsx @@ -1,24 +1,47 @@ "use client"; import { useEffect, useState } from "react"; +import { describeJob } from "./_ui/status"; +import { StatTiles } from "./_ui/stat-tiles"; +import { SectionHeader } from "./_ui/section-header"; +import { JobRow } from "./_ui/job-row"; +import { PressedList } from "./_ui/pressed-list"; type Row = { id: string; artist: string; album: string; status: string; + createdAt?: string; job: { state: string; currentStage: string } | null; }; +function jobOf(r: Row) { + return r.job ?? { state: "requested", currentStage: "intake" }; +} + +function pressedMark(r: Row): string { + if (!r.createdAt) return "Lyra"; + const d = new Date(r.createdAt); + return `Lyra · ${d.toLocaleDateString("en-GB", { day: "2-digit", month: "short" }).toUpperCase()}`; +} + export function Queue() { const [rows, setRows] = useState([]); + const [wanted, setWanted] = useState(0); + const [watching, setWatching] = useState(0); const [artist, setArtist] = useState(""); const [album, setAlbum] = useState(""); async function refresh() { - const res = await fetch("/api/requests"); - const body = await res.json(); - setRows(body.requests); + const [reqRes, wantRes, artRes] = await Promise.all([ + fetch("/api/requests"), + fetch("/api/wanted"), + fetch("/api/artists"), + ]); + setRows((await reqRes.json()).requests ?? []); + setWanted(((await wantRes.json()).wanted ?? []).length); + setWatching(((await artRes.json()).artists ?? []).length); } useEffect(() => { @@ -40,21 +63,78 @@ export function Queue() { refresh(); } + const active = rows.filter((r) => jobOf(r).state !== "imported"); + const pressed = rows.filter((r) => jobOf(r).state === "imported"); + return (
-
- setArtist(e.target.value)} /> - setAlbum(e.target.value)} /> - + + + + + + + + -
    - {rows.map((r) => ( -
  • - {r.artist} — {r.album} · {r.job?.state ?? r.status} - {r.job ? ` (${r.job.currentStage})` : ""} -
  • - ))} -
+ + {active.length === 0 ? ( +

The press is quiet. Queue a release above, or send one over from Wanted or Discover.

+ ) : ( +
+ {active.map((r) => { + const j = jobOf(r); + const d = describeJob(j.state, j.currentStage); + const attention = d.kind === "attention"; + return ( + + ); + })} +
+ )} + + {pressed.length > 0 ? ( + <> + + ({ id: r.id, artist: r.artist, album: r.album, matrix: pressedMark(r) }))} + /> + + ) : null} + +
+ Lyra — self-hosted pressing plant + MusicBrainz · Qobuz · Soulseek · YouTube +
); }