# 画面設計書 118-キャンペーン画面

## 概要

本ドキュメントは、Chatwootのチャットウィジェットにおけるキャンペーン画面の設計書です。アクティブなキャンペーンメッセージを表示するための画面です。

### 本画面の処理概要

**業務上の目的・背景**：キャンペーン画面は、マーケティングやプロモーション目的で設定されたキャンペーンメッセージを顧客に表示するための画面です。管理者が設定したライブチャットキャンペーンのメッセージが、特定の条件（ページ滞在時間、訪問回数など）を満たした訪問者に対して自動的に表示されます。これにより、プロアクティブな顧客エンゲージメントを実現し、コンバージョン率の向上に貢献します。

**画面へのアクセス方法**：キャンペーンがトリガーされた際に自動的に表示されます。URLハッシュパスは `/campaigns` です。

**主要な操作・処理内容**：
1. アクティブキャンペーンメッセージの表示
2. キャンペーン送信者情報の表示
3. 閉じるボタンでウィジェットを閉じてキャンペーンをスヌーズ
4. メッセージクリックでフル会話画面へ遷移

**画面遷移**：
- 遷移元：ウィジェット自動表示（キャンペーントリガー時）
- 遷移先：ホーム画面またはプリチャットフォーム（メッセージクリック時）

**権限による表示制御**：ウィジェットはWebサイト訪問者向けのため、特別な権限制御はありません。キャンペーンの表示はサーバーサイドのトリガー条件に依存します。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 165 | Widgetキャンペーン | 主機能 | キャンペーンメッセージ表示 |

## 画面種別

通知画面

## URL/ルーティング

- パス: `#/campaigns`（ウィジェット内ハッシュルーティング）
- ルート名: `campaigns`
- コンポーネント: `Campaigns.vue`

## 入出力項目

### 入力項目

本画面には入力項目はありません。

### 出力項目

| 項目名 | 項目ID | 型 | 説明 |
|--------|--------|-----|------|
| キャンペーン情報 | campaign | object | アクティブキャンペーンの詳細 |
| メッセージ配列 | messages | array | キャンペーンメッセージ（1件） |

## 表示項目

### 閉じるボタンエリア

| 項目名 | 説明 |
|--------|------|
| 閉じるボタン | ウィジェットを閉じるボタン |

### メッセージエリア

| 項目名 | 説明 |
|--------|------|
| 送信者情報 | キャンペーンメッセージの送信者名とアバター |
| メッセージバブル | キャンペーンメッセージ内容 |

### アクションエリア

| 項目名 | 説明 |
|--------|------|
| View Messages ボタン | 会話開始のためのボタン |

## イベント仕様

### 1-ウィジェット閉じる（キャンペーンスヌーズ）

**トリガー**: 閉じるボタンクリック

**処理フロー**:
1. `closeFullView`メソッド呼び出し
2. iframe内かどうかを確認
3. iframe内の場合:
   - `setCampaignReadOn`イベントを親ページへ送信（キャンペーン既読マーク）
   - `toggleBubble`イベントを親ページへ送信（ウィジェット閉じる）
   - `snooze-campaigns`イベントをemitterで発火（キャンペーンスヌーズ）
4. ウィジェットが閉じる

### 2-会話開始

**トリガー**: メッセージまたは「View Messages」ボタンクリック

**処理フロー**:
1. UnreadMessageListコンポーネントの`openConversationView`が呼び出される
2. `ON_UNREAD_MESSAGE_CLICK`イベントが発火
3. ホーム画面またはプリチャットフォームへ遷移
4. キャンペーンIDが会話作成時に使用される

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| キャンペーンスヌーズ | - | - | クライアントサイドで管理（localStorage等） |

本画面自体はデータベースへの直接更新を行いません。キャンペーンの既読状態はクライアントサイドで管理されます。

## メッセージ仕様

| 種別 | キー | メッセージ内容 | 表示タイミング |
|------|-----|---------------|---------------|
| ボタン | UNREAD_VIEW.CLOSE_MESSAGES_BUTTON | メッセージを閉じる | 閉じるボタン |
| ボタン | UNREAD_VIEW.VIEW_MESSAGES_BUTTON | メッセージを見る | View Messagesボタン |

## 例外処理

