# 画面設計書 21-セグメント別連絡先一覧

## 概要

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

### 本画面の処理概要

セグメント別連絡先一覧画面は、保存済みのカスタムフィルタ（セグメント）に基づいて連絡先をフィルタリングし、一覧表示する機能を提供する画面である。

**業務上の目的・背景**：顧客サポート業務において、特定の条件に合致する連絡先グループを効率的に管理・参照することが求められる。セグメント機能により、ユーザーは事前に定義したフィルタ条件（例：地域、購入履歴、タグなど）に基づく連絡先リストへ即座にアクセスでき、ターゲットを絞ったコミュニケーションやキャンペーン対象者の抽出が可能となる。

**画面へのアクセス方法**：ダッシュボードのサイドバーから「連絡先」メニューを選択し、サブメニュー内の特定セグメント名をクリックすることでアクセスする。URLは `/app/accounts/:accountId/contacts/segments/:segmentId` の形式となる。

**主要な操作・処理内容**：
1. セグメント条件に合致する連絡先の一覧表示
2. 連絡先の検索（検索クエリによる絞り込み）
3. ソート順の変更（名前、最終活動日時など）
4. ページネーションによる一覧のページ切り替え
5. 連絡先の選択とバルク操作（ラベル付与、削除）
6. 連絡先詳細画面への遷移

**画面遷移**：連絡先一覧画面、ラベル別連絡先一覧から遷移可能。各連絡先をクリックすると連絡先編集画面に遷移する。

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

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 55 | 連絡先CRUD | 主機能 | セグメント別連絡先の表示 |
| 79 | カスタムフィルタ | 補助機能 | セグメントフィルタの適用 |

## 画面種別

一覧

## URL/ルーティング

| パス | ルート名 | 説明 |
|------|----------|------|
| `/app/accounts/:accountId/contacts/segments/:segmentId` | contacts_dashboard_segments_index | セグメント別連絡先一覧 |

## 入出力項目

### 入力項目

| 項目名 | 型 | 必須 | 説明 |
|--------|-----|------|------|
| segmentId | パスパラメータ | 必須 | 表示するセグメントのID |
| page | クエリパラメータ | 任意 | ページ番号（デフォルト: 1） |
| search | クエリパラメータ | 任意 | 検索キーワード |

### 出力項目

| 項目名 | 型 | 説明 |
|--------|-----|------|
| contacts | Array | 連絡先オブジェクトの配列 |
| meta | Object | ページネーション情報（count, currentPage, hasMore） |

## 表示項目

### 連絡先一覧

| 項目名 | 説明 | データソース |
|--------|------|--------------|
| アバター | 連絡先のプロフィール画像 | contact.avatar |
| 名前 | 連絡先の表示名 | contact.name |
| メールアドレス | 連絡先のメールアドレス | contact.email |
| 電話番号 | 連絡先の電話番号 | contact.phone_number |
| 最終活動日時 | 最後のアクティビティ日時 | contact.last_activity_at |
| ラベル | 付与されているラベル一覧 | contact.labels |

### ヘッダー情報

| 項目名 | 説明 |
|--------|------|
| セグメント名 | 現在表示中のセグメント名 |
| 総件数 | フィルタに合致する連絡先の総数 |

## イベント仕様

### 1-画面初期表示

画面マウント時、セグメントIDに基づいてフィルタ条件を取得し、連絡先をフィルタリングして一覧を表示する。

**処理フロー**:
1. `customViews/getContactCustomViews` getterからセグメント一覧を取得
2. `activeSegmentId`に対応するセグメントの`query`を取得
3. `contacts/filter` アクションを実行してフィルタ済み連絡先を取得
4. 結果をストアに保存し、画面に表示

### 2-検索実行

検索ボックスに入力後、デバウンス処理（300ms）を経て検索APIを呼び出す。

**処理フロー**:
1. 入力値をデバウンス処理
2. `contacts/search` アクションを実行
3. URLのクエリパラメータに検索条件を反映
4. 検索結果を画面に表示

### 3-ソート変更

ソートメニューから並び順を選択すると、指定条件で再取得を実行する。

**処理フロー**:
1. ソート条件を`sortState`に保存
2. `updateUISettings`でソート設定を永続化
3. `fetchSavedOrAppliedFilteredContact`を実行
4. 再取得した結果を表示

