# 機能設計書 23-カードプレビュー

## 概要

本ドキュメントは、Fizzyアプリケーションにおけるカードプレビュー機能の設計を記載するものです。

### 本機能の処理概要

カードプレビュー機能は、カード一覧をプレビュー形式で表示する機能です。フィルター条件に基づいてカードを取得し、Turbo Streamを使用して動的に画面を更新します。

**業務上の目的・背景**：カンバンボードでは、カードの概要を素早く把握することが重要です。プレビュー機能により、カードの詳細画面に遷移することなく、一覧画面上でカードの主要情報（タイトル、担当者、ステップ進捗など）を確認できます。これにより、チームの作業状況を効率的に把握できます。

**機能の利用シーン**：
- ボード画面でカード一覧をプレビュー表示する場合
- フィルター適用後のカード一覧を動的に更新する場合
- ページネーションでカードを追加読み込みする場合

**主要な処理内容**：
1. フィルター条件の解析とカードの取得
2. ページネーション処理
3. Turbo Streamによるプレビュー一覧の描画

**関連システム・外部連携**：特になし

**権限による制御**：ユーザーがアクセス可能なボード・カードのみ表示されます。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 18 | カード一覧画面 | 主画面 | カードプレビューの表示 |
| 10 | ボード詳細画面 | 参照画面 | ボード上のカードプレビュー |

## 機能種別

データ取得 / 表示処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| board_id | UUID | No | フィルター対象のボードID | 存在するボードであること |
| column_id | UUID | No | フィルター対象のカラムID | 存在するカラムであること |
| user_id | UUID | No | フィルター対象のユーザーID | 存在するユーザーであること |
| tag_ids | Array | No | フィルター対象のタグIDリスト | 存在するタグであること |
| index | String | No | カードのインデックス種別（stalled, postponing_soon, closed等） | 有効なインデックス種別 |
| sort | String | No | ソート種別（newest, oldest, latest） | 有効なソート種別 |
| page | Integer | No | ページ番号 | 1以上の整数 |

### 入力データソース

- URL クエリパラメータからフィルター条件を取得
- Filter モデルによるフィルター条件の解析

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| cards | Array | プレビュー表示用のカード一覧 |
| page | Object | ページネーション情報 |

### 出力先

- Turbo Stream レスポンスとして画面の一部を動的更新

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信（GET /cards/previews）
   └─ クエリパラメータからフィルター条件を取得

2. フィルター設定
   └─ FilterScoped concernによりフィルターオブジェクトを生成

3. カード取得
   └─ @filter.cards でフィルター条件に基づくカードを取得

4. ページネーション処理
   └─ set_page_and_extract_portion_from でページ分割

5. レスポンス返却
   └─ Turbo Stream形式でプレビュー一覧を描画
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B[フィルター条件解析]
    B --> C[フィルターオブジェクト生成]
    C --> D[カード取得]
    D --> E[ページネーション処理]
    E --> F[Turbo Stream レスポンス]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-23-01 | アクセス制御 | ユーザーがアクセス可能なカードのみ表示 | すべての表示 |
| BR-23-02 | デフォルトソート | ソートが指定されない場合は last_active_at の降順 | ソート未指定時 |
| BR-23-03 | ページサイズ | 1ページあたりの表示件数はデフォルト設定に従う | ページネーション時 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| カード取得 | cards | SELECT | フィルター条件に基づく取得 |
| 関連データ取得 | columns, tags, steps, users | SELECT | プレビュー表示用の関連データ |

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

#### cards

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | 全カラム | フィルター条件による絞り込み | preloadedスコープで関連データも取得 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 404 | RecordNotFound | 存在しないフィルターIDを指定した場合 | 適切なエラー画面を表示 |
| 401 | Unauthorized | 未認証でアクセスした場合 | ログイン画面へリダイレクト |

### リトライ仕様

読み取り専用のためリトライ不要

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

読み取り専用のためトランザクション管理不要

## パフォーマンス要件

- プレビュー一覧の取得は500ms以内に完了すること
- preloaded スコープでN+1問題を回避

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

- 認証：ログインユーザーのみアクセス可能
- 認可：ユーザーがアクセス可能なボード・カードのみ表示
- データ保護：アクセス権のないカードは取得対象から除外

## 備考

- Turbo Stream を使用した動的更新に対応
- フィルター条件はURL パラメータで指定可能

---

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

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

### 推奨読解順序

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

フィルターの仕組みとカード取得のスコープを理解することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | filter.rb | `app/models/filter.rb` | Filterモデルの定義、cardsメソッドによるカード取得 |
| 1-2 | card.rb | `app/models/card.rb` | スコープ定義（preloaded, sorted_by, indexed_by） |

**読解のコツ**: Filter モデルの `cards` メソッドが実際のフィルター処理を行います。Card モデルのスコープと組み合わせて理解してください。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | previews_controller.rb | `app/controllers/cards/previews_controller.rb` | indexアクションがエントリーポイント |
| 2-2 | filter_scoped.rb | `app/controllers/concerns/filter_scoped.rb` | フィルター処理の共通化 |

**主要処理フロー**:
1. **4行目**: `before_action :set_filter` - フィルターオブジェクトの生成
2. **6-8行目**: `index`アクション - カード取得とページネーション

#### Step 3: フィルター処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | filter_scoped.rb | `app/controllers/concerns/filter_scoped.rb` | set_filterメソッドでフィルターを生成 |

**主要処理フロー**:
- **10-16行目**: `set_filter` - filter_idまたはパラメータからフィルターを生成
- **18-20行目**: `filter_params` - 許可されたパラメータの取得

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

```
Cards::PreviewsController#index
    │
    ├─ FilterScoped#set_filter (before_action)
    │      ├─ params[:filter_id] あり → Current.user.filters.find
    │      └─ params[:filter_id] なし → Filter.from_params
    │
    └─ set_page_and_extract_portion_from @filter.cards
           ├─ Filter#cards
           │      └─ フィルター条件によるカード絞り込み
           └─ ページネーション処理
```

### データフロー図

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

GET /cards/previews ──▶ PreviewsController#index ──▶ Turbo Stream
  │                          │
  │ filter params            ├─ set_filter
  │                          │    └─ Filter (生成)
  │                          │
  │                          └─ @filter.cards
  │                               └─ cards (SELECT)
  │                                    └─ preloaded
  │
  └─ cards, columns, tags, steps, users (SELECT)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| previews_controller.rb | `app/controllers/cards/previews_controller.rb` | コントローラー | プレビューリクエストの処理 |
| filter_scoped.rb | `app/controllers/concerns/filter_scoped.rb` | コントローラー(concern) | フィルター処理の共通化 |
| filter.rb | `app/models/filter.rb` | モデル | フィルター条件の管理とカード取得 |
| card.rb | `app/models/card.rb` | モデル | カードモデル定義とスコープ |
| index.turbo_stream.erb | `app/views/cards/previews/index.turbo_stream.erb` | ビュー | Turbo Streamレスポンス |
| _preview.html.erb | `app/views/cards/display/_preview.html.erb` | ビュー | カードプレビューのパーシャル |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義（72-74行目） |
