# 画面設計書 72-公開カード詳細画面

## 概要

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

### 本画面の処理概要

公開カード詳細画面は、外部公開されたカードの詳細情報を表示するための画面である。認証なしでアクセス可能であり、公開ボードに含まれるカードの詳細を外部ユーザーが閲覧できる。OGP（Open Graph Protocol）に対応しており、SNSでの共有時にリッチなプレビュー表示が可能。

**業務上の目的・背景**：プロジェクトの進捗状況やタスクの詳細を外部の利害関係者と共有する際、認証なしでカード単位の情報を閲覧可能とすることで、円滑な情報共有を実現する。SNSやチャットツールで共有する際には、OGPタグにより視覚的に訴求力のあるプレビューが表示され、リンク先の内容が一目で分かる。

**画面へのアクセス方法**：公開ボード詳細画面または各カラム画面からカードをクリックして遷移する。URL形式は `/public/boards/:published_key/cards/:card_number` となり、外部から直接URLでアクセスすることも可能。

**主要な操作・処理内容**：
1. カードの詳細情報（タイトル、説明、ステップ）の表示
2. カードのメタ情報（作成者、作成日時、更新日時、担当者）の表示
3. 添付画像がある場合のライトボックス表示
4. カードの状態（完了、Not Now等）の表示

**画面遷移**：
- 遷移元：公開ボード詳細画面、公開ボードカラム画面、公開ボードストリーム画面、公開ボードNot Now画面、公開ボードクローズド画面、外部リンク
- 遷移先：公開ボード詳細画面（戻るリンク）

**権限による表示制御**：認証不要。ただし、ボードが公開設定されていること、カードが公開状態（published）であること、およびアカウントがキャンセルされていないことが条件となる。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 87 | 公開カード閲覧 | 主機能 | 外部公開されたカードの詳細を閲覧 |

## 画面種別

詳細画面

## URL/ルーティング

| パス | HTTPメソッド | コントローラ#アクション |
|------|-------------|----------------------|
| `/public/boards/:board_id/cards/:id` | GET | `Public::CardsController#show` |

## 入出力項目

本画面は読み取り専用であり、ユーザー入力項目はない。

| 項目 | 入出力 | 説明 |
|------|--------|------|
| board_id | 入力（URL） | 公開キー（published_key） |
| id | 入力（URL） | カード番号（number） |

## 表示項目

### メタタグ部（OGP対応）

| 項目 | 説明 |
|------|------|
| og:title | カードタイトル + ボード名 |
| og:description | カード説明文の抜粋（最大200文字） |
| og:image | カード添付画像または既定のapp-icon.png |
| og:url | 公開カードURL |
| twitter:title | og:titleと同様 |
| twitter:description | og:descriptionと同様 |
| twitter:image | og:imageと同様 |
| twitter:card | summary_large_image |

### ヘッダー部

| 項目 | 説明 |
|------|------|
| 戻るリンク | 公開ボード詳細画面へのリンク（ボード名表示） |

### カード詳細部

| 項目 | 説明 |
|------|------|
| ボード名 | カードが属するボードの名前 |
| タグ | カードに付与されたタグ一覧 |
| カードタイトル | カードのタイトル |
| カード説明 | カードの説明文（リッチテキスト） |
| カラム表示 | オープン状態の場合、所属カラム一覧と現在カラムの強調表示 |
| スタンプ | カードの状態スタンプ（完了、Not Now等） |

### ステップ部

| 項目 | 説明 |
|------|------|
| ステップチェックボックス | ステップの完了状態（無効化状態で表示） |
| ステップ内容 | 各ステップの内容テキスト |

### メタ情報部

| 項目 | 説明 |
|------|------|
| 作成者アバター | カード作成者のアバター画像 |
| 作成日時 | カード作成日時（相対時間表示） |
| 作成者名 | カード作成者の名前 |
| 更新日時 | 最終更新日時（相対時間表示） |
| 担当者名 | アサインされたユーザー名（複数可） |
| 担当者アバター | アサインされたユーザーのアバター画像 |

