import { data, Link, useLoaderData } from "react-router";
import type { LoaderFunctionArgs } from "react-router";
import {
getArtistById,
getArtistLinks,
getArtistMembers,
getArtistRevisions,
groupArtistMembers,
type BandGroup,
} from "~/lib/db.server";
import { formatDuration } from "~/lib/utils";
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);
const grouped = groupArtistMembers(memberships);
return { artist, links, grouped, latest: revisions[0] ?? null };
}
function periodRange(since: string, until: string): string | null {
if (!since && !until) return null;
return `${since || "?"} 〜 ${until || "現在"}`;
}
function BandItem({ group }: { group: BandGroup }) {
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.band_name}
{roles.map((r, i) => {r})}
{group.duration_months !== null && (
{formatDuration(group.duration_months)}
)}
{hasPeriodInfo && (
{group.periods.map((p) => {
const range = periodRange(p.since, p.until);
if (!range && !p.note) return null;
return (
-
{range && {range}}
{p.note && {p.note}}
);
})}
)}
);
}
export default function ArtistDetail() {
const { artist, links, grouped, latest } = useLoaderData();
return (
{grouped.current.length > 0 && (
在籍中のバンド
{grouped.current.map((g) => )}
)}
{grouped.former.length > 0 && (
元在籍バンド
{grouped.former.map((g) => )}
)}
{links.length > 0 && (
)}
/artists/of/{artist.id}
/artists/named/{artist.slug}
{latest && (
最終更新: {latest.created_at} — {latest.message}
)}
);
}