# 画面設計書 26-カードピン画面

## 概要

本ドキュメントは、Fizzyアプリケーションにおけるカードピン画面の設計を記述したものです。カードピン画面は、カードのピン状態を表示するTurbo Frameコンポーネントです。

### 本画面の処理概要

**業務上の目的・背景**：タスク管理において、重要なカードや頻繁に参照するカードを素早くアクセスできるようにするため、個人のピン留めリストに追加する機能です。ピン留めしたカードは「マイピン」画面から一覧で確認でき、即座にアクセスできます。この画面はピン状態の表示と、ピン/解除ボタンを提供します。

**画面へのアクセス方法**：カード詳細画面のピンボタンエリアに表示されます。URLは `/cards/:card_id/pin` ですが、通常はTurbo Frameとしてカード詳細画面に埋め込まれます。

**主要な操作・処理内容**：
1. 現在のピン状態の表示（ピン留め済み/未ピン留め）
2. ピン留め操作（ショートカットキー `Shift+P`）
3. ピン留め解除操作

**画面遷移**：
- 遷移元：カード詳細画面（インライン表示）
- 遷移先：なし（Turbo Streamで画面更新）

**権限による表示制御**：ログインユーザーのみアクセス可能です。ピン留めは個人単位で管理され、他のユーザーのピン状態には影響しません。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 33 | カードをピン留め | 主機能 | カードのピン状態を表示 |
| 34 | カードのピン留め解除 | 補助機能 | ピン留めを解除 |

## 画面種別

状態表示/操作（インライン）

## URL/ルーティング

| HTTPメソッド | URL | コントローラー#アクション |
|-------------|-----|------------------------|
| GET | `/cards/:card_id/pin` | `cards/pins#show` |
| POST | `/cards/:card_id/pin` | `cards/pins#create` |
| DELETE | `/cards/:card_id/pin` | `cards/pins#destroy` |

## 入出力項目

本画面に入力項目はありません。操作はボタンクリックのみです。

## 表示項目

| 項目名 | データソース | 説明 |
|-------|-------------|------|
| ピンボタン | card.pinned_by?(Current.user) | ピン状態に応じてアイコンと動作が切り替わる |
| ツールチップ | 固定文字列 | 「Pin this card」または「Unpin this card」 |
| ショートカットキー | 固定文字列 | shift+p |

## イベント仕様

### 1-ピン留めボタンクリック（未ピン時）

ピン留めされていないカードでボタンをクリックすると、カードがピン留めされます。

**処理内容**：
1. `card_pin_path`へPOSTリクエスト送信
2. `@card.pin_by(Current.user)`でピンを作成
3. マイピントレイにブロードキャスト
4. Turbo Streamでボタン表示を更新

### 2-ピン解除ボタンクリック（ピン済み時）

ピン留めされているカードでボタンをクリックすると、ピンが解除されます。

**処理内容**：
1. `card_pin_path`へDELETEリクエスト送信
2. `@card.unpin_by(Current.user)`でピンを削除
3. マイピントレイからブロードキャスト削除
4. Turbo Streamでボタン表示を更新

### 3-ショートカットキー（Shift+P）

`Shift+P`キーを押すと、現在表示しているカードのピン状態がトグルされます。

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面表示 | pins | SELECT | ユーザーのピン状態を取得 |
| ピン留め | pins | INSERT | ピンレコードの作成 |
| ピン解除 | pins | DELETE | ピンレコードの削除 |

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

#### pins

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | - | card_id, user_id | ピン状態の確認 |
| INSERT | account_id | user.account_id | 自動設定 |
| INSERT | card_id | @card.id | 対象カード |
| INSERT | user_id | Current.user.id | ピンしたユーザー |
| DELETE | - | card_id, user_id | ピンの削除 |

## メッセージ仕様

本画面では明示的なメッセージ表示はありません。ピン状態の変更は即座にアイコンに反映されます。

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| カードが存在しない | 404エラーページを表示 |
| カードへのアクセス権限がない | 404エラーページを表示 |

