# 画面設計書 99-ライブチャットキャンペーン画面

## 概要

本ドキュメントは、Chatwootのキャンペーン機能における「ライブチャットキャンペーン画面」の設計仕様を記述したものである。

### 本画面の処理概要

ライブチャットキャンペーン画面は、Webサイトウィジェット向けのプロアクティブメッセージキャンペーンを管理する画面である。訪問者が特定のURLで一定時間滞在した際に自動的にメッセージを送信するキャンペーンを作成・編集・削除できる。

**業務上の目的・背景**：カスタマーエンゲージメントを向上させるために、Webサイト訪問者に対してプロアクティブにメッセージを送信する機能が必要である。特定のページに滞在している見込み客に対して、タイミングよくサポートや案内を提供することで、コンバージョン率の向上やカスタマーエクスペリエンスの改善を図る。

**画面へのアクセス方法**：サイドバーの「Campaigns」メニューから「Live Chat」タブをクリック、または直接URL `/app/accounts/:accountId/campaigns/live_chat` にアクセスする。

**主要な操作・処理内容**：
1. ライブチャットキャンペーン一覧の表示
2. 新規キャンペーンの作成
3. 既存キャンペーンの編集
4. キャンペーンの削除

**画面遷移**：
- 遷移元：サイドナビゲーション、キャンペーントップ
- 遷移先：なし（本画面で操作完結）

**権限による表示制御**：administrator権限を持つユーザーのみアクセス可能。CAMPAIGNSフィーチャーフラグが有効である必要がある。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 161 | キャンペーン管理 | 主機能 | ライブチャットキャンペーンの管理 |

## 画面種別

一覧

## URL/ルーティング

| 項目 | 値 |
|------|-----|
| パス | `/app/accounts/:accountId/campaigns/live_chat` |
| ルート名 | `campaigns_livechat_index` |
| コンポーネント | `LiveChatCampaignsPage.vue` |

## 入出力項目

### 入力項目（URLパラメータ）

| 項目名 | 型 | 必須 | 説明 |
|--------|-----|------|------|
| accountId | Number | Yes | アカウントID |

### 出力項目

| 項目名 | 型 | 説明 |
|--------|-----|------|
| liveChatCampaigns | Array | ライブチャットキャンペーン一覧 |
| isFetchingCampaigns | Boolean | データ取得中フラグ |

## 表示項目

### ヘッダー

| 項目名 | 型 | 説明 |
|--------|-----|------|
| タイトル | Text | "Live chat campaigns" |
| 新規作成ボタン | Button | "Create campaign" |

### キャンペーンカード（CampaignCard）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| title | String | キャンペーン名 |
| message | String | キャンペーンメッセージ |
| enabled | Boolean | 有効/無効状態 |
| sender | Object | 送信者情報（Bot or User） |
| inbox | Object | 関連インボックス |
| アクションメニュー | Dropdown | Edit / Delete |

### キャンペーン作成/編集フォーム（LiveChatCampaignForm）

| 項目名 | 型 | バリデーション | 説明 |
|--------|-----|--------------|------|
| タイトル | Input | 必須、1文字以上 | キャンペーン名 |
| メッセージ | Editor | 必須、1文字以上 | 送信メッセージ（リッチテキスト） |
| インボックス | ComboBox | 必須 | Website Inboxのみ |
| 送信者 | ComboBox | 必須 | Bot or インボックスメンバー |
| URL | Input | 必須、http/https開始、URLPattern形式 | トリガーURL |
| ページ滞在時間 | Input(Number) | 必須 | 秒単位 |
| 有効フラグ | Checkbox | - | キャンペーンの有効/無効 |
| 営業時間のみ | Checkbox | - | 営業時間中のみトリガー |

## イベント仕様

### 1-画面初期表示

**トリガー**: コンポーネントのマウント時

