import { data, Link, useLoaderData } from "react-router"; import type { LoaderFunctionArgs } from "react-router"; import { getArtistById, getArtistLinks, getArtistMembers, getArtistRevisions, type ArtistMemberRow } 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 memberships = getArtistMembers(artist.id); const revisions = getArtistRevisions(artist.id); return { artist, links, memberships, latest: revisions[0] ?? null }; } function periodLabel(m: ArtistMemberRow): string | null { if (!m.since && !m.until) return null; const from = m.since || "?"; const to = m.until || "現在"; return `${from} 〜 ${to}`; } export default function ArtistDetail() { const { artist, links, memberships, latest } = useLoaderData(); // group by band_id const bandIds = [...new Set(memberships.map((m) => m.band_id))]; return (

{artist.name}

履歴 編集
{bandIds.length > 0 && (

バンド

    {bandIds.map((bandId) => { const group = memberships.filter((m) => m.band_id === bandId); const first = group[0]; return (
  • {first.band_name} {first.role && {first.role}}
    {group.some((m) => m.since || m.until || m.note) && (
      {group.map((m) => { const label = periodLabel(m); if (!label && !m.note) return null; return (
    • {label && {label}} {m.note && {m.note}}
    • ); })}
    )}
  • ); })}
)} {links.length > 0 && (

リンク

)}

/artists/of/{artist.id}

/artists/named/{artist.slug}

{latest && (

最終更新: {latest.created_at} — {latest.message}

)}
); }