| 例外状況 | 処理内容 |
|---------|---------|
| キャンペーンなし | 本画面は表示されない |
| iframe外で実行 | IFrameHelper判定によりpostMessage送信をスキップ |

## 備考

- 未読メッセージ画面（UnreadMessages.vue）とUIコンポーネント（UnreadMessageList）を共有
- キャンペーンメッセージは1件のみを配列形式で渡す
- キャンペーンスヌーズにより、同一キャンペーンの再表示を防止
- campaignIdはプリチャットフォーム経由で会話作成時に使用される

---

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

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

### 推奨読解順序

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

キャンペーンデータの取得方法を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Campaigns.vue | `app/javascript/widget/views/Campaigns.vue` | L13: campaign getter |
| 1-2 | getters.js | `app/javascript/widget/store/modules/campaign/getters.js` | getActiveCampaign getter |

**読解のコツ**: campaignストアからアクティブキャンペーンを取得し、messages配列形式に変換してUnreadMessageListに渡す。

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

ルーティングとビューコンポーネントの構成を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | router.js | `app/javascript/widget/router.js` | L13-17: campaignsルート定義 |
| 2-2 | Campaigns.vue | `app/javascript/widget/views/Campaigns.vue` | ビューコンポーネント |

**主要処理フロー**:
1. **L13**: mapGettersでcampaign取得（getActiveCampaign）
2. **L14-22**: messages計算プロパティ - キャンペーンをメッセージ配列形式に変換
3. **L26-34**: closeFullViewメソッド - スヌーズ処理

#### Step 3: スヌーズ処理を理解する

キャンペーンスヌーズのイベントフローを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Campaigns.vue | `app/javascript/widget/views/Campaigns.vue` | closeFullViewメソッド |

**closeFullView主要処理**:
- **L28-29**: setCampaignReadOnイベント送信
- **L30**: toggleBubbleイベント送信
- **L31**: snooze-campaignsイベント発火

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

```
Campaigns.vue (ビュー)
    |
    +-- computed: messages
    |       |
    |       +-- campaign/getActiveCampaign
    |               |
    |               +-- { content, sender, campaignId }
    |
    +-- UnreadMessageList.vue
            |
            +-- 閉じるボタン
            |       |
            |       +-- @click -> closeFullView() [emit('close')]
            |               |
            |               +-- Campaigns.vue: closeFullView()
            |                       |
            |                       +-- IFrameHelper.sendMessage({ event: 'setCampaignReadOn' })
            |                       +-- IFrameHelper.sendMessage({ event: 'toggleBubble' })
            |                       +-- emitter.emit('snooze-campaigns')
            |
            +-- UnreadMessage.vue (キャンペーンメッセージ)
            |
            +-- View Messages ボタン
                    |
                    +-- openConversationView()
```

### データフロー図

```
[ストア]                  [コンポーネント]              [出力]

campaign/getActiveCampaign
        |
        v
Campaigns.vue ---> messages computed
        |               |
        |               v
        |       { content, sender, campaignId }
        |
        v
UnreadMessageList.vue ---> 表示
        |
        +-- closeFullView()
        |       |
        |       +-- setCampaignReadOn -------> 親ページ
        |       +-- toggleBubble -----------> 親ページ
        |       +-- snooze-campaigns -------> emitter
        |
        +-- openConversationView()
                |
                v
        ON_UNREAD_MESSAGE_CLICK
                |
                v
        ホーム/プリチャットフォーム
                |
                v
        会話作成（campaignId付き）
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Campaigns.vue | `app/javascript/widget/views/Campaigns.vue` | ソース | ビューコンポーネント |
| UnreadMessageList.vue | `app/javascript/widget/components/UnreadMessageList.vue` | ソース | メッセージリスト（共有） |
| UnreadMessage.vue | `app/javascript/widget/components/UnreadMessage.vue` | ソース | 個別メッセージ（共有） |
| router.js | `app/javascript/widget/router.js` | ソース | ルーティング |
| getters.js | `app/javascript/widget/store/modules/campaign/getters.js` | ソース | キャンペーンgetter |
| index.js | `app/javascript/widget/store/modules/campaign/index.js` | ソース | キャンペーンストア |
| utils.js | `app/javascript/widget/helpers/utils.js` | ソース | IFrameHelper |