**処理フロー**:
1. CampaignLayoutでキャンペーン取得（親コンポーネントで処理済み）
2. Vuexゲッター `campaigns/getLiveChatCampaigns` でライブチャットキャンペーンをフィルタ
3. campaign_type=ongoing かつ inbox.channel_type=Channel::WebWidget のみ表示
4. CampaignListコンポーネントで一覧表示

### 2-新規キャンペーン作成

**トリガー**: 「Create campaign」ボタンクリック

**処理フロー**:
1. toggleLiveChatCampaignDialog関数でダイアログを表示
2. LiveChatCampaignDialogがオーバーレイ表示
3. フォーム入力・バリデーション（Vuelidate）
4. 送信ボタンクリックでhandleSubmit関数を実行
5. addCampaign関数でキャンペーン情報を構築
6. `campaigns/create` アクションをディスパッチ
7. POST `/api/v1/accounts/:accountId/campaigns` を呼び出し
8. 成功時：成功メッセージを表示、ダイアログを閉じる
9. 失敗時：エラーメッセージを表示
10. イベントトラッキング（CAMPAIGNS_EVENTS.CREATE_CAMPAIGN）

### 3-キャンペーン編集

**トリガー**: キャンペーンカードの「Edit」ボタンクリック

**処理フロー**:
1. handleEdit関数を実行
2. 選択キャンペーンをselectedCampaignに設定
3. EditLiveChatCampaignDialogを開く
4. 既存データをフォームに反映（updateStateFromCampaign）
5. フォーム編集後、送信ボタンクリック
6. `campaigns/update` アクションをディスパッチ
7. PATCH `/api/v1/accounts/:accountId/campaigns/:id` を呼び出し
8. 成功時：成功メッセージを表示、ダイアログを閉じる
9. 失敗時：エラーメッセージを表示
10. イベントトラッキング（CAMPAIGNS_EVENTS.UPDATE_CAMPAIGN）

### 4-キャンペーン削除

**トリガー**: キャンペーンカードの「Delete」ボタンクリック

**処理フロー**:
1. handleDelete関数を実行
2. 選択キャンペーンをselectedCampaignに設定
3. ConfirmDeleteCampaignDialogを開く
4. 確認後handleDialogConfirm関数を実行
5. deleteCampaign関数を実行
6. `campaigns/delete` アクションをディスパッチ
7. DELETE `/api/v1/accounts/:accountId/campaigns/:id` を呼び出し
8. 成功時：成功メッセージを表示、ダイアログを閉じる
9. 失敗時：エラーメッセージを表示
10. イベントトラッキング（CAMPAIGNS_EVENTS.DELETE_CAMPAIGN）

### 5-インボックス変更時のメンバー取得

**トリガー**: フォームでインボックスを選択

**処理フロー**:
1. handleInboxChange関数を実行（watchで監視）
2. `inboxMembers/get` アクションをディスパッチ
3. 選択インボックスのメンバー一覧を取得
4. sendersAndBotListを更新（Bot + メンバー一覧）

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| キャンペーン取得 | campaigns | SELECT | account_idに紐づくキャンペーン取得 |
| キャンペーン作成 | campaigns | INSERT | 新規キャンペーン作成 |
| キャンペーン更新 | campaigns | UPDATE | 既存キャンペーン更新 |
| キャンペーン削除 | campaigns | DELETE | キャンペーン削除 |
| キャンペーン削除 | conversations | UPDATE | campaign_idをnullに更新 |

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

#### campaigns

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | title | 入力値 | 必須 |
| INSERT/UPDATE | message | 入力値 | 必須 |
| INSERT/UPDATE | inbox_id | 入力値 | Website Inboxのみ |
| INSERT/UPDATE | sender_id | 入力値（null許容） | 0の場合はBot |
| INSERT/UPDATE | enabled | true/false | デフォルトtrue |
| INSERT/UPDATE | trigger_only_during_business_hours | true/false | デフォルトfalse |
| INSERT/UPDATE | trigger_rules | {"url": "xx", "time_on_page": n} | JSONB |
| INSERT/UPDATE | campaign_type | 'ongoing' | ライブチャット固定 |
| DELETE | - | id = campaignId | カスケード影響あり |

