# 画面設計書 20-連絡先一覧画面

## 概要

本ドキュメントは、Chatwootの連絡先一覧画面の設計仕様を記述したものです。CRM機能の中核となる連絡先管理画面で、連絡先の一覧表示、検索、フィルタリング、一括操作などを行います。

### 本画面の処理概要

**業務上の目的・背景**：
連絡先一覧画面は、Chatwootの顧客管理（CRM）機能のメイン画面です。すべての連絡先を一元管理し、検索・フィルタリング・ソートによって目的の連絡先を素早く見つけることができます。また、一括操作（バルクアクション）機能により、複数の連絡先に対してラベル付与や削除などを効率的に実行できます。

**画面へのアクセス方法**：
1. サイドバーの「Contacts」リンクをクリック
2. URL直接アクセス: `/app/accounts/:accountId/contacts`

**主要な操作・処理内容**：
1. 連絡先一覧の表示
2. 連絡先の検索（名前、メール、電話番号など）
3. フィルタリング（カスタムフィルター適用）
4. ソート順の変更（最終活動日時、名前など）
5. 連絡先の新規作成
6. 一括操作（ラベル付与、削除）
7. ページネーションによる追加読み込み
8. 連絡先詳細画面への遷移

**画面遷移**：
- 遷移元: ダッシュボードホーム画面（サイドバーのContactsリンク）
- 遷移先: 連絡先編集画面（連絡先クリック時）、セグメント別・ラベル別連絡先一覧（サイドバー選択時）

**権限による表示制御**：
- administrator: 全機能へのフルアクセス
- agent: 連絡先の閲覧・編集
- contact_manage: 連絡先の管理操作が可能

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 55 | 連絡先CRUD | 主機能 | 連絡先の一覧表示・作成 |
| 56 | 連絡先検索・フィルタリング | 補助機能 | 連絡先の検索とフィルタリング |
| 57 | 連絡先インポート・エクスポート | 補助機能 | CSV経由でのインポート・エクスポート |
| 62 | ラベル管理 | API連携 | 連絡先へのラベル付与 |
| 188 | バルク接触アクション | 補助機能 | 複数連絡先の一括処理 |

## 画面種別

一覧

## URL/ルーティング

| ルート名 | パス | 説明 |
|---------|------|------|
| contacts_dashboard_index | `/accounts/:accountId/contacts` | 連絡先一覧（基本） |
| contacts_dashboard_segments_index | `/accounts/:accountId/contacts/segments/:segmentId` | セグメント別一覧 |
| contacts_dashboard_labels_index | `/accounts/:accountId/contacts/labels/:label` | ラベル別一覧 |
| contacts_dashboard_active | `/accounts/:accountId/contacts/active` | アクティブ連絡先一覧 |

## 入出力項目

### ルートパラメータ

| 項目名 | 型 | 必須 | 説明 |
|--------|-----|------|------|
| accountId | Number | Yes | アカウントID |
| segmentId | Number | No | セグメントID（セグメントビュー時） |
| label | String | No | ラベル名（ラベルビュー時） |

### クエリパラメータ

| 項目名 | 型 | 必須 | 説明 |
|--------|-----|------|------|
| page | Number | No | ページ番号（デフォルト: 1） |
| search | String | No | 検索クエリ |

### ソート設定（UISettings）

| 項目名 | 型 | 必須 | 説明 |
|--------|-----|------|------|
| contacts_sort_by | String | No | ソート設定（例: "-last_activity_at"） |

## 表示項目

### ヘッダー部（ContactsListLayout）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| header_title | String | 画面タイトル（「Contacts」、検索時は「Search Results」など） |
| search_input | Component | 検索入力フィールド |
| sort_dropdown | Component | ソート順選択ドロップダウン |
| filter_button | Component | フィルターモーダル表示ボタン |
| pagination | Component | ページネーションコンポーネント |

### 連絡先一覧（ContactsList）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| contact_id | Number | 連絡先ID |
| name | String | 連絡先名 |
| email | String | メールアドレス |
| phone_number | String | 電話番号 |
| thumbnail | String | アバター画像URL |
| last_activity_at | DateTime | 最終活動日時 |
| created_at | DateTime | 作成日時 |
| labels | Array | 付与されているラベル一覧 |
| custom_attributes | Object | カスタム属性 |

