# 画面設計書 24-連絡先編集画面

## 概要

本ドキュメントは、Chatwootの連絡先編集画面の設計仕様を記述したものである。

### 本画面の処理概要

連絡先編集画面は、特定の連絡先の詳細情報を表示し、編集する機能を提供する画面である。

**業務上の目的・背景**：顧客サポート業務において、顧客情報の正確な管理は信頼関係の構築と効率的なサポート提供に不可欠である。本画面により、オペレーターは連絡先の基本情報（名前、メール、電話番号など）の編集、カスタム属性の設定、過去の会話履歴の確認、内部メモの追加、重複連絡先の統合など、包括的な顧客情報管理を行うことができる。

**画面へのアクセス方法**：連絡先一覧画面から特定の連絡先をクリックすることでアクセスする。URLは `/app/accounts/:accountId/contacts/:contactId` の形式となる。

**主要な操作・処理内容**：
1. 連絡先基本情報の表示・編集（名前、メール、電話番号、識別子など）
2. カスタム属性の表示・編集
3. 会話履歴の確認
4. ノート（内部メモ）の追加・編集・削除
5. 連絡先のマージ（統合）
6. 連絡先のブロック/ブロック解除
7. 連絡先一覧への戻り

**画面遷移**：連絡先一覧画面（通常、セグメント別、ラベル別）から遷移。連絡先一覧に戻ることが可能。

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

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 55 | 連絡先CRUD | 主機能 | 連絡先情報の編集 |
| 59 | カスタム属性 | 補助機能 | カスタム属性値の設定 |
| 60 | 会話履歴表示 | 補助機能 | 過去の会話一覧の表示 |
| 61 | ノート管理 | 補助機能 | 連絡先メモの追加・編集 |
| 64 | アバター管理 | 補助機能 | 連絡先アバターの設定 |
| 58 | 連絡先マージ | 補助機能 | 重複連絡先の統合 |

## 画面種別

詳細 / 編集

## URL/ルーティング

| パス | ルート名 | 説明 |
|------|----------|------|
| `/app/accounts/:accountId/contacts/:contactId` | contacts_edit | 連絡先編集画面 |
| `/app/accounts/:accountId/contacts/:contactId/segments/:segmentId` | contacts_edit_segment | セグメント経由の連絡先編集 |
| `/app/accounts/:accountId/contacts/:contactId/labels/:label` | contacts_edit_label | ラベル経由の連絡先編集 |

## 入出力項目

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

| 項目名 | 型 | 必須 | 説明 |
|--------|-----|------|------|
| contactId | パスパラメータ | 必須 | 編集対象の連絡先ID |
| segmentId | パスパラメータ | 任意 | 遷移元セグメントID |
| label | パスパラメータ | 任意 | 遷移元ラベル名 |

### 編集可能項目

| 項目名 | 型 | 必須 | 説明 |
|--------|-----|------|------|
| name | 文字列 | 任意 | 連絡先名 |
| email | 文字列 | 任意 | メールアドレス |
| phone_number | 文字列 | 任意 | 電話番号 |
| identifier | 文字列 | 任意 | 外部識別子 |
| additional_attributes | オブジェクト | 任意 | 追加属性（ソーシャルプロファイルなど） |
| custom_attributes | オブジェクト | 任意 | カスタム属性 |
| avatar | ファイル | 任意 | プロフィール画像 |
| blocked | 真偽値 | 任意 | ブロック状態 |

## 表示項目

### メインエリア

| 項目名 | 説明 | データソース |
|--------|------|--------------|
| アバター | 連絡先のプロフィール画像 | contact.avatar |
| 名前 | 連絡先の表示名 | contact.name |
| メールアドレス | 連絡先のメールアドレス | contact.email |
| 電話番号 | 連絡先の電話番号 | contact.phone_number |
| 識別子 | 外部システムの識別子 | contact.identifier |
| 作成日時 | 連絡先の作成日時 | contact.created_at |
| 最終活動日時 | 最後のアクティビティ日時 | contact.last_activity_at |
| ブロック状態 | ブロックされているか | contact.blocked |

### サイドバー（タブ切り替え）

| タブ名 | 内容 |
|--------|------|
| 属性（Attributes） | カスタム属性の表示・編集 |
| 履歴（History） | 過去の会話一覧 |
| ノート（Notes） | 内部メモの一覧・追加 |
| マージ（Merge） | 連絡先の統合機能 |

## イベント仕様

### 1-画面初期表示

画面マウント時、連絡先詳細情報を取得する。

**処理フロー**:
1. `route.params.contactId` から連絡先IDを取得
2. `contacts/show` アクションで連絡先詳細を取得
3. `contacts/fetchContactableInbox` で関連インボックスを取得
4. `contactNotes/get` で連絡先ノートを取得
5. `contactConversations/get` で会話履歴を取得
6. `attributes/get` でカスタム属性定義を取得

### 2-連絡先情報更新