## メッセージ仕様

| 種別 | メッセージキー | メッセージ内容 | 表示条件 |
|------|--------------|--------------|----------|
| ヘッダータイトル | CAMPAIGN.LIVE_CHAT.HEADER_TITLE | "Live chat campaigns" | 常時 |
| 新規作成ボタン | CAMPAIGN.LIVE_CHAT.NEW_CAMPAIGN | "Create campaign" | 常時 |
| 空状態タイトル | CAMPAIGN.LIVE_CHAT.EMPTY_STATE.TITLE | "No live chat campaigns are available" | キャンペーンなし時 |
| 空状態サブタイトル | CAMPAIGN.LIVE_CHAT.EMPTY_STATE.SUBTITLE | "Connect with your customers using proactive messages..." | キャンペーンなし時 |
| 作成成功 | CAMPAIGN.LIVE_CHAT.CREATE.FORM.API.SUCCESS_MESSAGE | "Live chat campaign created successfully" | 作成成功時 |
| 作成失敗 | CAMPAIGN.LIVE_CHAT.CREATE.FORM.API.ERROR_MESSAGE | "There was an error. Please try again." | 作成失敗時 |
| 更新成功 | CAMPAIGN.LIVE_CHAT.EDIT.FORM.API.SUCCESS_MESSAGE | "Live chat campaign updated successfully" | 更新成功時 |
| 更新失敗 | CAMPAIGN.LIVE_CHAT.EDIT.FORM.API.ERROR_MESSAGE | "There was an error. Please try again." | 更新失敗時 |
| 削除確認タイトル | CAMPAIGN.CONFIRM_DELETE.TITLE | "Are you sure to delete?" | 削除確認時 |
| 削除確認説明 | CAMPAIGN.CONFIRM_DELETE.DESCRIPTION | "The delete action is permanent and cannot be reversed." | 削除確認時 |
| 削除成功 | CAMPAIGN.CONFIRM_DELETE.API.SUCCESS_MESSAGE | "Campaign deleted successfully" | 削除成功時 |
| 削除失敗 | CAMPAIGN.CONFIRM_DELETE.API.ERROR_MESSAGE | "There was an error. Please try again." | 削除失敗時 |

## 例外処理

| 例外状況 | 処理内容 |
|---------|---------|
| キャンペーン取得失敗 | 空のリストを表示 |
| インボックスメンバー取得失敗 | useAlertでエラーメッセージを表示、送信者リストを空に |
| キャンペーン作成失敗 | useAlertでエラーメッセージを表示 |
| キャンペーン更新失敗 | useAlertでエラーメッセージを表示 |
| キャンペーン削除失敗 | useAlertでエラーメッセージを表示 |
| URLバリデーションエラー | フォームにエラーメッセージを表示 |
| 権限不足 | ルートガードによりアクセス拒否 |

## 備考

- ライブチャットキャンペーンはcampaign_type=ongoing（継続型）として作成される
- インボックスはWebsite（Channel::WebWidget）タイプのみ選択可能
- 送信者を0（Bot）に設定すると、ボットからメッセージが送信される
- trigger_rulesのURLはhttp://またはhttps://で始まる必要がある
- URLPatternによる高度なパターンマッチングが可能（例：https://example.com/products/*）
- ページ滞在時間（秒）経過後にメッセージがトリガーされる
- 営業時間のみトリガーオプションはビジネスアワー設定と連動
- フィーチャーフラグ `CAMPAIGNS` が有効である必要がある

---

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

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

### 推奨読解順序

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

キャンペーンのデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Campaign (Model) | `app/models/campaign.rb` | スキーマ定義、campaign_type enum、バリデーション |
| 1-2 | campaigns.js (Store) | `app/javascript/dashboard/store/modules/campaigns.js` | Vuexストアの状態、getLiveChatCampaignsゲッター |

**読解のコツ**: Campaign モデルの campaign_type が ongoing/one_off の2種類あり、ライブチャットは ongoing タイプであることを確認する。

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