### バルクアクションバー（ContactsBulkActionBar）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| selected_count | Number | 選択された連絡先数 |
| select_all_checkbox | Checkbox | 全選択/全解除チェックボックス |
| assign_labels_button | Button | ラベル付与ボタン |
| delete_button | Button | 削除ボタン |

### 空状態表示（ContactEmptyState）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| title | String | 「No contacts yet」等のタイトル |
| subtitle | String | 説明文 |
| create_button | Button | 連絡先作成ボタン |

## イベント仕様

### 1-連絡先一覧取得

**トリガー**: 画面マウント時またはフィルター変更時

**処理フロー**:
1. onMountedでfetchContactsBasedOnContext()を実行
2. 現在のビュー（通常/検索/アクティブ/セグメント/ラベル）を判定
3. 適切なVuexアクション（contacts/get, contacts/active, contacts/filter, contacts/search）を呼び出し
4. APIレスポンスをストアに保存
5. ContactsListで表示

**Vuexアクション**: `contacts/get`, `contacts/active`, `contacts/filter`, `contacts/search`

### 2-連絡先検索

**トリガー**: 検索入力フィールドへの入力（300msデバウンス）

**処理フロー**:
1. searchContacts()がデバウンス後に呼び出される
2. contacts/searchアクションでAPI呼び出し
3. URLクエリパラメータを更新（search=xxx）
4. 検索結果をストアに保存
5. 検索モードでは無限スクロールを有効化

**Vuexアクション**: `contacts/search`

### 3-ソート変更

**トリガー**: ソートドロップダウンで選択変更

**処理フロー**:
1. handleSort()が呼び出される
2. sortStateを更新
3. updateUISettings()でソート設定を保存
4. 現在のビューに応じて適切なfetch関数を実行
5. 一覧を再取得して表示

**Vuexアクション**: `updateUISettings`

### 4-フィルター適用

**トリガー**: フィルターモーダルで適用ボタンクリック

**処理フロー**:
1. fetchSavedOrAppliedFilteredContact()が呼び出される
2. filterQueryGenerator()でクエリペイロードを生成
3. contacts/filterアクションでAPI呼び出し
4. フィルタリング結果を表示

**Vuexアクション**: `contacts/filter`

### 5-連絡先選択

**トリガー**: 連絡先カードのチェックボックスクリック

**処理フロー**:
1. toggleContactSelection()が呼び出される
2. selectedContactIdsにIDを追加/削除
3. hasSelectionがtrueになるとBulkActionBarを表示

### 6-バルク削除

**トリガー**: バルクアクションバーの削除ボタンクリック

**処理フロー**:
1. openBulkDeleteDialog()で確認ダイアログ表示
2. ダイアログで確定後、deleteContacts()が呼び出される
3. BulkActionsAPI.create()でサーバーへ送信
4. 成功時、連絡先一覧を再取得
5. 選択状態をクリア

**Vuexアクション**: BulkActionsAPIを使用（Vuex経由ではない）

### 7-ラベル一括付与

**トリガー**: バルクアクションバーのラベルボタンクリック

**処理フロー**:
1. assignLabels()が呼び出される
2. BulkActionsAPI.create()でサーバーへ送信
3. 成功時、連絡先一覧を再取得
4. 選択状態をクリア

### 8-連絡先新規作成

**トリガー**: 空状態のCreateボタン、またはヘッダーの追加ボタン

**処理フロー**:
1. createContact()が呼び出される
2. contacts/createアクションでAPI呼び出し
3. 作成された連絡先をストアに追加

**Vuexアクション**: `contacts/create`

### 9-ページネーション

**トリガー**: ページネーションコントロールでページ変更

**処理フロー**:
1. fetchContactsBasedOnContext(page)が呼び出される
2. URLクエリパラメータを更新（page=xxx）
3. 指定ページの連絡先を取得

### 10-無限スクロール（検索モード）

**トリガー**: 検索結果のスクロールで末尾に到達

**処理フロー**:
1. loadMoreSearchResults()が呼び出される
2. hasMoreがtrueの場合のみ実行
3. 次のページを取得してappend: trueで既存リストに追加

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 連絡先一覧取得 | contacts | SELECT | 連絡先一覧の取得 |
| 連絡先検索 | contacts | SELECT | 検索条件での取得 |
| フィルター適用 | contacts | SELECT | フィルター条件での取得 |
| 連絡先作成 | contacts | INSERT | 新規連絡先の作成 |
| バルク削除 | contacts | DELETE | 複数連絡先の削除 |
| ラベル付与 | contact_labels | INSERT | ラベル関連付けの追加 |

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

