# 機能設計書 17-カード一覧表示

## 概要

本ドキュメントは、フィルター条件に基づいてカードの一覧を表示する機能の設計を記載する。

### 本機能の処理概要

本機能は、複数の条件でフィルタリングされたカード一覧を取得し、画面に表示する機能である。ボード、担当者、タグ、作成者、ステータスなど多様な条件を組み合わせて、必要なカードのみを抽出できる。

**業務上の目的・背景**：大量のカードが存在するプロジェクトでは、全カードを一覧表示することは非効率である。ユーザーは自分に関係するカード、特定のボードのカード、特定のタグが付いたカードなど、必要な情報だけを素早く確認したい。本機能により、柔軟なフィルタリングと検索が可能となる。

**機能の利用シーン**：マイメニュー画面、ボード詳細画面、検索結果画面など多くの場面でカード一覧が使用される。保存済みフィルターを適用したり、URLパラメータで条件を指定したりしてカードを絞り込む。

**主要な処理内容**：
1. URLパラメータまたは保存済みフィルターから条件を取得
2. Filterオブジェクトを構築
3. フィルター条件に基づいてカードをクエリ
4. ページネーション処理
5. プリロードと画面表示

**関連システム・外部連携**：Turbo Framesによる部分更新に対応。ページネーションはKaminariまたはカスタム実装。

**権限による制御**：ユーザーがアクセス可能なカード（accessible_cards）のみが対象。ボードのアクセス権限に基づく。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 18 | カード一覧画面 | 主画面 | フィルター条件に基づいてカードの一覧を表示 |
| 10 | ボード詳細画面 | 参照画面 | ボード内のカード一覧を表示 |
| 32 | 検索画面 | 参照画面 | 検索結果としてカード一覧を表示 |

## 機能種別

CRUD操作（Read） / データ取得・フィルタリング処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| filter_id | UUID | No | 保存済みフィルターのID | 存在するフィルターであること |
| board_ids[] | Array[UUID] | No | 対象ボードのID配列 | アクセス可能なボードであること |
| assignee_ids[] | Array[UUID] | No | 担当者のID配列 | - |
| creator_ids[] | Array[UUID] | No | 作成者のID配列 | - |
| tag_ids[] | Array[UUID] | No | タグのID配列 | - |
| indexed_by | String | No | インデックス種別（stalled/postponing_soon/closed/not_now/golden/draft） | 指定値のみ |
| sorted_by | String | No | ソート順（newest/oldest/latest） | 指定値のみ |
| assignment_status | String | No | アサイン状態（unassigned等） | - |
| terms[] | Array[String] | No | 検索キーワード配列 | - |

### 入力データソース

- URLパラメータ: フィルター条件各種
- セッション: 認証済みユーザー情報（Current.user）
- データベース: 保存済みフィルター（filter_id指定時）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| cards | Array[Card] | フィルター条件に合致するカードの配列 |
| page | Page | ページネーション情報 |
| filter | Filter | 適用されているフィルター |
| user_filtering | User::Filtering | ユーザーフィルタリング状態 |

### 出力先

- 画面表示: HTML/Turbo Frames
- キャッシュキー: filter.cache_key（条件付きキャッシュ）

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信（GET /cards）
   └─ FilterScopedによりフィルター条件を設定

2. フィルター構築
   └─ filter_id指定時: 保存済みフィルターを取得
   └─ パラメータ指定時: 新規Filterオブジェクトを構築

3. カードクエリ生成
   └─ @filter.cardsで条件付きクエリを構築
   └─ accessible_cards.preloaded.published をベースに各条件を適用

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

