# 機能設計書 62-タイムライン表示

## 概要

本ドキュメントは、Fizzyアプリケーションにおけるユーザーの日次タイムライン（イベント履歴）を表示する機能の設計を詳述する。

### 本機能の処理概要

**業務上の目的・背景**：チーム内で発生したアクティビティ（カードの作成、更新、クローズ、コメント追加など）を時系列で把握できるようにすることで、プロジェクトの進捗状況や他メンバーの作業状況を効率的に確認できる。これはFizzyのホーム画面として機能し、ユーザーがログイン後最初に目にする画面である。

**機能の利用シーン**：ユーザーがログイン後にホーム画面を開いた際、または日々の業務開始時にチーム全体のアクティビティを確認する場面で利用される。複数のボードにまたがるアクティビティを一元的に閲覧できる。

**主要な処理内容**：
1. 現在のユーザーがアクセス可能なボードのイベントを取得
2. 指定された日（デフォルトは当日）のイベントをフィルタリング
3. イベントを「Added（追加）」「Updated（更新）」「Done（完了）」の3カラムに分類
4. ページネーションにより過去のイベントを遡って閲覧可能
5. フィルター機能により特定のボードや作成者でフィルタリング可能

**関連システム・外部連携**：Eventモデル、Boardモデル、Filterモデルと連携。Turbo Framesを使用した非同期ページネーションを実装。

**権限による制御**：ユーザーがアクセス権を持つボードのイベントのみ表示される。accessesテーブルによりボードへのアクセス権が管理されている。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 8 | アクティビティ一覧画面 | 主画面 | ユーザーの日次タイムライン（イベント履歴）を時系列で表示 |
| 40 | ユーザーアクティビティ画面 | 参照画面 | 特定ユーザーのアクティビティを表示 |

## 機能種別

データ参照処理（READ）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| day | String | No | 表示対象日（YYYY-MM-DD形式） | Time.zone.parseで解析可能な形式。無効な場合は404を返す |
| board_ids | Array[UUID] | No | フィルタリング対象のボードID | ユーザーがアクセス可能なボードのみ |
| creator_ids | Array[UUID] | No | フィルタリング対象の作成者ID | アカウント内のユーザーのみ |

### 入力データソース

- URLパラメータ（day）
- FilterScopedのセッション/パラメータからのフィルター情報
- Current.userのボードアクセス情報

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| @day_timeline | User::DayTimeline | 日次タイムラインオブジェクト |
| @day_timeline.events | Event::Relation | 指定日のイベント一覧 |
| @day_timeline.added_column | User::DayTimeline::Column | 追加イベントのカラム |
| @day_timeline.updated_column | User::DayTimeline::Column | 更新イベントのカラム |
| @day_timeline.closed_column | User::DayTimeline::Column | 完了イベントのカラム |
| @day_timeline.next_day | Time | 次にイベントがある日（ページネーション用） |

### 出力先

- 画面表示（HTML）
- HTTPキャッシュヘッダー（fresh_whenによるETagキャッシュ）

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信（GET /events）
   └─ EventsController#index
2. フィルター設定
   └─ FilterScopedによる@filterの設定
3. タイムライン生成
   └─ DayTimelinesScopedによる@day_timelineの設定
   └─ Current.user.timeline_for(day, filter: @filter)
4. イベント取得
   └─ User::DayTimeline#eventsでイベントを取得
   └─ フィルタリング、日付範囲絞り込み、ソート
5. キャッシュ判定
   └─ fresh_when @day_timelineでETagベースのキャッシュ制御
6. ビューレンダリング
   └─ イベントを3カラム（Added/Updated/Done）に分類して表示
```

### フローチャート

```mermaid
flowchart TD
    A[GET /events] --> B[FilterScopedで@filter設定]
    B --> C[DayTimelinesScopedで@day_timeline設定]
    C --> D[day パラメータ解析]
    D -->|無効| E[404 Not Found]
    D -->|有効または未指定| F[Current.user.timeline_for実行]
    F --> G[User::DayTimeline生成]
    G --> H[timelineable_eventsでイベント取得]
    H --> I[フィルター適用]
    I --> J[日付範囲でフィルタリング]
    J --> K[fresh_when でキャッシュ判定]
    K -->|キャッシュ有効| L[304 Not Modified]
    K -->|キャッシュ無効| M[ビューレンダリング]
    M --> N[3カラムに分類して表示]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-62-01 | アクセス可能ボード制限 | ユーザーがアクセス権を持つボードのイベントのみ表示 | 常に |
| BR-62-02 | タイムライン対象アクション | 特定のアクション（card_published, card_closed等）のみ表示 | 常に |
| BR-62-03 | 日付デフォルト | dayパラメータ未指定時は当日を使用 | dayが空の場合 |
| BR-62-04 | カラム分類 | card_published/card_reopened→Added、card_closed→Done、その他→Updated | 常に |

### 計算ロジック

タイムライン対象アクション（TIMELINEABLE_ACTIONS）:
- card_assigned, card_unassigned
- card_published, card_closed, card_reopened
- card_collection_changed, card_board_changed
- card_postponed, card_auto_postponed
- card_triaged, card_sent_back_to_triage
- comment_created

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| イベント取得 | events | SELECT | 条件に合致するイベントを取得 |
| 関連データプリロード | cards, comments, users, boards | SELECT | N+1防止のためのプリロード |

### テーブル別操作詳細

#### events

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | board_id IN (アクセス可能ボードID), action IN (TIMELINEABLE_ACTIONS), created_at IN (day.all_day) | フィルター、日付範囲で絞り込み |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 404 | Not Found | dayパラメータが無効な日付形式 | head :not_found |