### 背景画像部

| 項目 | 説明 |
|------|------|
| 背景画像 | カードに添付された画像（カードの色設定による装飾） |
| 拡大ボタン | 画像クリックでライトボックス表示 |

### フッター部

| 項目 | 説明 |
|------|------|
| コロフォン | アプリケーション情報 |

## イベント仕様

### 1-戻るリンククリック

ヘッダーの戻るリンク（ボード名）をクリックすると、公開ボード詳細画面に遷移する。

- 遷移先URL: `/public/boards/:board_id`
- ホットキー: 左矢印キー（←）またはEscキーでも同様の遷移

### 2-画像拡大（ライトボックス）

カードに添付された画像の拡大ボタンをクリックすると、ライトボックスでフルサイズ表示される。

- Stimulusコントローラー: `lightbox`
- 閉じる操作: Escキーまたは閉じるボタン

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

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面表示 | boards, cards, closures, steps, tags, users | SELECT | カード詳細情報の取得 |

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

#### cards（参照のみ）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | 全カラム | board.published_key = :board_id AND number = :id AND published = true | 公開カードのみ |

#### steps（参照のみ）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | 全カラム | card_id = @card.id | カードに紐づくステップ一覧 |

## メッセージ仕様

本画面では特定のメッセージ表示はない。

## 例外処理

| 条件 | 処理内容 |
|------|---------|
| ボードが存在しない | 404 Not Found |
| ボードが非公開 | 404 Not Found（ensure_board_accessible） |
| カードが存在しない | 404 Not Found（find_by!による例外） |
| カードが非公開（下書き状態） | 404 Not Found（publishedスコープ） |
| アカウントがキャンセル済み | 404 Not Found（ensure_board_accessible） |

## 備考

- 30秒のパブリックキャッシュが設定されており、CDN等でキャッシュ可能
- HTTPキャッシュヘッダー: `public: true, max-age: 30`
- レイアウト: `public`（認証不要レイアウト）
- カードの色設定（`--card-color`）がCSSカスタムプロパティとして適用される
- OGP画像は、カードに画像が添付されている場合はその画像、ない場合は `/app-icon.png` が使用される

---

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

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

### 推奨読解順序

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

カード詳細表示に必要なモデルの関連を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | card.rb | `app/models/card.rb` | Cardモデルの定義、関連モジュール（Closeable, Multistep, Taggable等）のinclude |
| 1-2 | board.rb | `app/models/board.rb` | Boardモデル、Publishableモジュールのinclude |
| 1-3 | step.rb | `app/models/step.rb` | ステップモデルの定義（存在する場合） |
| 1-4 | tag.rb | `app/models/tag.rb` | タグモデルの定義 |

**読解のコツ**: Cardモデルは多数のConcernをincludeしている。画像添付には `has_one_attached :image`、リッチテキストには `has_rich_text :description` を使用している。

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | cards_controller.rb | `app/controllers/public/cards_controller.rb` | showアクションの実装（シンプルに空） |
| 2-2 | base_controller.rb | `app/controllers/public/base_controller.rb` | 親クラス、before_actionでの@card設定 |

**主要処理フロー**:
1. **base_controller 4行目**: `before_action :set_board, :set_card, :set_public_cache_expiration`
2. **base_controller 14-15行目**: `set_card` でカードを取得（`@board.cards.published.find_by!(number: params[:id])`）
3. **cards_controller 2-3行目**: showアクションは空（ビューのみ）

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

テンプレートの構造とOGP設定を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | show.html.erb | `app/views/public/cards/show.html.erb` | 全体構造、OGPメタタグ、各パーシャルの呼び出し |
| 3-2 | _content.html.erb | `app/views/public/cards/show/_content.html.erb` | タイトルと説明文の表示 |
| 3-3 | _steps.html.erb | `app/views/public/cards/show/_steps.html.erb` | ステップ一覧の表示 |
| 3-4 | _meta.html.erb | `app/views/cards/display/public_preview/_meta.html.erb` | メタ情報（作成者、日時、担当者） |