## 備考

- Turbo Frameで読み込まれ、`turbo_frame_tag @card, :pin`でフレームを識別します
- ETTagによるキャッシュ制御が実装されています（`fresh_when etag: @card.pin_for(Current.user) || "none"`）
- ピン/解除時にAction Cableでマイピントレイにブロードキャストされます
- `hotkey`コントローラーでショートカットキー（Shift+P）が実装されています
- `tooltip`コントローラーでホバー時にツールチップが表示されます
- ピン済み時はボタンに`btn--reversed`クラスが適用され、視覚的に区別されます
- ブロードキャスト先は`[ Current.user, :pins_tray ]`チャンネルです

---

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

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

### 推奨読解順序

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

まず、ピン機能に関連するモデル構造を理解することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | pin.rb | `app/models/pin.rb` | Pinモデル、belongs_to関係、orderedスコープ |
| 1-2 | schema.rb | `db/schema.rb` | pinsテーブル（406-416行目） |

**読解のコツ**: Pinモデルはシンプルな構造で、card_idとuser_idの組み合わせでユニーク制約があります。

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

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

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

**主要処理フロー**:
1. **4-6行目**: `show` - ETTagでピン状態をキャッシュ
2. **8-16行目**: `create` - ピン作成とブロードキャスト
3. **18-27行目**: `destroy` - ピン削除とブロードキャスト
4. **31-40行目**: ブロードキャスト処理

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | show.html.erb | `app/views/cards/pins/show.html.erb` | Turbo Frame定義 |
| 3-2 | _pin_button.html.erb | `app/views/cards/pins/_pin_button.html.erb` | ピンボタンのUI |

**主要処理フロー**:
- **1行目**: `turbo_frame_tag @card, :pin` - Turbo Frameの定義
- **2行目**: パーシャルをレンダリング
- パーシャル内でピン状態に応じて異なるボタンを表示

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

```
GET /cards/:card_id/pin
    |
    +-- Cards::PinsController#show
           |
           +-- CardScoped#set_card (before_action)
           |      +-- Current.user.accessible_cards.find_by!
           |
           +-- fresh_when etag: @card.pin_for(Current.user)
           |
           +-- render 'cards/pins/show'
                  +-- render 'cards/pins/_pin_button'

POST /cards/:card_id/pin
    |
    +-- Cards::PinsController#create
           |
           +-- @card.pin_by(Current.user)
           +-- broadcast_add_pin_to_tray
           |      +-- @pin.broadcast_prepend_to [ Current.user, :pins_tray ]
           +-- render_pin_button_replacement

DELETE /cards/:card_id/pin
    |
    +-- Cards::PinsController#destroy
           |
           +-- @card.unpin_by(Current.user)
           +-- broadcast_remove_pin_from_tray
           |      +-- @pin.broadcast_remove_to [ Current.user, :pins_tray ]
           +-- render_pin_button_replacement
```

### データフロー図

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

ボタン操作               PinsController                    Turbo Stream
    |                         |                              + Broadcast
    +-- POST ────────────▶ create ─────────────────────▶ pins INSERT
    |                         +-- broadcast_add_pin         Action Cable
    |
    +-- DELETE ──────────▶ destroy ────────────────────▶ pins DELETE
                              +-- broadcast_remove_pin      Action Cable
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| pins_controller.rb | `app/controllers/cards/pins_controller.rb` | ソース | ピン機能コントローラー |
| card_scoped.rb | `app/controllers/concerns/card_scoped.rb` | ソース | カード取得の共通処理 |
| show.html.erb | `app/views/cards/pins/show.html.erb` | テンプレート | ピン状態表示のTurbo Frame |
| _pin_button.html.erb | `app/views/cards/pins/_pin_button.html.erb` | テンプレート | ピンボタンパーシャル |
| pin.rb | `app/models/pin.rb` | ソース | Pinモデル |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義 |
| schema.rb | `db/schema.rb` | 設定 | データベーススキーマ |