画面のエントリーポイントとコンポーネント構成を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | LiveChatCampaignsPage.vue | `app/javascript/dashboard/routes/dashboard/campaigns/pages/LiveChatCampaignsPage.vue` | 画面コンポーネントのメイン処理 |
| 2-2 | campaigns.routes.js | `app/javascript/dashboard/routes/dashboard/campaigns/campaigns.routes.js` | ルート定義（43-47行目） |

**主要処理フロー**:
1. **22-23行目**: uiFlagsとisFetchingCampaignsを取得
2. **27-29行目**: getLiveChatCampaignsゲッターでフィルタリング
3. **35-38行目**: handleEdit関数で編集ダイアログを開く
4. **39-42行目**: handleDelete関数で削除ダイアログを開く

#### Step 3: 子コンポーネントを理解する

キャンペーンの作成・編集UIを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | CampaignList.vue | `app/javascript/dashboard/components-next/Campaigns/Pages/CampaignPage/CampaignList.vue` | キャンペーンカードリスト |
| 3-2 | LiveChatCampaignDialog.vue | `app/javascript/dashboard/components-next/Campaigns/Pages/CampaignPage/LiveChatCampaign/LiveChatCampaignDialog.vue` | 新規作成ダイアログ |
| 3-3 | LiveChatCampaignForm.vue | `app/javascript/dashboard/components-next/Campaigns/Pages/CampaignPage/LiveChatCampaign/LiveChatCampaignForm.vue` | 作成/編集フォーム |
| 3-4 | ConfirmDeleteCampaignDialog.vue | `app/javascript/dashboard/components-next/Campaigns/Pages/CampaignPage/ConfirmDeleteCampaignDialog.vue` | 削除確認ダイアログ |

**主要処理フロー**:
- **LiveChatCampaignForm.vue 135-146行目**: prepareCampaignDetails関数でキャンペーンオブジェクト構築
- **LiveChatCampaignForm.vue 117-133行目**: handleInboxChange関数でインボックスメンバー取得
- **LiveChatCampaignForm.vue 70-77行目**: バリデーションルール定義

#### Step 4: Vuex Actionsを理解する

キャンペーン操作のAPI呼び出し処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | campaigns.js (Store) | `app/javascript/dashboard/store/modules/campaigns.js` | get, create, update, deleteアクション |
| 4-2 | campaigns.js (API) | `app/javascript/dashboard/api/campaigns.js` | APIクライアント（ApiClient継承） |

**主要処理フロー**:
- **17-54行目**: ゲッター定義（getCampaigns, getLiveChatCampaigns）
- **57-67行目**: getアクション - キャンペーン一覧取得
- **68-78行目**: createアクション - キャンペーン作成
- **79-90行目**: updateアクション - キャンペーン更新
- **91-102行目**: deleteアクション - キャンペーン削除

#### Step 5: バックエンドAPIを理解する

RailsコントローラーでのAPI処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | campaigns_controller.rb | `app/controllers/api/v1/accounts/campaigns_controller.rb` | CRUD操作のエンドポイント |
| 5-2 | campaign.rb (Model) | `app/models/campaign.rb` | バリデーション、ensure_correct_campaign_attributes |

**主要処理フロー**:
- **5-7行目**: indexアクション - キャンペーン一覧取得
- **11-13行目**: createアクション - キャンペーン作成
- **15-17行目**: updateアクション - キャンペーン更新
- **19-22行目**: destroyアクション - キャンペーン削除
- **Model 88-98行目**: ensure_correct_campaign_attributes - campaign_typeの自動設定

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

