# 画面設計書 98-ポータル設定画面

## 概要

本ドキュメントは、Chatwootのヘルプセンター機能における「ポータル設定画面」の設計仕様を記述したものである。

### 本画面の処理概要

ポータル設定画面は、ヘルプセンターポータルの基本設定とカスタムドメイン設定を管理する画面である。ポータル名、ロゴ、テーマカラー、カスタムドメインなどの設定変更やポータルの削除を行う。

**業務上の目的・背景**：ヘルプセンターのブランディングやカスタマイズを行い、企業のブランドアイデンティティに合わせたポータルを構築するために設定機能が必要である。カスタムドメイン機能により、自社ドメインでヘルプセンターを公開できる。

**画面へのアクセス方法**：ヘルプセンターのサイドナビゲーションから「Settings」タブをクリック、または直接URL `/app/accounts/:accountId/portals/:portalSlug/settings` にアクセスする。

**主要な操作・処理内容**：
1. ポータル基本設定の表示・編集（名前、ロゴ、ヘッダーテキスト、スラッグ等）
2. ライブチャットウィジェットの設定
3. カスタムドメインの追加・編集
4. SSL証明書ステータスの確認・更新（Chatwoot Cloud限定）
5. ポータルの削除

**画面遷移**：
- 遷移元：サイドナビゲーション
- 遷移先：ポータル新規作成画面（削除後、他にポータルがない場合）、記事一覧画面（削除後、他にポータルがある場合）

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

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 152 | ポータル管理 | 主機能 | ポータルの設定変更・削除 |

## 画面種別

詳細/編集

## URL/ルーティング

| 項目 | 値 |
|------|-----|
| パス | `/app/accounts/:accountId/portals/:portalSlug/settings` |
| ルート名 | `portals_settings_index` |
| コンポーネント | `PortalsSettingsIndexPage.vue` |

## 入出力項目

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

| 項目名 | 型 | 必須 | 説明 |
|--------|-----|------|------|
| accountId | Number | Yes | アカウントID |
| portalSlug | String | Yes | ポータルのスラッグ |

### 出力項目

| 項目名 | 型 | 説明 |
|--------|-----|------|
| activePortal | Object | 現在のポータル情報 |
| portals | Array | 全ポータル一覧 |
| isFetching | Boolean | データ取得中フラグ |

## 表示項目

### ポータル基本設定（PortalBaseSettings）

| 項目名 | 型 | バリデーション | 説明 |
|--------|-----|--------------|------|
| ロゴ | Image | 最大4MB | ポータルロゴ画像 |
| 名前 | Input | 必須、2文字以上 | ポータル名 |
| ヘッダーテキスト | Input | - | ポータルヘッダーテキスト |
| ページタイトル | Input | - | ポータルページタイトル |
| ホームページリンク | Input | URL形式 | ホームページへのリンク |
| スラッグ | Input | 必須、有効なスラッグ形式 | ポータルスラッグ |
| ライブチャットウィジェット | ComboBox | - | 関連付けるWebWidgetインボックス |
| ブランドカラー | ColorPicker | - | テーマカラー |

### カスタムドメイン設定（PortalConfigurationSettings）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| カスタムドメイン | Text | 設定済みドメイン表示 |
| SSLステータス | Badge | Live/Pending/Error（Cloud限定） |
| 編集ボタン | Button | ドメイン編集ダイアログを開く |
| 更新ボタン | Button | SSLステータス更新（Cloud限定） |
| 追加ボタン | Button | ドメイン追加ダイアログを開く |

### ポータル削除セクション

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 削除ボタン | Button | ポータル削除確認ダイアログを開く |

## イベント仕様

### 1-画面初期表示

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

**処理フロー**:
1. URLパラメータからportalSlugを取得
2. Vuexゲッターから現在のポータル情報を取得
3. PortalBaseSettingsに現在のポータル情報を反映
4. カスタムドメイン設定がある場合、SSLステータスを取得（Cloud限定）

### 2-基本設定更新

**トリガー**: 「Save changes」ボタンクリック

**処理フロー**:
1. バリデーションチェック（Vuelidate）
2. handleUpdatePortal関数を実行
3. updatePortalSettings関数でポータル情報を構築
4. `portals/update` アクションをディスパッチ
5. PATCH `/api/v1/accounts/:accountId/portals/:portalSlug` を呼び出し
6. スラッグ変更時はrefreshPortalRoute関数でルート更新
7. 成功時：成功メッセージを表示
8. 失敗時：エラーメッセージを表示

### 3-ロゴアップロード

**トリガー**: Avatarコンポーネントでファイル選択

**処理フロー**:
1. handleAvatarUpload関数を実行
2. ファイルサイズチェック（最大4MB）
3. uploadLogoToStorage関数でファイルをアップロード
4. uploadFile関数でblobIdとfileUrlを取得
5. 成功時：成功メッセージを表示、プレビュー更新
6. 失敗時：エラーメッセージを表示

