From 184e6947707ecdf07dfa3a5cbc6e51cf9440e93a Mon Sep 17 00:00:00 2001
From: yyamashita <yyamashita@mosquit.one>
Date: Sun, 10 May 2026 00:21:04 +0900
Subject: Add members table with membership period and note support

Replace band_artists + member_periods with a single members table
(id, band_id, artist_id, role, since, until, note, order_index).
Each row represents one membership period, so rejoining artists
get multiple rows. Existing band_artists data is auto-migrated on
startup. Export format bumped to version 3.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---
 app/routes/artist-by-uuid.tsx | 53 ++++++++++++++++++++++++++++++++++---------
 1 file changed, 42 insertions(+), 11 deletions(-)

(limited to 'app/routes/artist-by-uuid.tsx')

diff --git a/app/routes/artist-by-uuid.tsx b/app/routes/artist-by-uuid.tsx
index 6eb06a7..a65525b 100644
--- a/app/routes/artist-by-uuid.tsx
+++ b/app/routes/artist-by-uuid.tsx
@@ -1,18 +1,29 @@
 import { data, Link, useLoaderData } from "react-router";
 import type { LoaderFunctionArgs } from "react-router";
-import { getArtistBands, getArtistById, getArtistLinks, getArtistRevisions } from "~/lib/db.server";
+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 bands = getArtistBands(artist.id);
+  const memberships = getArtistMembers(artist.id);
   const revisions = getArtistRevisions(artist.id);
-  return { artist, links, bands, latest: revisions[0] ?? null };
+  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, bands, latest } = useLoaderData<typeof loader>();
+  const { artist, links, memberships, latest } = useLoaderData<typeof loader>();
+
+  // group by band_id
+  const bandIds = [...new Set(memberships.map((m) => m.band_id))];
+
   return (
     <main>
       <div className="detail-header">
@@ -23,16 +34,36 @@ export default function ArtistDetail() {
         </div>
       </div>
 
-      {bands.length > 0 && (
+      {bandIds.length > 0 && (
         <section>
           <h2>バンド</h2>
           <ul className="member-list">
-            {bands.map((b) => (
-              <li key={b.band_id}>
-                <Link to={`/bands/of/${b.band_id}`}>{b.band_name}</Link>
-                {b.role && <span className="muted">{b.role}</span>}
-              </li>
-            ))}
+            {bandIds.map((bandId) => {
+              const group = memberships.filter((m) => m.band_id === bandId);
+              const first = group[0];
+              return (
+                <li key={bandId}>
+                  <div className="member-main">
+                    <Link to={`/bands/of/${bandId}`}>{first.band_name}</Link>
+                    {first.role && <span className="muted">{first.role}</span>}
+                  </div>
+                  {group.some((m) => m.since || m.until || m.note) && (
+                    <ul className="period-list">
+                      {group.map((m) => {
+                        const label = periodLabel(m);
+                        if (!label && !m.note) return null;
+                        return (
+                          <li key={m.id} className="period-item">
+                            {label && <span className="period-range">{label}</span>}
+                            {m.note && <span className="period-note">{m.note}</span>}
+                          </li>
+                        );
+                      })}
+                    </ul>
+                  )}
+                </li>
+              );
+            })}
           </ul>
         </section>
       )}
-- 
cgit v1.2.3