### リトライ仕様

読み取り専用のため、リトライ仕様は不要。

## トランザクション仕様

読み取り専用のため、トランザクション制御は不要。

## パフォーマンス要件

- レスポンス時間：1秒以内
- events.preloadedによるN+1クエリ防止
- fresh_whenによるETagキャッシュで不要なレンダリングを回避
- pagination_paginate_on_intersection_valueによる無限スクロール実装

## セキュリティ考慮事項

- 認証必須（デフォルトのApplicationController設定）
- ユーザーがアクセス可能なボードのイベントのみ表示
- Current.userによるユーザー特定

## 備考

- ホーム画面（root）としてルーティングされている
- Turbo Framesによる非同期ページネーションを実装
- カードの放送（broadcasts）によるリアルタイム更新に対応

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

タイムラインの核となるUser::DayTimelineクラスの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | day_timeline.rb | `app/models/user/day_timeline.rb` | タイムラインの主要クラス |
| 1-2 | event.rb | `app/models/event.rb` | イベントモデルの構造 |
| 1-3 | timelined.rb | `app/models/user/timelined.rb` | Userモデルへのタイムライン機能追加 |

**読解のコツ**: User::DayTimelineは純粋なRubyクラス（ActiveRecordではない）で、イベントの取得とカラム分類のロジックをカプセル化している。TIMELINEABLE_ACTIONS定数（49-62行目）でタイムライン対象のアクションを定義している。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | events_controller.rb | `app/controllers/events_controller.rb` | コントローラーのエントリーポイント |
| 2-2 | day_timelines_scoped.rb | `app/controllers/concerns/day_timelines_scoped.rb` | タイムライン生成のconcern |

**主要処理フロー**:
1. **1行目**: EventsControllerはDayTimelinesScopedをinclude
2. **4-6行目**: indexアクションでfresh_whenを使用してキャッシュ制御
3. (concern) **11-13行目**: set_day_timelineでCurrent.user.timeline_forを呼び出し
4. (concern) **15-22行目**: dayメソッドでパラメータから日付を解析

#### Step 3: タイムライン生成ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | day_timeline.rb | `app/models/user/day_timeline.rb` | イベント取得とカラム分類 |

**主要処理フロー**:
- **8-9行目**: initializeでuser, day, filterを受け取る
- **16-17行目**: eventsメソッドでフィルター済みイベントを日付範囲で取得
- **32-41行目**: added_column, updated_column, closed_columnでアクション別に分類
- **64-69行目**: filtered_eventsでフィルター条件を適用
- **72-77行目**: timelineable_eventsでベースクエリを構築

#### Step 4: ビューを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | index.html.erb | `app/views/events/index.html.erb` | メインビュー |
| 4-2 | _day.html.erb | `app/views/events/_day.html.erb` | 日次イベント表示パーシャル |
| 4-3 | _event.html.erb | `app/views/events/_event.html.erb` | 個別イベント表示パーシャル |

**読解のコツ**: index.html.erbではTurbo Framesとpaginationコントローラーを使用して無限スクロールを実装している（22-28行目）。

### プログラム呼び出し階層図

```
EventsController#index
    │
    ├─ DayTimelinesScoped (include)
    │      ├─ FilterScoped (include)
    │      │      └─ @filter設定
    │      └─ set_day_timeline (before_action)
    │             └─ Current.user.timeline_for(day, filter: @filter)
    │                    └─ User::DayTimeline.new(user, day, filter)
    │
    └─ fresh_when @day_timeline
           │
           ├─ @day_timeline.cache_key
           │      └─ events取得
           │             ├─ timelineable_events
           │             │      └─ Event.preloaded.where(board:...).where(action:...)
           │             ├─ filtered_events（creatorフィルター適用）
           │             └─ where(created_at: window)
           │
           └─ ビューレンダリング
                  ├─ added_column
                  ├─ updated_column
                  └─ closed_column
```

### データフロー図

```
[入力]                      [処理]                           [出力]

day (URLパラメータ) ───▶ Time.zone.parse ───▶ @day_timeline.day
                              │
filter (params/session) ───▶ Filter.find_or_create ───▶ @day_timeline.filter
                              │
Current.user.boards ───▶ boards (フィルター適用) ───▶ Event query
                              │
                              ▼
                    events.where(board:, action:, created_at:)
                              │
                              ▼
                    ┌─────────┼─────────┐
                    ▼         ▼         ▼
              added_column  updated_column  closed_column
                    │         │         │
                    └─────────┼─────────┘
                              ▼
                        HTML出力
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| events_controller.rb | `app/controllers/events_controller.rb` | コントローラー | タイムライン表示のエントリーポイント |
| day_timelines_scoped.rb | `app/controllers/concerns/day_timelines_scoped.rb` | concern | タイムライン生成ロジック |
| day_timeline.rb | `app/models/user/day_timeline.rb` | モデル | タイムラインのビジネスロジック |
| timelined.rb | `app/models/user/timelined.rb` | モデルconcern | Userへのタイムライン機能追加 |
| event.rb | `app/models/event.rb` | モデル | イベントエンティティ |
| index.html.erb | `app/views/events/index.html.erb` | ビュー | メイン画面 |
| _day.html.erb | `app/views/events/_day.html.erb` | ビュー（パーシャル） | 日次イベント表示 |
| routes.rb | `config/routes.rb` | 設定 | ルーティング（1行目: root "events#index"） |