### 4-ロゴ削除

**トリガー**: Avatarコンポーネントで削除クリック

**処理フロー**:
1. handleAvatarDelete関数を実行
2. deleteLogo関数を実行
3. `portals/deleteLogo` アクションをディスパッチ
4. DELETE `/api/v1/accounts/:accountId/portals/:portalSlug/logo` を呼び出し
5. 成功時：成功メッセージを表示
6. 失敗時：エラーメッセージを表示

### 5-カスタムドメイン追加/編集

**トリガー**: ドメイン追加/編集ボタンクリック

**処理フロー**:
1. AddCustomDomainDialogを開く
2. ドメイン入力・バリデーション
3. updatePortalConfiguration関数を実行
4. `portals/update` アクションをディスパッチ（custom_domain更新）
5. 成功時：DNSConfigurationDialogを表示
6. Cloud環境の場合、5秒後にSSLステータスを自動取得

### 6-SSLステータス更新

**トリガー**: 更新ボタンクリック（Cloud限定）

**処理フロー**:
1. fetchSSLStatus関数を実行
2. `portals/sslStatus` アクションをディスパッチ
3. GET `/api/v1/accounts/:accountId/portals/:portalSlug/ssl_status` を呼び出し
4. ssl_settingsをストアに反映
5. ステータスに応じたバッジ表示（Live/Pending/Error）

### 7-CNAME設定メール送信

**トリガー**: DNSConfigurationDialogで送信ボタンクリック

**処理フロー**:
1. handleSendCnameInstructions関数を実行
2. `portals/sendCnameInstructions` アクションをディスパッチ
3. POST `/api/v1/accounts/:accountId/portals/:portalSlug/send_instructions` を呼び出し
4. 成功時：成功メッセージを表示
5. 失敗時：エラーメッセージを表示

### 8-ポータル削除

**トリガー**: 削除ボタンクリック → 確認ダイアログで確定

**処理フロー**:
1. openConfirmDeletePortalDialog関数でダイアログを開く
2. 確認後handleDeletePortal関数を実行
3. deletePortal関数を実行
4. `portals/delete` アクションをディスパッチ
5. DELETE `/api/v1/accounts/:accountId/portals/:portalSlug` を呼び出し
6. updateRouteAfterDeletion関数で遷移先を決定
7. 他ポータルがある場合：記事一覧画面へ遷移
8. 他ポータルがない場合：ポータル新規作成画面へ遷移
9. 成功時：成功メッセージを表示
10. 失敗時：エラーメッセージを表示

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 基本設定更新 | portals | UPDATE | name, slug, color等を更新 |
| ロゴアップロード | active_storage_blobs | INSERT | ロゴファイルを保存 |
| ロゴ削除 | active_storage_attachments | DELETE | ロゴ関連付けを削除 |
| ドメイン追加/編集 | portals | UPDATE | custom_domainを更新 |
| ポータル削除 | portals | DELETE | ポータルレコードを削除 |
| ポータル削除 | categories | DELETE | 関連カテゴリを非同期削除 |
| ポータル削除 | articles | DELETE | 関連記事を非同期削除 |

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

#### portals

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | name | 入力値 | 必須 |
| UPDATE | slug | 入力値 | 必須、一意 |
| UPDATE | color | 入力値 | テーマカラー |
| UPDATE | header_text | 入力値 | ヘッダーテキスト |
| UPDATE | page_title | 入力値 | ページタイトル |
| UPDATE | homepage_link | 入力値 | URL形式 |
| UPDATE | custom_domain | 入力値 | 一意、null許容 |
| UPDATE | channel_web_widget_id | inbox_idから変換 | null許容 |
| DELETE | - | slug = portalSlug | カスケード削除 |

## メッセージ仕様

