# 画面設計書 22-ラベル別連絡先一覧

## 概要

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

### 本画面の処理概要

ラベル別連絡先一覧画面は、特定のラベルが付与された連絡先をフィルタリングし、一覧表示する機能を提供する画面である。

**業務上の目的・背景**：顧客サポートやマーケティング業務において、連絡先を分類・整理するためにラベル機能が活用される。本画面により、特定のラベル（例：VIP顧客、問い合わせ中、フォローアップ必要など）が付与された連絡先のみを効率的に参照・管理でき、ターゲットを絞ったアクションが可能となる。

**画面へのアクセス方法**：ダッシュボードのサイドバーから「連絡先」メニューを展開し、表示されるラベル一覧から対象ラベルを選択することでアクセスする。URLは `/app/accounts/:accountId/contacts/labels/:label` の形式となる。

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

**画面遷移**：連絡先一覧画面、セグメント別連絡先一覧から遷移可能。各連絡先をクリックすると連絡先編集画面に遷移する。

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

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 55 | 連絡先CRUD | 主機能 | ラベル別連絡先の表示 |
| 78 | ラベル作成・管理 | 補助機能 | ラベルによるフィルタリング |

## 画面種別

一覧

## URL/ルーティング

| パス | ルート名 | 説明 |
|------|----------|------|
| `/app/accounts/:accountId/contacts/labels/:label` | contacts_dashboard_labels_index | ラベル別連絡先一覧 |

## 入出力項目

### 入力項目

| 項目名 | 型 | 必須 | 説明 |
|--------|-----|------|------|
| label | パスパラメータ | 必須 | フィルタリングするラベル名 |
| 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-画面初期表示

画面マウント時、ラベル名をパラメータとして連絡先一覧を取得する。

**処理フロー**:
1. `route.params.label` からラベル名を取得
2. `contacts/get` アクションをラベルパラメータ付きで実行
3. 結果をストアに保存し、画面に表示

### 2-検索実行

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

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

### 3-ソート変更

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

**処理フロー**:
1. ソート条件を`sortState`に保存
2. `updateUISettings`でソート設定を永続化
3. `fetchContacts`をラベルパラメータ付きで実行
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/labels/:label` に遷移

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

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

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

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

#### contacts

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | labels[]パラメータに指定ラベル名 | ラベル名でJOIN検索 |
| 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機能が無効な場合、画面へのアクセス不可 |

## 備考

- ラベル名はURLエンコードされてパスパラメータとして渡される
- ヘッダータイトルには `#` プレフィックスが付与される（例：#VIP顧客）
- ラベルフィルタとセグメントフィルタは排他的に動作する（同時適用されない）
- ソート設定はUI設定として永続化され、ユーザーごとに保持される

---

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

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

### 推奨読解順序

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index.js | `app/javascript/dashboard/store/modules/contacts/index.js` | stateの構造（records, meta, uiFlags）を確認 |
| 1-2 | labels/index.js | `app/javascript/dashboard/store/modules/labels.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` | ルーティング定義、行30-33のラベル用ルート |

**主要処理フロー**:
1. **行61**: `activeLabel`がルートパラメータから取得される
2. **行128**: ヘッダータイトルに `#${activeLabel.value}` が設定される
3. **行193-198**: `fetchContacts`でlabelパラメータ付きで連絡先取得
4. **行284-286**: ラベル変更を監視し、変更時にデータ再取得

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | actions.js | `app/javascript/dashboard/store/modules/contacts/actions.js` | get, searchアクションのlabelパラメータ処理 |

**主要処理フロー**:
- **行68-81**: `get`アクションでlabelパラメータを使った連絡先取得
- **行48-66**: `search`アクションでlabelパラメータ付き検索

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | contacts.js | `app/javascript/dashboard/api/contacts.js` | buildContactParamsでlabels[]パラメータ構築 |

**主要処理フロー**:
- **行4-13**: `buildContactParams`でラベルパラメータをクエリ文字列に追加
- **行20-28**: `get`メソッドでラベル付きリクエスト

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

```
ContactsIndex.vue (View層)
    │
    ├─ onMounted / watch(activeLabel)
    │      │
    │      └─ fetchContacts (labelパラメータ付き)
    │             └─ store.dispatch('contacts/get', { label })
    │                    └─ ContactAPI.get(page, sortAttr, label)
    │                           └─ axios.get('/contacts?labels[]=xxx')
    │
    ├─ searchContacts (検索時)
    │      └─ store.dispatch('contacts/search', { label })
    │             └─ ContactAPI.search(search, page, sortAttr, label)
    │
    ├─ assignLabels (バルクラベル付与)
    │      └─ BulkActionsAPI.create()
    │
    └─ deleteContacts (バルク削除)
           └─ BulkActionsAPI.create()
```

### データフロー図

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

label (URL) ─────────────▶ ContactAPI.get({ label })
                                  │
                                  ▼
                           Rails API (/api/v1/accounts/:account_id/contacts?labels[]=xxx)
                                  │
                                  ▼
                           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アクション定義 |
| contacts.js | `app/javascript/dashboard/api/contacts.js` | ソース | 連絡先API |
| bulkActions.js | `app/javascript/dashboard/api/bulkActions.js` | ソース | バルク操作API |
| labels.js | `app/javascript/dashboard/store/modules/labels.js` | ソース | ラベルストア |
| 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` | ソース | バルクアクションバー |