```
LiveChatCampaignsPage.vue
    │
    ├─ CampaignLayout.vue (親から提供)
    │      └─ campaigns/get (マウント時)
    │
    ├─ Vuexゲッター
    │      └─ campaigns/getLiveChatCampaigns
    │             └─ フィルタ: campaign_type=ongoing, channel_type=WEB
    │
    ├─ @click (Create campaign)
    │      └─ toggleLiveChatCampaignDialog()
    │             └─ LiveChatCampaignDialog.vue
    │                    ├─ LiveChatCampaignForm.vue
    │                    │      ├─ handleInboxChange()
    │                    │      │      └─ inboxMembers/get
    │                    │      └─ handleSubmit()
    │                    │             └─ prepareCampaignDetails()
    │                    │
    │                    └─ addCampaign()
    │                           └─ campaigns/create
    │                                  └─ POST /api/v1/.../campaigns
    │
    ├─ CampaignList.vue
    │      └─ CampaignCard[forEach]
    │             ├─ @edit → handleEdit()
    │             │      └─ EditLiveChatCampaignDialog
    │             │             └─ campaigns/update
    │             │
    │             └─ @delete → handleDelete()
    │                    └─ ConfirmDeleteCampaignDialog
    │                           └─ campaigns/delete
    │
    └─ LiveChatCampaignEmptyState.vue (空状態)
```

### データフロー図

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

URLパラメータ ────────▶ CampaignLayout.vue ─────────────▶ campaigns[]
  - accountId              │
                           ▼
                      campaigns/get action
                           │
                           ▼
                      CampaignsAPI.get()
                           │
                           ▼
                      campaigns (state.records)
                           │
                           ▼
                      getLiveChatCampaigns getter
                           │  (フィルタ: ongoing + WEB)
                           ▼
                      liveChatCampaigns[]
                           │
                           ▼
                      CampaignList.vue
                           │
                           └─▶ CampaignCard[forEach]
                                  │
                                  ├─▶ title, message
                                  ├─▶ enabled (status badge)
                                  ├─▶ sender info
                                  ├─▶ inbox name
                                  └─▶ action buttons (edit/delete)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| LiveChatCampaignsPage.vue | `app/javascript/dashboard/routes/dashboard/campaigns/pages/LiveChatCampaignsPage.vue` | ソース | 画面コンポーネント |
| CampaignLayout.vue | `app/javascript/dashboard/components-next/Campaigns/CampaignLayout.vue` | ソース | レイアウト共通 |
| CampaignList.vue | `app/javascript/dashboard/components-next/Campaigns/Pages/CampaignPage/CampaignList.vue` | ソース | キャンペーンリスト |
| CampaignCard.vue | `app/javascript/dashboard/components-next/Campaigns/CampaignCard/CampaignCard.vue` | ソース | キャンペーンカード |
| LiveChatCampaignDialog.vue | `app/javascript/dashboard/components-next/Campaigns/Pages/CampaignPage/LiveChatCampaign/LiveChatCampaignDialog.vue` | ソース | 新規作成ダイアログ |
| LiveChatCampaignForm.vue | `app/javascript/dashboard/components-next/Campaigns/Pages/CampaignPage/LiveChatCampaign/LiveChatCampaignForm.vue` | ソース | フォーム |
| EditLiveChatCampaignDialog.vue | `app/javascript/dashboard/components-next/Campaigns/Pages/CampaignPage/LiveChatCampaign/EditLiveChatCampaignDialog.vue` | ソース | 編集ダイアログ |
| ConfirmDeleteCampaignDialog.vue | `app/javascript/dashboard/components-next/Campaigns/Pages/CampaignPage/ConfirmDeleteCampaignDialog.vue` | ソース | 削除確認ダイアログ |
| campaigns.routes.js | `app/javascript/dashboard/routes/dashboard/campaigns/campaigns.routes.js` | ソース | ルート定義 |
| campaigns.js | `app/javascript/dashboard/store/modules/campaigns.js` | ソース | Vuexモジュール |
| campaigns.js | `app/javascript/dashboard/api/campaigns.js` | ソース | APIクライアント |
| campaigns_controller.rb | `app/controllers/api/v1/accounts/campaigns_controller.rb` | ソース | コントローラー |
| campaign.rb | `app/models/campaign.rb` | ソース | モデル定義 |