5. レスポンス生成
   └─ HTML: カード一覧をレンダリング
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B{filter_id指定?}
    B -->|Yes| C[保存済みフィルター取得]
    B -->|No| D[パラメータからフィルター構築]
    C --> E[FilterScoped: set_filter]
    D --> E
    E --> F[set_user_filtering]
    F --> G[@filter.cards呼び出し]
    G --> H[accessible_cards.preloaded.published]
    H --> I[indexed_by適用]
    I --> J[sorted_by適用]
    J --> K[その他条件適用]
    K --> L[set_page_and_extract_portion_from]
    L --> M[ビューレンダリング]
    M --> N[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 公開カードのみ | 下書きカードは表示されない（publishedスコープ） | 常時 |
| BR-02 | アクセス可能カードのみ | ユーザーがアクセス権を持つボードのカードのみ | 常時 |
| BR-03 | Not Nowカード除外 | 通常は延期カードを除外（indexed_by: not_now以外） | デフォルト |
| BR-04 | クローズカード除外 | 通常はクローズカードを除外（indexed_by: closed以外） | デフォルト |
| BR-05 | ゴールデン優先 | ゴールデンマーク付きカードが上位表示（カラム詳細時） | カラム詳細表示時 |
| BR-06 | 最新順デフォルト | ソート未指定時はlast_active_at降順 | デフォルト |

### 計算ロジック

```ruby
# Filter#cardsメソッドの条件適用ロジック
result = creator.accessible_cards.preloaded.published
result = result.indexed_by(indexed_by)      # ステータスフィルター
result = result.sorted_by(sorted_by)        # ソート順
result = result.where(id: card_ids) if card_ids.present?
result = result.where.missing(:not_now) unless include_not_now_cards?
result = result.open unless include_closed_cards?
result = result.unassigned if assignment_status.unassigned?
result = result.assigned_to(assignees.ids) if assignees.present?
# ... その他条件
result.distinct
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| カード取得 | cards | SELECT | フィルター条件でカード一覧取得 |
| 関連データ取得 | 複数テーブル | SELECT | preloadedによる関連データ取得 |

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

#### cards

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | 複数条件の組み合わせ | accessible_cards経由 |
| JOIN | accesses | ユーザーアクセス権の確認 | - |
| JOIN | board_publications | 公開ボードも含める場合 | - |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 404 | Not Found | 存在しないfilter_idが指定 | RecordNotFound例外 |
| 401 | Unauthorized | 未認証ユーザー | ログイン画面へリダイレクト |

### リトライ仕様

該当なし（読み取り専用操作）

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

該当なし（SELECT文のみ）

## パフォーマンス要件

- レスポンス時間: 500ms以内
- preloadedスコープで関連データをN+1なく取得
- インデックス: account_id + last_active_at + statusの複合インデックス

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

- 認証: ログイン済みユーザーのみアクセス可能
- 認可: accessible_cardsによりアクセス可能なカードのみ表示

## 備考

- フィルターはcacheableな場合のみキャッシュキーが有効
- User::Filteringはフィルター状態のUI表示用ヘルパー

---

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

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

### 推奨読解順序

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

フィルターの構造とカードへの条件適用を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | filter.rb | `app/models/filter.rb` | Filterモデルの構造とcardsメソッド |
| 1-2 | params.rb | `app/models/filter/params.rb` | PERMITTED_PARAMSとパラメータ処理 |
| 1-3 | card.rb | `app/models/card.rb` | indexed_by/sorted_byスコープ |

**読解のコツ**: Filter#cardsメソッドは複数の条件を順次適用するチェーン形式。各条件は存在する場合のみ適用される。

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

リクエストを受け付けるコントローラーを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | cards_controller.rb | `app/controllers/cards_controller.rb` | indexアクションの実装 |
| 2-2 | filter_scoped.rb | `app/controllers/concerns/filter_scoped.rb` | フィルター設定処理 |

**主要処理フロー**:
1. **6行目（filter_scoped.rb）**: set_filterでフィルターを構築
2. **7行目（filter_scoped.rb）**: set_user_filteringでUI用状態を設定
3. **10行目（cards_controller.rb）**: set_page_and_extract_portion_fromでページング

#### Step 3: フィルター条件の適用を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | filter.rb | `app/models/filter.rb` | cardsメソッド（19-41行目）の条件適用ロジック |

**主要処理フロー**:
- **21行目**: accessible_cards.preloaded.publishedをベースクエリに
- **22-34行目**: 各フィルター条件を順次適用
- **39行目**: distinctで重複排除

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | index.html.erb | `app/views/cards/index.html.erb` | カード一覧の表示構造 |

**主要処理フロー**:
- **20-29行目**: Turbo Frameでカードコンテナをラップ
- **23-25行目**: ページネーション付きでカードプレビューを表示

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

```
CardsController#index
    │
    ├─ FilterScoped (concern)
    │      ├─ set_filter
    │      │      ├─ filter_id指定時: filters.find
    │      │      └─ パラメータ指定時: filters.from_params
    │      │
    │      └─ set_user_filtering
    │             └─ User::Filtering.new
    │
    └─ set_page_and_extract_portion_from @filter.cards
           │
           └─ Filter#cards
                  ├─ accessible_cards.preloaded.published
                  ├─ indexed_by(indexed_by)
                  ├─ sorted_by(sorted_by)
                  ├─ where(board: boards.ids)
                  ├─ assigned_to(assignees.ids)
                  └─ ...その他条件
```

### データフロー図

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

GET /cards?board_ids[]=xxx&tag_ids[]=yyy
         │
         ▼
┌─────────────────┐
│ filter_params抽出│ ───▶ PERMITTED_PARAMSでフィルタ
└─────────────────┘
         │
         ▼
┌─────────────────┐
│ Filter構築      │ ───▶ from_paramsまたはfind
└─────────────────┘
         │
         ▼
┌─────────────────┐
│ @filter.cards   │ ───▶ 条件付きクエリ生成
└─────────────────┘
         │
         ▼
┌─────────────────┐
│ ページネーション │ ───▶ @page生成
└─────────────────┘
         │
         ▼
┌─────────────────┐
│ ビューレンダリング│ ───▶ cards/index.html.erb
└─────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| cards_controller.rb | `app/controllers/cards_controller.rb` | コントローラー | カード一覧リクエストの処理 |
| filter_scoped.rb | `app/controllers/concerns/filter_scoped.rb` | concern | フィルター設定 |
| filter.rb | `app/models/filter.rb` | モデル | フィルターモデル、cardsメソッド |
| params.rb | `app/models/filter/params.rb` | concern | パラメータ処理 |
| card.rb | `app/models/card.rb` | モデル | カードモデル、スコープ定義 |
| index.html.erb | `app/views/cards/index.html.erb` | ビュー | カード一覧表示 |
| schema.rb | `db/schema.rb` | スキーマ | cardsテーブル定義（202-217行目） |
