diff options
Diffstat (limited to 'app/routes/artist-by-uuid.tsx')
| -rw-r--r-- | app/routes/artist-by-uuid.tsx | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/app/routes/artist-by-uuid.tsx b/app/routes/artist-by-uuid.tsx new file mode 100644 index 0000000..9b8a4b1 --- /dev/null +++ b/app/routes/artist-by-uuid.tsx @@ -0,0 +1,100 @@ +import { data, Link, useLoaderData } from "react-router"; +import type { LoaderFunctionArgs } from "react-router"; +import { getArtistBands, getArtistById, getArtistLinks, getArtistRevisions } from "~/lib/db.server"; + +export async function loader({ params }: LoaderFunctionArgs) { + const artist = getArtistById(params.uuid!); + if (!artist) throw data("Not found", { status: 404 }); + const links = getArtistLinks(artist.id); + const bands = getArtistBands(artist.id); + const revisions = getArtistRevisions(artist.id); + return { artist, links, bands, latest: revisions[0] ?? null }; +} + +export default function ArtistDetail() { + const { artist, links, bands, latest } = useLoaderData<typeof loader>(); + return ( + <main className="max-w-3xl mx-auto px-4 py-8"> + <div className="flex items-start justify-between mb-6"> + <h1 className="text-2xl font-bold">{artist.name}</h1> + <div className="flex items-center gap-3 text-sm shrink-0 ml-4"> + <Link + to={`/artists/of/${artist.id}/history`} + className="text-gray-400 hover:text-gray-200 transition-colors" + > + 履歴 + </Link> + <Link + to={`/artists/of/${artist.id}/edit`} + className="bg-gray-800 hover:bg-gray-700 text-gray-200 px-3 py-1.5 rounded transition-colors" + > + 編集 + </Link> + </div> + </div> + + {bands.length > 0 && ( + <section className="mb-6"> + <h2 className="text-xs font-medium text-gray-500 uppercase tracking-wider mb-3"> + バンド + </h2> + <ul className="space-y-2"> + {bands.map((b) => ( + <li key={b.band_id} className="flex items-center gap-3"> + <Link + to={`/bands/of/${b.band_id}`} + className="text-blue-400 hover:text-blue-300 transition-colors font-medium" + > + {b.band_name} + </Link> + {b.role && ( + <span className="text-gray-400 text-sm">{b.role}</span> + )} + </li> + ))} + </ul> + </section> + )} + + {links.length > 0 && ( + <section className="mb-6"> + <h2 className="text-xs font-medium text-gray-500 uppercase tracking-wider mb-3"> + リンク + </h2> + <ul className="space-y-1.5"> + {links.map((l) => ( + <li key={l.id}> + <a + href={l.url} + target="_blank" + rel="noopener noreferrer" + className="text-blue-400 hover:text-blue-300 transition-colors text-sm" + > + {l.label} + </a> + </li> + ))} + </ul> + </section> + )} + + <hr className="border-gray-800 my-6" /> + <div className="text-xs text-gray-600 space-y-1 font-mono"> + <p>/artists/of/{artist.id}</p> + <p> + <Link + to={`/artists/named/${artist.slug}`} + className="hover:text-gray-400 transition-colors" + > + /artists/named/{artist.slug} + </Link> + </p> + {latest && ( + <p className="font-sans text-gray-500 mt-2"> + 最終更新: {latest.created_at} — {latest.message} + </p> + )} + </div> + </main> + ); +} |