フォームで編集後、保存ボタンをクリックする。

**処理フロー**:
1. 入力値をバリデーション
2. `contacts/update` アクションを実行
3. 成功時、成功メッセージを表示
4. 失敗時、エラーメッセージを表示

### 3-ブロック/ブロック解除

ブロックボタンをクリックしてブロック状態を切り替える。

**処理フロー**:
1. 現在のブロック状態を反転
2. `contacts/update` アクションで `blocked` フィールドを更新
3. 成功時、成功メッセージを表示

### 4-タブ切り替え

サイドバーのタブを切り替える。

**処理フロー**:
1. `activeTab` 状態を更新
2. 対応するコンポーネント（ContactCustomAttributes, ContactHistory, ContactNotes, ContactMerge）を表示

### 5-ノート追加

ノートタブでノートを追加する。

**処理フロー**:
1. ノート内容を入力
2. `contactNotes/create` アクションを実行
3. ノート一覧を再取得

### 6-連絡先マージ

マージタブで他の連絡先と統合する。

**処理フロー**:
1. 統合対象の連絡先を検索・選択
2. マージ確認
3. `contacts/merge` アクションを実行
4. 成功時、連絡先一覧に戻る

### 7-一覧に戻る

戻るボタンをクリックして連絡先一覧に戻る。

**処理フロー**:
1. ブラウザ履歴があれば `router.back()`
2. なければ連絡先一覧画面にナビゲート

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面初期表示 | contacts, contact_inboxes, notes, conversations | SELECT | 連絡先関連情報取得 |
| 連絡先情報更新 | contacts | UPDATE | 連絡先情報の更新 |
| ブロック切り替え | contacts | UPDATE | blocked フィールド更新 |
| ノート追加 | notes | INSERT | 新規ノート作成 |
| 連絡先マージ | contacts | UPDATE/DELETE | 連絡先の統合処理 |

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

#### contacts

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | id = :contactId | 詳細情報取得 |
| UPDATE | name, email, phone_number, identifier, additional_attributes, custom_attributes, blocked | フォーム入力値 | 更新時 |

#### notes

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | contact_id = :contactId | ノート一覧取得 |
| INSERT | content, contact_id, user_id | ノート内容、連絡先ID、ユーザーID | ノート追加時 |

## メッセージ仕様

| 種別 | コード | メッセージ | 表示条件 |
|------|--------|-----------|----------|
| 成功 | CONTACTS_LAYOUT.HEADER.ACTIONS.BLOCK_SUCCESS_MESSAGE | 連絡先がブロックされました | ブロック成功時 |
| 成功 | CONTACTS_LAYOUT.HEADER.ACTIONS.UNBLOCK_SUCCESS_MESSAGE | 連絡先のブロックが解除されました | ブロック解除成功時 |
| エラー | CONTACTS_LAYOUT.HEADER.ACTIONS.BLOCK_ERROR_MESSAGE | ブロックに失敗しました | ブロック失敗時 |
| エラー | CONTACTS_LAYOUT.HEADER.ACTIONS.UNBLOCK_ERROR_MESSAGE | ブロック解除に失敗しました | ブロック解除失敗時 |

## 例外処理

| 例外条件 | 処理内容 |
|----------|---------|
| 連絡先が見つからない | エラー状態を表示、一覧へのナビゲーションリンクを提供 |
| API通信エラー | ローディング状態を解除し、エラーアラートを表示 |
| 権限不足 | 画面へのアクセスがブロックされ、権限エラー画面を表示 |
| マージ失敗 | エラーメッセージを表示、マージ処理を中断 |

## 備考

- サイドバーはタブ形式で4つの機能（属性、履歴、ノート、マージ）を切り替え表示
- マージ成功後は連絡先一覧に自動で戻る
- 戻るボタンはブラウザ履歴を優先的に使用
- カスタム属性の定義は別途 `attributes/get` で取得される

---

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

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

### 推奨読解順序

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

まず、連絡先と関連データのデータ構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index.js | `app/javascript/dashboard/store/modules/contacts/index.js` | stateの構造を確認 |
| 1-2 | contactNotes/index.js | `app/javascript/dashboard/store/modules/contactNotes.js` | ノートのデータ構造を確認 |
| 1-3 | contactConversations/index.js | `app/javascript/dashboard/store/modules/contactConversations.js` | 会話履歴のデータ構造を確認 |

**読解のコツ**: Vuexストアの`state`オブジェクトを見ることで、どのようなデータが管理されているか把握できる。

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

処理の起点となるVueコンポーネントを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ContactManageView.vue | `app/javascript/dashboard/routes/dashboard/contacts/pages/ContactManageView.vue` | 画面のメインコンポーネント |
| 2-2 | routes.js | `app/javascript/dashboard/routes/dashboard/contacts/routes.js` | ルーティング定義、行43-67 |

