# 画面設計書 25-カードタグ付け画面

## 概要

本ドキュメントは、Fizzyアプリケーションにおけるカードタグ付け画面の設計を記述したものです。カードタグ付け画面は、カードにタグを追加または削除するためのポップアップ形式の画面です。

### 本画面の処理概要

**業務上の目的・背景**：タスク管理において、カードを分類・整理するためにタグ（ラベル）を使用します。タグにより、特定のカテゴリーや優先度、プロジェクトフェーズなどでカードをグループ化し、フィルタリングや検索を容易にします。この画面では、既存のタグを選択するだけでなく、新しいタグをその場で作成することも可能です。

**画面へのアクセス方法**：カード詳細画面からタグボタン（ショートカットキー `t`）を押すと、Turbo Frameとしてこの画面がポップアップ表示されます。URLは `/cards/:card_id/taggings/new` です。

**主要な操作・処理内容**：
1. カードに付けられているタグの表示（チェック済み状態）
2. アカウント内の既存タグ一覧の表示
3. タグ名によるフィルター検索
4. 既存タグの選択/解除（トグル動作）
5. 新しいタグの作成と同時付与

**画面遷移**：
- 遷移元：カード詳細画面（タグボタンクリック）
- 遷移先：なし（Turbo Streamで画面更新、ポップアップは継続表示）

**権限による表示制御**：ログインユーザーのみアクセス可能です。カードにアクセス権限があるユーザーのみ操作可能です。タグはアカウント単位で共有されるため、同じアカウントのすべてのタグが表示されます。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 40 | タグ付け | 主機能 | カードにタグを追加/削除 |

## 画面種別

選択/入力（ポップアップ）

## URL/ルーティング

| HTTPメソッド | URL | コントローラー#アクション |
|-------------|-----|------------------------|
| GET | `/cards/:card_id/taggings/new` | `cards/taggings#new` |
| POST | `/cards/:card_id/taggings` | `cards/taggings#create` |

## 入出力項目

| 項目名 | 入出力 | データ型 | 必須 | 説明 |
|-------|-------|---------|-----|------|
| tag_title | 入力 | String | 必須 | タグのタイトル（新規作成時または既存タグ選択時） |

## 表示項目

| 項目名 | データソース | 説明 |
|-------|-------------|------|
| ポップアップタイトル | 固定文字列 | 「Tag this...」 |
| ショートカットキー表示 | 固定文字列 | `t`（タイトル横） |
| タグ入力欄 | - | タグ名の入力/フィルター用 |
| タグ付け済みタグ | @tagged_with | 既にカードに付けられているタグ一覧 |
| 未タグ付けタグ | @tags | まだカードに付けられていないタグ一覧 |
| 新規タグ作成ボタン | 固定文字列 | 「Create tag」 |

## イベント仕様

### 1-タグ選択（クリック/Enter）

既存タグをクリックまたはEnterキーで選択すると、POSTリクエストが送信され、タグ付け/解除がトグルされます。

**処理内容**：
1. `card_taggings_path`へPOSTリクエスト送信（tag_title付き）
2. `@card.toggle_tag_with`でタグ付け/解除を実行
3. Turbo Streamでカード表示を更新

### 2-フィルター入力

入力欄に文字を入力すると、リアルタイムでタグ一覧がフィルタリングされます。

**処理内容**：
1. `filter`コントローラーの`filter`アクションが発火
2. JavaScriptで表示/非表示を切り替え（サーバー通信なし）

### 3-新規タグ作成

入力欄にタグ名を入力し、「Create tag」ボタンをクリックまたはEnterで送信すると、新しいタグが作成され、同時にカードに付与されます。

**処理内容**：
1. フォーム送信で`card_taggings_path`へPOSTリクエスト
2. タグが存在しない場合は新規作成
3. タグをカードに付与
4. Turbo Streamで更新

### 4-キーボードナビゲーション

上下キーでタグ一覧を移動し、Enterで選択できます。

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面表示 | tags | SELECT | アカウント内のタグ一覧を取得 |
| 画面表示 | taggings | SELECT | カードのタグ付け状態を取得 |
| タグ選択（新規タグ） | tags | INSERT | 新しいタグの作成 |
| タグ選択 | taggings | INSERT/DELETE | タグ付けの追加または削除 |
| タグ選択 | cards | UPDATE | updated_atの更新（touch） |

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

#### tags

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | account_id | Current.account.id | 自動設定 |
| INSERT | title | params[:tag_title].downcase | 小文字正規化 |

#### taggings

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | account_id | card.account_id | 自動設定 |
| INSERT | card_id | @card.id | 対象カード |
| INSERT | tag_id | 選択/作成されたタグのID | |
| DELETE | - | card_id, tag_id | 既存タグ付けの削除 |

#### cards

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | updated_at | 現在時刻 | taggings belongs_to :card, touch: true |

## メッセージ仕様