### 4-ページ変更

ページネーションコントロールでページを切り替える。

**処理フロー**:
1. `fetchContactsBasedOnContext`を新しいページ番号で実行
2. URLのpageパラメータを更新
3. 新しいページのデータを表示

### 5-バルク操作（ラベル付与）

選択した連絡先に対してラベルを一括付与する。

**処理フロー**:
1. 連絡先チェックボックスで選択
2. ラベル選択ポップアップからラベルを選択
3. `BulkActionsAPI.create` でラベル付与リクエスト送信
4. 成功時、一覧を再取得して表示を更新

### 6-バルク操作（削除）

選択した連絡先を一括削除する。

**処理フロー**:
1. 連絡先チェックボックスで選択
2. 削除ボタンをクリック
3. 確認ダイアログを表示
4. 確認後、`BulkActionsAPI.create` で削除リクエスト送信
5. 成功時、一覧を再取得

### 7-連絡先詳細遷移

連絡先行をクリックすると詳細編集画面に遷移する。

**処理フロー**:
1. 連絡先IDを取得
2. `/app/accounts/:accountId/contacts/:contactId` に遷移

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面初期表示 | contacts | SELECT | セグメント条件でフィルタした連絡先取得 |
| 検索実行 | contacts | SELECT | 検索条件で連絡先を検索 |
| ラベル付与 | contacts_labels | INSERT | 選択連絡先にラベルを関連付け |
| 連絡先削除 | contacts | DELETE | 選択連絡先を削除 |

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

#### contacts

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | セグメントのquery条件に合致 | フィルタ条件はJSON形式で送信 |
| DELETE | id | 選択されたcontact_ids | バルク削除時 |

#### contacts_labels

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | contact_id, label_id | 選択連絡先ID、選択ラベルID | バルクラベル付与時 |

## メッセージ仕様

| 種別 | コード | メッセージ | 表示条件 |
|------|--------|-----------|----------|
| 成功 | CONTACTS_BULK_ACTIONS.ASSIGN_LABELS_SUCCESS | ラベルが正常に付与されました | バルクラベル付与成功時 |
| 成功 | CONTACTS_BULK_ACTIONS.DELETE_SUCCESS | 連絡先が正常に削除されました | バルク削除成功時 |
| エラー | CONTACTS_BULK_ACTIONS.ASSIGN_LABELS_FAILED | ラベルの付与に失敗しました | バルクラベル付与失敗時 |
| エラー | CONTACTS_BULK_ACTIONS.DELETE_FAILED | 連絡先の削除に失敗しました | バルク削除失敗時 |
| 情報 | CONTACTS_LAYOUT.EMPTY_STATE.LIST_EMPTY_STATE_TITLE | 連絡先が見つかりません | 検索結果が空の場合 |

## 例外処理

| 例外条件 | 処理内容 |
|----------|---------|
| セグメントが見つからない | セグメント一覧が空の場合、通常の連絡先一覧にフォールバック |
| API通信エラー | ローディング状態を解除し、エラーアラートを表示 |
| 権限不足 | 画面へのアクセスがブロックされ、権限エラー画面を表示 |
| フィーチャーフラグ無効 | CRM機能が無効な場合、画面へのアクセス不可 |

## 備考

- セグメントはカスタムビュー（customViews）として管理され、連絡先用とチケット用で分類されている
- ソート設定はUI設定として永続化され、ユーザーごとに保持される
- 検索時は無限スクロールが有効になり、ページネーションからスクロール読み込みに切り替わる

---

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

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

### 推奨読解順序

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

まず、連絡先とセグメント（カスタムビュー）のデータ構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index.js | `app/javascript/dashboard/store/modules/contacts/index.js` | stateの構造（records, meta, uiFlags, appliedFilters）を確認 |
| 1-2 | customViews/index.js | `app/javascript/dashboard/store/modules/customViews.js` | カスタムビュー（セグメント）のデータ構造を確認 |

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

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ContactsIndex.vue | `app/javascript/dashboard/routes/dashboard/contacts/pages/ContactsIndex.vue` | 画面のメインコンポーネント、状態管理とイベントハンドラを確認 |
| 2-2 | routes.js | `app/javascript/dashboard/routes/dashboard/contacts/routes.js` | ルーティング定義とメタ情報（権限、機能フラグ）を確認 |