**主要処理フロー**:
1. **行31**: `selectedContact`でルートパラメータからcontactIdを取得し、連絡先を取得
2. **行65-73**: `fetchActiveContact`で連絡先詳細とインボックス情報を取得
3. **行79-87**: ノートと会話履歴を取得
4. **行93-118**: `toggleContactBlock`でブロック状態を切り替え
5. **行120-125**: `onMounted`で初期データをすべて取得

#### Step 3: サイドバーコンポーネントを理解する

各タブのコンポーネントを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ContactCustomAttributes.vue | `app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactCustomAttributes.vue` | カスタム属性の表示・編集 |
| 3-2 | ContactHistory.vue | `app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactHistory.vue` | 会話履歴の表示 |
| 3-3 | ContactNotes.vue | `app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactNotes.vue` | ノートの表示・追加 |
| 3-4 | ContactMerge.vue | `app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactMerge.vue` | 連絡先マージ機能 |

#### Step 4: Vuexストアアクションを理解する

連絡先操作のビジネスロジックを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | actions.js | `app/javascript/dashboard/store/modules/contacts/actions.js` | show, update, mergeアクション |

**主要処理フロー**:
- **行98-111**: `show`アクションで連絡先詳細取得
- **行113-134**: `update`アクションで連絡先更新
- **行251-261**: `merge`アクションで連絡先統合

#### Step 5: APIレイヤーを理解する

バックエンドとの通信処理を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | contacts.js | `app/javascript/dashboard/api/contacts.js` | show, updateメソッド |

**主要処理フロー**:
- **行30-32**: `show`メソッドで連絡先詳細取得
- **行34-36**: `update`メソッドで連絡先更新（PATCH）

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

```
ContactManageView.vue (View層)
    │
    ├─ onMounted
    │      ├─ fetchActiveContact
    │      │      ├─ store.dispatch('contacts/show')
    │      │      │      └─ ContactAPI.show()
    │      │      └─ store.dispatch('contacts/fetchContactableInbox')
    │      │             └─ ContactAPI.getContactableInboxes()
    │      │
    │      ├─ fetchContactNotes
    │      │      └─ store.dispatch('contactNotes/get')
    │      │
    │      ├─ fetchContactConversations
    │      │      └─ store.dispatch('contactConversations/get')
    │      │
    │      └─ fetchAttributes
    │             └─ store.dispatch('attributes/get')
    │
    ├─ toggleContactBlock
    │      └─ store.dispatch('contacts/update', { blocked })
    │
    └─ サイドバーコンポーネント
           ├─ ContactCustomAttributes (属性タブ)
           ├─ ContactHistory (履歴タブ)
           ├─ ContactNotes (ノートタブ)
           └─ ContactMerge (マージタブ)
                  └─ store.dispatch('contacts/merge')
```

### データフロー図

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

contactId (URL) ─────────▶ contacts/show
                                 │
                                 ▼
                          ContactAPI.show()
                                 │
                                 ▼
                          Rails API (/api/v1/accounts/:account_id/contacts/:id)
                                 │
                                 ▼
                          Vuexストア更新 ───▶ メインエリア表示
                                 │
                                 ├─ contactNotes/get ───▶ ノートタブ
                                 ├─ contactConversations/get ───▶ 履歴タブ
                                 └─ attributes/get ───▶ 属性タブ
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ContactManageView.vue | `app/javascript/dashboard/routes/dashboard/contacts/pages/ContactManageView.vue` | ソース | メイン画面コンポーネント |
| routes.js | `app/javascript/dashboard/routes/dashboard/contacts/routes.js` | ソース | ルーティング定義 |
| actions.js | `app/javascript/dashboard/store/modules/contacts/actions.js` | ソース | Vuexアクション定義 |
| contacts.js | `app/javascript/dashboard/api/contacts.js` | ソース | 連絡先API |
| ContactsDetailsLayout.vue | `app/javascript/dashboard/components-next/Contacts/ContactsDetailsLayout.vue` | ソース | 詳細レイアウトコンポーネント |
| ContactDetails.vue | `app/javascript/dashboard/components-next/Contacts/Pages/ContactDetails.vue` | ソース | 連絡先詳細コンポーネント |
| ContactCustomAttributes.vue | `app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactCustomAttributes.vue` | ソース | カスタム属性コンポーネント |
| ContactHistory.vue | `app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactHistory.vue` | ソース | 会話履歴コンポーネント |
| ContactNotes.vue | `app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactNotes.vue` | ソース | ノートコンポーネント |
| ContactMerge.vue | `app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactMerge.vue` | ソース | マージコンポーネント |
| TabBar.vue | `app/javascript/dashboard/components-next/tabbar/TabBar.vue` | ソース | タブバーコンポーネント |
| contactNotes.js | `app/javascript/dashboard/store/modules/contactNotes.js` | ソース | ノートストア |
| contactConversations.js | `app/javascript/dashboard/store/modules/contactConversations.js` | ソース | 会話履歴ストア |
