# 画面設計書 73-カード検索プロンプト画面

## 概要

本ドキュメントは、Fizzyアプリケーションにおける「カード検索プロンプト画面」の設計仕様を記述したものである。

### 本画面の処理概要

カード検索プロンプト画面は、テキスト入力フィールドでカードを検索・選択するためのオートコンプリート機能を提供するAPI画面である。主にリッチテキストエディタ内でカードをメンション（リンク）する際に使用され、ユーザーが入力したフィルタ文字列に基づいて候補カードを表示する。

**業務上の目的・背景**：カード間の関連性を表現するため、コメントやカード説明文内で他のカードへのリンクを簡単に挿入できることが重要である。本プロンプト画面は、「#」に続けてカード番号やタイトルの一部を入力することで、候補カードをリアルタイムに絞り込み、選択できるインターフェースを提供する。これにより、ユーザーはカード番号を正確に記憶していなくても、素早く目的のカードにリンクを張ることができる。

**画面へのアクセス方法**：リッチテキストエディタ（Lexxyエディタ）内で「#」を入力するとプロンプトが表示される。URLは `/prompts/cards` であり、Ajax（Fetch API）経由で非同期に呼び出される。

**主要な操作・処理内容**：
1. フィルタ文字列が指定された場合、全文検索によるカード候補の取得（最大10件）
2. カード番号による完全一致検索（優先表示）
3. フィルタ文字列がない場合、最近のカードを表示
4. カード選択によるリンク挿入

**画面遷移**：
- 遷移元：カード編集画面、コメント入力欄等のリッチテキストエディタ
- 遷移先：選択したカードの詳細画面（リンクとして挿入される）

**権限による表示制御**：認証必須。ユーザーがアクセス可能なカード（accessible_cards）のみが候補として表示される。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 53 | 全文検索 | 主機能 | カード検索用のオートコンプリートプロンプト |

## 画面種別

APIレスポンス画面（オートコンプリートプロンプト）

## URL/ルーティング

| パス | HTTPメソッド | コントローラ#アクション |
|------|-------------|----------------------|
| `/prompts/cards` | GET | `Prompts::CardsController#index` |

## 入出力項目

### 入力項目

| 項目 | パラメータ名 | 必須 | 説明 |
|------|-------------|------|------|
| フィルタ文字列 | filter | 任意 | カード番号またはタイトルによる絞り込み |

### 出力項目

| 項目 | 説明 |
|------|------|
| カード候補リスト | lexxy-prompt-item要素のリスト |

## 表示項目

### カード候補アイテム（lexxy-prompt-item）

| 項目 | 説明 |
|------|------|
| メニュー表示（menu） | `#番号 タイトル` 形式 |
| エディタ挿入時表示（editor） | カード詳細画面へのリンク |

## イベント仕様

### 1-フィルタ入力

ユーザーがフィルタ文字列を入力すると、Ajax経由でAPI呼び出しが行われる。

- リクエスト: `GET /prompts/cards?filter=検索文字列`
- レスポンス: HTMLフラグメント（lexxy-prompt-itemのリスト）

### 2-カード選択

候補リストからカードを選択すると、エディタにカードリンクが挿入される。

- 挿入形式: `#番号 タイトル` のリンク形式
- リンク先: `/cards/:number`

## データベース更新仕様

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

本画面は読み取り専用であり、データベースの更新は発生しない。

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| カード検索 | cards, search_records | SELECT | フィルタに基づくカード検索 |

### テーブル別更新項目詳細

#### cards（参照のみ）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | number, title | published = true AND accessible | アクセス可能な公開カードのみ |

## メッセージ仕様

本画面では特定のメッセージ表示はない（候補が0件の場合は空のレスポンス）。

## 例外処理

| 条件 | 処理内容 |
|------|---------|
| 未認証アクセス | ログイン画面へリダイレクト |

## 備考