**主要処理フロー**:
1. **行62**: `activeSegmentId`がルートパラメータから取得される
2. **行93-96**: `activeSegment`でセグメント情報を取得
3. **行200-208**: `fetchSavedOrAppliedFilteredContact`でフィルタ済み連絡先を取得
4. **行416-433**: `onMounted`で初期データ取得を実行

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

連絡先取得のビジネスロジックを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | actions.js | `app/javascript/dashboard/store/modules/contacts/actions.js` | filter, search, getアクションの実装を確認 |

**主要処理フロー**:
- **行281-301**: `filter`アクションでフィルタ条件を使った連絡先取得
- **行48-66**: `search`アクションで検索処理
- **行68-81**: `get`アクションで通常の連絡先取得

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | contacts.js | `app/javascript/dashboard/api/contacts.js` | filter, search, getメソッドのAPI呼び出しを確認 |
| 4-2 | bulkActions.js | `app/javascript/dashboard/api/bulkActions.js` | バルク操作のAPI呼び出しを確認 |

**主要処理フロー**:
- **行76-79**: `filter`メソッドでPOSTリクエストによるフィルタ検索
- **行60-68**: `search`メソッドでGETリクエストによる検索

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

```
ContactsIndex.vue (View層)
    │
    ├─ onMounted / fetchContactsBasedOnContext
    │      │
    │      ├─ fetchSavedOrAppliedFilteredContact
    │      │      └─ store.dispatch('contacts/filter')
    │      │             └─ ContactAPI.filter()
    │      │                    └─ axios.post('/contacts/filter')
    │      │
    │      └─ fetchContacts / fetchActiveContacts
    │             └─ store.dispatch('contacts/get')
    │                    └─ ContactAPI.get()
    │
    ├─ searchContacts (検索時)
    │      └─ store.dispatch('contacts/search')
    │             └─ ContactAPI.search()
    │
    ├─ assignLabels (バルクラベル付与)
    │      └─ BulkActionsAPI.create()
    │
    └─ deleteContacts (バルク削除)
           └─ BulkActionsAPI.create()
```

### データフロー図

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

segmentId (URL) ───────▶ customViews/getContactCustomViews ───▶ セグメント条件取得
                               │
                               ▼
                        contacts/filter
                               │
                               ▼
                        ContactAPI.filter()
                               │
                               ▼
                        Rails API (/api/v1/accounts/:account_id/contacts/filter)
                               │
                               ▼
                        Vuexストア更新 ───▶ 画面表示更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ContactsIndex.vue | `app/javascript/dashboard/routes/dashboard/contacts/pages/ContactsIndex.vue` | ソース | メイン画面コンポーネント |
| routes.js | `app/javascript/dashboard/routes/dashboard/contacts/routes.js` | ソース | ルーティング定義 |
| actions.js | `app/javascript/dashboard/store/modules/contacts/actions.js` | ソース | Vuexアクション定義 |
| getters.js | `app/javascript/dashboard/store/modules/contacts/getters.js` | ソース | Vuexゲッター定義 |
| mutations.js | `app/javascript/dashboard/store/modules/contacts/mutations.js` | ソース | Vuexミューテーション定義 |
| contacts.js | `app/javascript/dashboard/api/contacts.js` | ソース | 連絡先API |
| bulkActions.js | `app/javascript/dashboard/api/bulkActions.js` | ソース | バルク操作API |
| ContactsListLayout.vue | `app/javascript/dashboard/components-next/Contacts/ContactsListLayout.vue` | ソース | リストレイアウトコンポーネント |
| ContactsList.vue | `app/javascript/dashboard/components-next/Contacts/Pages/ContactsList.vue` | ソース | 連絡先リストコンポーネント |
| ContactsBulkActionBar.vue | `app/javascript/dashboard/routes/dashboard/contacts/components/ContactsBulkActionBar.vue` | ソース | バルクアクションバー |
| filterQueryGenerator.js | `app/javascript/dashboard/helper/filterQueryGenerator.js` | ソース | フィルタクエリ生成ヘルパー |
| customViews.js | `app/javascript/dashboard/store/modules/customViews.js` | ソース | カスタムビュー（セグメント）ストア |
