import { data, Link, useLoaderData } from "react-router"; import type { LoaderFunctionArgs } from "react-router"; import { getBandById, getBandLinks, getBandMembers, getBandRevisions, groupBandMembers, type MemberGroup, } from "~/lib/db.server"; import { LINK_TYPE_LABEL } from "~/lib/constants"; import { formatDuration } from "~/lib/utils"; export async function loader({ params }: LoaderFunctionArgs) { const band = getBandById(params.uuid!); if (!band) throw data("Not found", { status: 404 }); const links = getBandLinks(band.id); const members = getBandMembers(band.id); const revisions = getBandRevisions(band.id); const grouped = groupBandMembers(members); return { band, links, grouped, latest: revisions[0] ?? null }; } const STATUS_LABEL: Record = { active: "活動中", hiatus: "活動休止", disbanded: "解散", }; function periodRange(since: string, until: string): string | null { if (!since && !until) return null; return `${since || "?"} 〜 ${until || "現在"}`; } function MemberItem({ group }: { group: MemberGroup }) { const roles = [...new Set(group.periods.flatMap((p) => p.role ? p.role.split(", ").filter(Boolean) : [] ))]; const hasPeriodInfo = group.periods.some((p) => p.since || p.until || p.note); return (
  • {group.artist_name} {roles.map((r, i) => {r})} {group.duration_months !== null && ( {formatDuration(group.duration_months)} )}
    {hasPeriodInfo && ( )}
  • ); } export default function BandDetail() { const { band, links, grouped, latest } = useLoaderData(); return (

    {band.name}

    {band.area && {band.area}} {STATUS_LABEL[band.status] ?? band.status}
    {band.description && (

    {band.description}

    )}
    履歴 編集
    {grouped.current.length > 0 && (

    メンバー

      {grouped.current.map((g) => )}
    )} {grouped.former.length > 0 && (

    元メンバー

      {grouped.former.map((g) => )}
    )} {links.length > 0 && (

    リンク

    )}

    /bands/of/{band.id}

    /bands/named/{band.slug}

    {latest && (

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

    )}
    ); }