- レイアウトなし（`render layout: false`）
- ETagベースのキャッシュを使用（`stale? etag: @cards`）
- 検索結果は最大10件（MAX_RESULTS = 10）
- カード番号による完全一致検索が優先され、結果の先頭に配置される
- Lexxyはカスタムリッチテキストエディタ（Trix拡張）

---

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

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

### 推奨読解順序

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

カード検索に関わるモデルの検索機能を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | card.rb | `app/models/card.rb` | Searchableモジュールのinclude、latestスコープの確認 |
| 1-2 | searchable.rb | `app/models/card/searchable.rb` | mentioningスコープの実装（7-10行目） |

**読解のコツ**: `mentioning` スコープは全文検索を行う。`Search::Record` を使用してカード検索を実行している。

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

処理の起点となるコントローラーを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | cards_controller.rb | `app/controllers/prompts/cards_controller.rb` | indexアクションの実装 |

**主要処理フロー**:
1. **2行目**: `MAX_RESULTS = 10` - 最大結果件数の定義
2. **4-13行目**: `index` アクション
   - フィルタありの場合: `prepending_exact_matches_by_id(search_cards)` を実行
   - フィルタなしの場合: `published_cards.latest` を返却
3. **21-26行目**: `search_cards` - mentioningスコープによる検索、最大10件
4. **32-38行目**: `prepending_exact_matches_by_id` - カード番号での完全一致を先頭に配置

#### Step 3: ビューレイヤーを理解する

テンプレートの構造を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.html.erb | `app/views/prompts/cards/index.html.erb` | カードコレクションのレンダリング |
| 3-2 | _card.html.erb | `app/views/prompts/cards/_card.html.erb` | 個別カードの表示形式 |

**主要処理フロー**:
- **index.html.erb 1行目**: `render partial: "prompts/cards/card", collection: @cards`
- **_card.html.erb 1-8行目**: `lexxy-prompt-item` 要素の生成
  - menu: `#番号 タイトル`
  - editor: カード詳細へのリンク

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

```
Prompts::CardsController#index
    │
    ├─ filter_param.present?
    │      │
    │      ├─ YES: prepending_exact_matches_by_id(search_cards)
    │      │         │
    │      │         ├─ search_cards
    │      │         │      ├─ published_cards
    │      │         │      │      └─ Current.user.accessible_cards.published
    │      │         │      │
    │      │         │      └─ mentioning(filter, user:)
    │      │         │             └─ Search::Record.for_query()
    │      │         │
    │      │         └─ card_by_id = accessible_cards.find_by(number:)
    │      │                └─ [card_by_id] + cards (番号一致を先頭に)
    │      │
    │      └─ NO: published_cards.latest
    │
    └─ stale? etag: @cards
           │
           └─ render layout: false
                  │
                  └─ index.html.erb
                         └─ _card.html.erb (collection)
```

### データフロー図

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

filter (任意) ────▶ Prompts::CardsController#index
                          │
        ┌─────────────────┴──────────────────┐
        │                                     │
        ▼                                     ▼
  filter.present?                      filter.blank?
        │                                     │
        ▼                                     ▼
  search_cards                        published_cards.latest
  (mentioning検索)                          │
        │                                     │
        ▼                                     │
  prepending_exact_matches_by_id              │
  (番号一致を先頭に)                         │
        │                                     │
        └──────────────┬──────────────────────┘
                       │
                       ▼
                   @cards
                       │
                       ▼
               index.html.erb
                       │
                       ▼
              lexxy-prompt-item (HTML)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| cards_controller.rb | `app/controllers/prompts/cards_controller.rb` | コントローラー | リクエストハンドリング |
| index.html.erb | `app/views/prompts/cards/index.html.erb` | テンプレート | メインビュー |
| _card.html.erb | `app/views/prompts/cards/_card.html.erb` | パーシャル | カード候補アイテム |
| card.rb | `app/models/card.rb` | モデル | カードモデル |
| searchable.rb | `app/models/card/searchable.rb` | Concern | 検索機能（mentioningスコープ） |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義（183行目） |
