Design
Context¶
本プロジェクト(Study Log)は React 19 + Vite + Tailwind CSS 4.1 で構築された静的サイトで、勉強会の学習記録を閲覧する。現在、トップページ(DashboardPage)に勉強会一覧とメンバー一覧が表示されており、メンバーをクリックするとメンバー詳細ページ(MemberDetailPage)へ遷移できる。しかし、勉強会一覧はクリック不可の静的表示であり、勉強会を軸とした閲覧導線が存在しない。
メンバー詳細ページは PR#3 でリデザインされ、「勉強会別サマリーカード+アコーディオン展開でセッション履歴を表示」というUIパターンが確立されている。このパターンを踏襲し、勉強会詳細ページでは「セッション別サマリーカード+アコーディオン展開で参加者詳細を表示」する構造を採用する。
Goals / Non-Goals¶
Goals:
- 勉強会詳細ページ(
StudyGroupDetailPage)を新規作成し、勉強会のセッション一覧と参加者詳細を閲覧可能にする - トップページの
StudyGroupListをリンク化し、勉強会詳細ページへの遷移導線を確立する - メンバー詳細ページと一貫したUIパターン(サマリーカード+アコーディオン展開)を採用する
- 既存の
DataFetcherAPIをそのまま活用し、新規API追加を不要にする
Non-Goals:
- データモデルやAPI(
index.json、セッションJSON)の構造変更 - 管理画面の変更
- メンバー詳細ページの変更
- 参加者名からメンバー詳細ページへのリンク追加(将来的に検討可能だが今回はスコープ外)
Decisions¶
1. ページ構造: MemberDetailPageのパターンを踏襲¶
選択: MemberDetailPageと対称的な構造を採用する
- ヘッダーカード: 勉強会名、総開催回数、合計学習時間
- セッション別サマリーカード(アコーディオン): 日付、参加人数、合計学習時間
- アコーディオン展開: 参加者テーブル(名前、学習時間)
理由: メンバーページでは「メンバー → 勉強会別 → セッション」の階層構造を持つ。勉強会ページでは「勉強会 → セッション別 → 参加者」の対称的な階層構造を持たせることで、アプリ全体のUI一貫性が保たれる。
代替案: セッション一覧をテーブル形式で表示する案もあるが、アコーディオンパターンの方がメンバーページとの一貫性が高い。
2. データ取得フロー: クライアントサイド集約¶
選択: index.json から対象勉強会の sessionIds を取得し、各セッションJSONを Promise.all で並列取得する。参加者情報は members 配列から名前を逆引きする。
1. DataFetcher.fetchIndex() → studyGroups から対象を取得
2. sessionIds で DataFetcher.fetchSession() を並列呼び出し
3. 各セッションの attendances から参加者一覧を構築
4. members 配列から memberId → name のマッピングで名前を解決
理由: MemberDetailPage と同じデータ取得パターンを使うことで、実装の一貫性を保ち、DataFetcher への変更を不要にする。セッション数は勉強会あたり最大10件程度のため、パフォーマンス上の問題はない。
3. ルーティング: #/study-groups/:studyGroupId¶
選択: ハッシュルーティングで #/study-groups/:studyGroupId パスを追加する。
理由: 既存のルーティング構造(#/members/:memberId)に合わせたリソース型URLパターン。createHashRouter のルート配列に追加するだけで対応可能。
4. StudyGroupListのリンク化¶
選択: 既存の <div> を <a href="#/study-groups/{id}"> でラップし、react-router-dom の Link コンポーネントは使わない。
理由: MemberList コンポーネントが <a href="#/members/{id}"> を使っている既存パターンに合わせる。StudyGroupList は createHashRouter 外でレンダリングされるため、Link コンポーネントではなくハッシュリンクを使用する。
5. アコーディオン状態管理¶
選択: useState で expandedSessions: Set<sessionId> を管理する。セッションが1件のみの場合はデフォルト展開する。
理由: MemberDetailPage の expandedGroups: Set と同じパターン。UXの一貫性を保つ。
Risks / Trade-offs¶
- セッション数の増加によるパフォーマンス: 勉強会のセッション数が増えると、並列フェッチの数が増加する → 現状は最大10件程度であり問題なし。将来的にセッション数が大幅に増えた場合は、index.jsonにセッションサマリーを含めることで対応可能
- メンバー名の解決: セッションJSONには
memberIdのみ含まれるため、名前解決にindex.jsonのmembers配列を使用する → index.json はすでに取得済みのためオーバーヘッドなし - 参加者テーブルでのメンバー詳細リンク: 参加者名にメンバー詳細ページへのリンクを付けると利便性が向上するが、今回はスコープ外とする → 将来のIssueとして検討可能