**主要処理フロー**:
- **1行目**: `@page_title = @card.title` - ページタイトル設定
- **3-13行目**: OGPメタタグの設定（og:title, og:description, og:image, twitter:*）
- **21-46行目**: カード詳細表示部分、各パーシャルのレンダリング

#### Step 4: ヘルパーを理解する

OGP用の抜粋生成ヘルパーを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | excerpt_helper.rb | `app/helpers/excerpt_helper.rb` | format_excerptメソッドの実装 |

**主要処理フロー**:
- **2-11行目**: `format_excerpt` - リッチテキストからプレーンテキストを抽出し、指定長で切り詰め

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

```
Public::CardsController#show
    │
    ├─ Public::BaseController (before_action)
    │      ├─ set_board (published_keyでボード取得)
    │      ├─ set_card (card番号でカード取得, publishedスコープ)
    │      ├─ set_public_cache_expiration (30秒キャッシュ)
    │      └─ ensure_board_accessible (アクセス可否チェック)
    │
    └─ show.html.erb
           ├─ content_for :head (OGPメタタグ)
           │      └─ ExcerptHelper#format_excerpt
           │
           ├─ content_for :header (戻るリンク)
           │
           ├─ カード詳細セクション
           │      ├─ cards/display/preview/_board.html.erb
           │      ├─ cards/display/preview/_tags.html.erb
           │      ├─ public/cards/show/_content.html.erb
           │      ├─ cards/display/public_preview/_columns.html.erb
           │      ├─ cards/display/common/_stamp.html.erb
           │      ├─ public/cards/show/_steps.html.erb
           │      ├─ cards/display/public_preview/_meta.html.erb
           │      └─ cards/display/perma/_background.html.erb
           │
           ├─ layouts/_lightbox.html.erb
           │
           └─ public/_footer.html.erb
```

### データフロー図

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

published_key ────▶ Board.find_by_published_key ────▶ @board
card_number   ────▶ @board.cards.published.find_by! ─▶ @card
                                                          │
                    ┌─────────────────────────────────────┘
                    │
                    ▼
              show.html.erb
                    │
    ┌───────────────┼───────────────┬─────────────────┐
    │               │               │                 │
    ▼               ▼               ▼                 ▼
  OGPメタ       カード詳細      ステップ        ライトボックス
  (head)        セクション       一覧            (画像拡大)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| cards_controller.rb | `app/controllers/public/cards_controller.rb` | コントローラー | リクエストハンドリング |
| base_controller.rb | `app/controllers/public/base_controller.rb` | コントローラー | 公開画面共通処理 |
| show.html.erb | `app/views/public/cards/show.html.erb` | テンプレート | メインビュー |
| _content.html.erb | `app/views/public/cards/show/_content.html.erb` | パーシャル | タイトル・説明 |
| _steps.html.erb | `app/views/public/cards/show/_steps.html.erb` | パーシャル | ステップ一覧 |
| _meta.html.erb | `app/views/cards/display/public_preview/_meta.html.erb` | パーシャル | メタ情報 |
| _columns.html.erb | `app/views/cards/display/public_preview/_columns.html.erb` | パーシャル | カラム表示 |
| _stamp.html.erb | `app/views/cards/display/common/_stamp.html.erb` | パーシャル | 状態スタンプ |
| _background.html.erb | `app/views/cards/display/perma/_background.html.erb` | パーシャル | 背景画像 |
| _lightbox.html.erb | `app/views/layouts/_lightbox.html.erb` | パーシャル | 画像拡大ダイアログ |
| excerpt_helper.rb | `app/helpers/excerpt_helper.rb` | ヘルパー | 抜粋テキスト生成 |
| card.rb | `app/models/card.rb` | モデル | カードモデル |
| board.rb | `app/models/board.rb` | モデル | ボードモデル |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義（206行目） |