本画面では明示的なメッセージ表示はありません。タグ付け/解除は即座にUIに反映されます。

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| カードが存在しない | 404エラーページを表示 |
| カードへのアクセス権限がない | 404エラーページを表示 |
| タグ名が空 | preventEmptySubmitでクライアント側で防止 |

## 備考

- Turbo Frameで読み込まれ、`turbo_frame_tag @card, :tagging`でフレームを識別します
- タグのタイトルは`#`で始まる場合、自動的に削除されます（`sanitized_tag_title_param`）
- タグは小文字に正規化されて保存されます（Tagモデルの`normalizes`）
- ETTagによるキャッシュ制御が実装されています（`fresh_when etag: [ @tags, @card.tags ]`）
- `form`コントローラーで空送信の防止、IME入力中の送信防止が実装されています
- タグは`#`付きのハッシュタグ形式で表示されます（`tag.hashtag`メソッド）
- `navigable-list`コントローラーで`focusOnSelection: false`、`actionableItems: true`が設定されています

---

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

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

### 推奨読解順序

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

まず、タグ関連のモデル構造を理解することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | tag.rb | `app/models/tag.rb` | Tagモデル、normalizes、hashtagメソッド |
| 1-2 | tagging.rb | `app/models/tagging.rb` | Taggingモデル（多対多中間テーブル） |
| 1-3 | schema.rb | `db/schema.rb` | tagsテーブル（757-763行目）、taggingsテーブル（746-755行目） |

**読解のコツ**: TagモデルはCardsと多対多の関係で、Taggingが中間テーブルです。タイトルは小文字に正規化され、`#`で始まることは許可されていません。

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

処理の起点となるコントローラーを特定します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | taggings_controller.rb | `app/controllers/cards/taggings_controller.rb` | new/createアクションの処理 |
| 2-2 | card_scoped.rb | `app/controllers/concerns/card_scoped.rb` | CardScopedモジュール |

**主要処理フロー**:
1. **4-7行目**: `new` - タグ付け済みタグと未タグ付けタグの取得
2. **10-16行目**: `create` - `toggle_tag_with`でタグ付け/解除
3. **20-22行目**: `sanitized_tag_title_param` - タグ名のサニタイズ

#### Step 3: ビューテンプレートを理解する

画面の表示内容を構成するビューファイルを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | new.html.erb | `app/views/cards/taggings/new.html.erb` | メインビュー |
| 3-2 | _tag.html.erb | `app/views/cards/taggings/_tag.html.erb` | タグ項目パーシャル |

**主要処理フロー**:
- **1行目**: `turbo_frame_tag @card, :tagging` - Turbo Frameの定義
- **8-9行目**: ポップアップタイトルとショートカットキー
- **12-18行目**: タグ入力フォーム
- **20-29行目**: タグ一覧と新規作成ボタン

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

```
GET /cards/:card_id/taggings/new
    |
    +-- Cards::TaggingsController#new
           |
           +-- CardScoped#set_card (before_action)
           |      +-- Current.user.accessible_cards.find_by!
           |
           +-- @card.tags.alphabetically (@tagged_with)
           +-- Current.account.tags.alphabetically - @tagged_with (@tags)
           |
           +-- render 'cards/taggings/new'
                  |
                  +-- render 'cards/taggings/_tag' (複数回)

POST /cards/:card_id/taggings
    |
    +-- Cards::TaggingsController#create
           |
           +-- sanitized_tag_title_param
           |      +-- params[:tag_title].strip.gsub(/\A#/, "")
           |
           +-- @card.toggle_tag_with(tag_title)
                  |
                  +-- Tag.find_or_create_by(title:)
                  +-- Tagging.create! or destroy
           |
           +-- respond_to turbo_stream / json
```

### データフロー図

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

リクエスト               TaggingsController                Turbo Stream
card_id ──────────────▶ set_card ─────────────────────▶ @card
tag_title ────────────▶ sanitized_tag_title_param
                              |
                              ▼
                        toggle_tag_with
                              |
                        ┌─────┴─────┐
                        ▼           ▼
                    未タグ付け    タグ付け済み
                        |           |
                        ▼           ▼
                    INSERT       DELETE
                    Tagging      Tagging
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| taggings_controller.rb | `app/controllers/cards/taggings_controller.rb` | ソース | タグ付けコントローラー |
| card_scoped.rb | `app/controllers/concerns/card_scoped.rb` | ソース | カード取得の共通処理 |
| new.html.erb | `app/views/cards/taggings/new.html.erb` | テンプレート | タグ付け画面のビュー |
| _tag.html.erb | `app/views/cards/taggings/_tag.html.erb` | テンプレート | タグ項目パーシャル |
| tag.rb | `app/models/tag.rb` | ソース | Tagモデル |
| tagging.rb | `app/models/tagging.rb` | ソース | Taggingモデル |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義 |
| schema.rb | `db/schema.rb` | 設定 | データベーススキーマ |