| 種別 | メッセージキー | メッセージ内容 | 表示条件 |
|------|--------------|--------------|----------|
| 設定更新成功 | HELP_CENTER.PORTAL_SETTINGS.API.UPDATE_PORTAL.SUCCESS_MESSAGE | "Portal updated successfully" | 更新成功時 |
| 設定更新失敗 | HELP_CENTER.PORTAL_SETTINGS.API.UPDATE_PORTAL.ERROR_MESSAGE | "Unable to update portal" | 更新失敗時 |
| ロゴアップロード成功 | HELP_CENTER.PORTAL_SETTINGS.FORM.AVATAR.IMAGE_UPLOAD_SUCCESS | "Image added successfully..." | アップロード成功時 |
| ロゴアップロード失敗 | HELP_CENTER.PORTAL_SETTINGS.FORM.AVATAR.IMAGE_UPLOAD_ERROR | "Couldn't upload image! Try again" | アップロード失敗時 |
| ロゴ削除成功 | HELP_CENTER.PORTAL_SETTINGS.FORM.AVATAR.IMAGE_DELETE_SUCCESS | "Logo deleted successfully" | 削除成功時 |
| ロゴ削除失敗 | HELP_CENTER.PORTAL_SETTINGS.FORM.AVATAR.IMAGE_DELETE_ERROR | "Unable to delete logo" | 削除失敗時 |
| ファイルサイズ超過 | HELP_CENTER.PORTAL_SETTINGS.FORM.AVATAR.IMAGE_UPLOAD_SIZE_ERROR | "Image size should be less than {size}MB" | サイズ超過時 |
| ポータル削除成功 | HELP_CENTER.PORTAL.PORTAL_SETTINGS.DELETE_PORTAL.API.DELETE_SUCCESS | - | 削除成功時 |
| ポータル削除失敗 | HELP_CENTER.PORTAL.PORTAL_SETTINGS.DELETE_PORTAL.API.DELETE_ERROR | "Error while deleting portal" | 削除失敗時 |
| CNAME送信成功 | HELP_CENTER.PORTAL.PORTAL_SETTINGS.SEND_CNAME_INSTRUCTIONS.API.SUCCESS_MESSAGE | "CNAME instructions sent successfully" | 送信成功時 |
| CNAME送信失敗 | HELP_CENTER.PORTAL.PORTAL_SETTINGS.SEND_CNAME_INSTRUCTIONS.API.ERROR_MESSAGE | "Error while sending CNAME instructions" | 送信失敗時 |

## 例外処理

| 例外状況 | 処理内容 |
|---------|---------|
| ポータル情報取得失敗 | ローディングスピナーを表示 |
| バリデーションエラー | 各入力フィールドにエラーメッセージを表示 |
| スラッグ重複 | APIエラーメッセージを表示 |
| カスタムドメイン重複 | APIエラーメッセージを表示 |
| ファイルアップロード失敗 | useAlertでエラーメッセージを表示 |
| 権限不足 | ルートガードによりアクセス拒否 |

## 備考

- スラッグ変更時はURLが自動的に更新され、UISettingsも更新される
- カスタムドメイン設定後、DNS設定のCNAMEレコードをchatwoot.helpに向ける必要がある
- SSL証明書の自動発行・更新はChatwoot Cloud限定機能
- ポータル削除時、関連するカテゴリと記事は非同期で削除される（dependent: :destroy_async）
- ライブチャットウィジェットはChannel::WebWidgetタイプのInboxのみ選択可能
- フィーチャーフラグ `HELP_CENTER` が有効である必要がある

---

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

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

### 推奨読解順序

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

ポータルのデータ構造と関連を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Portal (Model) | `app/models/portal.rb` | スキーマ定義、カスケード削除設定 |
| 1-2 | index.js (portals) | `app/javascript/dashboard/store/modules/helpCenterPortals/index.js` | Vuexストアの状態構造 |

**読解のコツ**: Portal モデルの has_many :categories, dependent: :destroy_async と has_many :articles, dependent: :destroy_async を確認し、削除時の動作を理解する。

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

画面のエントリーポイントとイベントハンドラを理解する。

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

**主要処理フロー**:
1. **31-38行目**: fetchSSLStatus関数でSSLステータス取得
2. **40-49行目**: fetchPortalAndItsCategories関数でポータル・カテゴリ・エージェント取得
3. **82-104行目**: updatePortalSettings関数で設定更新
4. **106-120行目**: deletePortal関数でポータル削除

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

設定UIの各コンポーネントを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | PortalSettings.vue | `app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalSettings.vue` | レイアウト、イベント中継 |
| 3-2 | PortalBaseSettings.vue | `app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalBaseSettings.vue` | 基本設定フォーム |
| 3-3 | PortalConfigurationSettings.vue | `app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalConfigurationSettings.vue` | カスタムドメイン設定 |

**主要処理フロー**:
- **PortalBaseSettings.vue 141-154行目**: handleUpdatePortal関数でポータル更新オブジェクト構築
- **PortalBaseSettings.vue 156-167行目**: uploadLogoToStorage関数でロゴアップロード
- **PortalConfigurationSettings.vue 109-120行目**: updatePortalConfiguration関数でドメイン更新

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

ポータル操作のAPI呼び出し処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | actions.js (portals) | `app/javascript/dashboard/store/modules/helpCenterPortals/actions.js` | update, delete, deleteLogo, sslStatus, sendCnameInstructionsアクション |
| 4-2 | portals.js (API) | `app/javascript/dashboard/api/helpCenter/portals.js` | updatePortal, deletePortal, deleteLogo, sslStatus, sendCnameInstructions APIメソッド |