#### contacts（SELECT）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | account_id = current_account_id | ページネーション付き |
| SELECT | * | labels[] = label | ラベルフィルター時 |
| SELECT | * | queryPayload | カスタムフィルター時 |

#### contacts（INSERT）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | name | フォーム入力値 | |
| INSERT | email | フォーム入力値 | |
| INSERT | phone_number | フォーム入力値 | |
| INSERT | account_id | current_account_id | |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|---------
| CONTACTS_LAYOUT.HEADER.TITLE | タイトル | Contacts | 通常表示時 |
| CONTACTS_LAYOUT.HEADER.SEARCH_TITLE | タイトル | Search Results | 検索表示時 |
| CONTACTS_LAYOUT.HEADER.ACTIVE_TITLE | タイトル | Active Contacts | アクティブ表示時 |
| CONTACTS_LAYOUT.EMPTY_STATE.TITLE | 情報 | No contacts yet | 連絡先が0件 |
| CONTACTS_LAYOUT.EMPTY_STATE.SUBTITLE | 情報 | 説明文 | 連絡先が0件 |
| CONTACTS_LAYOUT.EMPTY_STATE.LIST_EMPTY_STATE_TITLE | 情報 | No contacts found | フィルター結果が0件 |
| CONTACTS_LAYOUT.EMPTY_STATE.SEARCH_EMPTY_STATE_TITLE | 情報 | No contacts found for search | 検索結果が0件 |
| CONTACTS_LAYOUT.EMPTY_STATE.ACTIVE_EMPTY_STATE_TITLE | 情報 | No active contacts | アクティブが0件 |
| CONTACTS_BULK_ACTIONS.ASSIGN_LABELS_SUCCESS | 成功 | Labels assigned successfully | ラベル付与成功 |
| CONTACTS_BULK_ACTIONS.ASSIGN_LABELS_FAILED | エラー | Failed to assign labels | ラベル付与失敗 |
| CONTACTS_BULK_ACTIONS.DELETE_SUCCESS | 成功 | Contacts deleted successfully | 削除成功 |
| CONTACTS_BULK_ACTIONS.DELETE_FAILED | エラー | Failed to delete contacts | 削除失敗 |

## 例外処理

| 例外状態 | 表示内容 | 処理 |
|---------|---------|------|
| 連絡先が0件 | ContactEmptyState表示 | 空状態コンポーネント表示 |
| 検索結果が0件 | 空状態メッセージ表示 | emptyStateMessage表示 |
| フェッチ中 | Spinner表示 | isFetchingList時 |
| バルク削除失敗 | エラーアラート | DELETE_FAILEDメッセージ |
| ネットワークエラー | サイレントエラー | catchブロックで処理 |

## 備考

- CRM機能（FEATURE_FLAGS.CRM）が有効な場合にのみアクセス可能
- 検索はデバウンス処理（300ms）で最適化
- 検索モードでは無限スクロール、通常モードではページネーション
- ソート設定はUISettingsに保存され、セッション間で維持
- バルクアクションは専用のBulkActionsAPIを使用
- セグメント・ラベル・アクティブビューは同じContactsIndex.vueコンポーネントで処理

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | contacts/routes.js | `app/javascript/dashboard/routes/dashboard/contacts/routes.js` | 連絡先関連ルートの構造 |
| 1-2 | contacts/getters.js | `app/javascript/dashboard/store/modules/contacts/getters.js` | 連絡先ストアのgetter定義 |

**読解のコツ**: ルートのmeta設定（permissions, featureFlag）と、getters（getContactsList, getMeta, getUIFlags）の役割を理解してください。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ContactsIndex.vue | `app/javascript/dashboard/routes/dashboard/contacts/pages/ContactsIndex.vue` | 連絡先一覧のメインコンポーネント |

**主要処理フロー**:
1. **行29-34**: useMapGetterでストアデータを取得
2. **行36-41**: クエリパラメータとref状態の管理
3. **行56-59**: ソート状態のreactive管理
4. **行61-68**: computedによる現在のビュー状態判定
5. **行193-198**: fetchContacts - 通常の連絡先取得
6. **行220-241**: searchContacts - 検索（デバウンス付き）
7. **行259-286**: fetchContactsBasedOnContext - コンテキストに応じた取得
8. **行310-331**: deleteContacts - バルク削除
9. **行416-433**: onMounted処理

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | contacts/actions.js | `app/javascript/dashboard/store/modules/contacts/actions.js` | 連絡先関連アクション |

**主要処理フロー**:
- **行48-66**: search - 連絡先検索
- **行68-81**: get - 連絡先一覧取得
- **行83-96**: active - アクティブ連絡先取得
- **行136-156**: create - 連絡先作成
- **行281-301**: filter - フィルター適用

#### Step 4: APIクライアントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | contacts.js | `app/javascript/dashboard/api/contacts.js` | 連絡先API |

**主要エンドポイント**:
- **行20-28**: get() - 一覧取得
- **行60-68**: search() - 検索
- **行70-73**: active() - アクティブ連絡先
- **行76-79**: filter() - フィルター

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

```
ContactsIndex.vue (ルートコンポーネント)
    |
    +-- ContactsListLayout.vue (レイアウト)
    |       |
    |       +-- 検索入力
    |       +-- ソートドロップダウン
    |       +-- フィルターモーダル
    |       +-- ページネーション
    |
    +-- ContactsBulkActionBar.vue (バルクアクション)
    |       |
    |       +-- 選択カウント表示
    |       +-- ラベル付与ボタン
    |       +-- 削除ボタン
    |
    +-- ContactEmptyState.vue (空状態)
    |       |
    |       +-- 作成ボタン
    |
    +-- ContactsList.vue (連絡先リスト)
    |       |
    |       +-- ContactCard (各連絡先)
    |               |
    |               +-- チェックボックス
    |               +-- アバター
    |               +-- 名前・メール・電話
    |
    +-- Dialog.vue (削除確認ダイアログ)
```

### データフロー図

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

URL params          --> Vue Router                --> ContactsIndex.vue
(/contacts)             (route matching)              (ルートマッチング)
                                                          |
                                                          v
                    --> useMapGetter              --> contacts
                        (contacts/getContactsList)    (連絡先一覧)
                                                          |
                                                          v
Search input        --> searchContacts()          --> contacts/search
(debounce 300ms)        (デバウンス)                  (API呼び出し)
                                                          |
                                                          v
                    --> Vuex Store                --> ContactsList
                        (contacts module)             (連絡先表示)

Selection           --> toggleContactSelection()  --> selectedContactIds
(checkbox click)                                          |
                                                          v
                    --> ContactsBulkActionBar     --> バルクアクション表示
                        (hasSelection)

Bulk Delete         --> deleteContacts()          --> BulkActionsAPI.create()
                                                          |
                                                          v
                    --> fetchContactsBasedOnContext() --> 一覧再取得
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ContactsIndex.vue | `app/javascript/dashboard/routes/dashboard/contacts/pages/ContactsIndex.vue` | ソース | 連絡先一覧メイン |
| routes.js | `app/javascript/dashboard/routes/dashboard/contacts/routes.js` | ソース | ルーティング定義 |
| ContactsListLayout.vue | `app/javascript/dashboard/components-next/Contacts/ContactsListLayout.vue` | ソース | レイアウトコンポーネント |
| ContactsList.vue | `app/javascript/dashboard/components-next/Contacts/Pages/ContactsList.vue` | ソース | 連絡先リスト |
| ContactEmptyState.vue | `app/javascript/dashboard/components-next/Contacts/EmptyState/ContactEmptyState.vue` | ソース | 空状態表示 |
| ContactsBulkActionBar.vue | `app/javascript/dashboard/routes/dashboard/contacts/components/ContactsBulkActionBar.vue` | ソース | バルクアクションバー |
| contacts/actions.js | `app/javascript/dashboard/store/modules/contacts/actions.js` | ソース | 連絡先アクション |
| contacts/getters.js | `app/javascript/dashboard/store/modules/contacts/getters.js` | ソース | 連絡先ゲッター |
| contacts.js | `app/javascript/dashboard/api/contacts.js` | ソース | 連絡先API |
| bulkActions.js | `app/javascript/dashboard/api/bulkActions.js` | ソース | バルクアクションAPI |