**主要処理フロー**:
- **53-72行目**: updateアクション - ポータル情報更新
- **74-91行目**: deleteアクション - ポータル削除
- **93-108行目**: deleteLogoアクション - ロゴ削除
- **120-127行目**: sendCnameInstructionsアクション - CNAME設定メール送信
- **128-138行目**: sslStatusアクション - SSL証明書ステータス取得

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | portals_controller.rb | `app/controllers/api/v1/accounts/portals_controller.rb` | update, destroy, logo, send_instructionsアクション |

**主要処理フロー**:
- **24-32行目**: updateアクション - ポータル更新・ロゴ処理
- **34-37行目**: destroyアクション - ポータル削除
- **44-47行目**: logoアクション - ロゴ削除
- **49-61行目**: send_instructionsアクション - CNAME設定メール送信

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

```
PortalsSettingsIndexPage.vue
    │
    ├─ Vuexゲッター
    │      ├─ portals/allPortals
    │      ├─ portals/portalBySlug
    │      └─ portals/isFetchingSSLStatus
    │
    ├─ PortalSettings.vue
    │      │
    │      ├─ PortalBaseSettings.vue
    │      │      ├─ @update-portal → handleUpdatePortal()
    │      │      │      └─ updatePortalSettings()
    │      │      │             └─ store.dispatch('portals/update')
    │      │      │
    │      │      ├─ handleAvatarUpload()
    │      │      │      └─ uploadLogoToStorage()
    │      │      │             └─ uploadFile()
    │      │      │
    │      │      └─ handleAvatarDelete()
    │      │             └─ deleteLogo()
    │      │                    └─ store.dispatch('portals/deleteLogo')
    │      │
    │      ├─ PortalConfigurationSettings.vue
    │      │      ├─ @update-portal-configuration → handleUpdatePortalConfiguration()
    │      │      │      ├─ updatePortalSettings()
    │      │      │      └─ fetchSSLStatus() (5秒後、Cloud限定)
    │      │      │
    │      │      ├─ @refresh-status → fetchSSLStatus()
    │      │      │      └─ store.dispatch('portals/sslStatus')
    │      │      │
    │      │      └─ @send-cname-instructions → handleSendCnameInstructions()
    │      │             └─ store.dispatch('portals/sendCnameInstructions')
    │      │
    │      └─ ConfirmDeletePortalDialog
    │             └─ @delete-portal → handleDeletePortal()
    │                    └─ deletePortal()
    │                           ├─ store.dispatch('portals/delete')
    │                           └─ updateRouteAfterDeletion()
    │                                  └─ router.push()
```

### データフロー図

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

URLパラメータ ────────▶ portalBySlug getter ───────────▶ activePortal
  - portalSlug              │
                            ▼
                       PortalSettings.vue
                            │
                ┌───────────┼───────────┐
                ▼           ▼           ▼
      PortalBase    PortalConfig   削除ボタン
      Settings      Settings
           │             │             │
           ▼             ▼             ▼
      基本設定        ドメイン設定     確認ダイアログ
      更新処理        更新処理        削除処理
           │             │             │
           ▼             ▼             ▼
      portals/       portals/       portals/
      update         update         delete
           │             │             │
           └─────────────┼─────────────┘
                         ▼
                    API呼び出し
                         │
                         ▼
                    portals table
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| PortalsSettingsIndexPage.vue | `app/javascript/dashboard/routes/dashboard/helpcenter/pages/PortalsSettingsIndexPage.vue` | ソース | 画面コンポーネント |
| PortalSettings.vue | `app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalSettings.vue` | ソース | 設定UI全体 |
| PortalBaseSettings.vue | `app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalBaseSettings.vue` | ソース | 基本設定フォーム |
| PortalConfigurationSettings.vue | `app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalConfigurationSettings.vue` | ソース | カスタムドメイン設定 |
| AddCustomDomainDialog.vue | `app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/AddCustomDomainDialog.vue` | ソース | ドメイン追加ダイアログ |
| DNSConfigurationDialog.vue | `app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/DNSConfigurationDialog.vue` | ソース | DNS設定ダイアログ |
| ConfirmDeletePortalDialog.vue | `app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/ConfirmDeletePortalDialog.vue` | ソース | 削除確認ダイアログ |
| helpcenter.routes.js | `app/javascript/dashboard/routes/dashboard/helpcenter/helpcenter.routes.js` | ソース | ルート定義 |
| actions.js | `app/javascript/dashboard/store/modules/helpCenterPortals/actions.js` | ソース | Vuexアクション |
| portals.js | `app/javascript/dashboard/api/helpCenter/portals.js` | ソース | APIクライアント |
| portals_controller.rb | `app/controllers/api/v1/accounts/portals_controller.rb` | ソース | コントローラー |
| portal.rb | `app/models/portal.rb` | ソース | モデル定義 |
