# 画面設計書 16-未対応会話一覧

## 概要

本ドキュメントは、Chatwootの未対応会話一覧画面の設計仕様を記述したものです。エージェントからの返信がまだ行われていない会話（Unattended）を表示する画面です。

### 本画面の処理概要

**業務上の目的・背景**：
顧客対応において、問い合わせに対する初回返信時間は顧客満足度に直結する重要な指標です。未対応会話一覧は、まだエージェントが返信していない会話を一元的に表示し、対応漏れを防止します。特にSLA（サービスレベルアグリーメント）を重視する組織にとって、この画面は初回応答時間を管理するための重要なツールとなります。

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

**主要な操作・処理内容**：
1. 未対応会話一覧の表示
2. 担当者タブ（自分/未割り当て/すべて）による追加フィルタリング
3. ステータスフィルター（Open/Resolved/Pending/Snoozed）による絞り込み
4. 会話の選択と詳細表示
5. 一括操作（複数会話のステータス変更、割り当て変更など）
6. 高度なフィルター機能

**画面遷移**：
- 遷移元: ダッシュボードホーム画面（サイドバーのUnattendedリンク）
- 遷移先: 会話詳細画面（会話クリック時）

**権限による表示制御**：
- administrator: 全機能へのフルアクセス
- agent: 未対応会話の閲覧・操作
- conversation_manage: 会話の管理操作が可能
- conversation_unassigned_manage: 未割り当て会話タブの表示
- conversation_participating_manage: 参加中会話の管理

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 25 | 会話CRUD | 主機能 | 未対応会話の一覧表示 |
| 27 | 会話ステータス管理 | 補助機能 | ステータスによるフィルタリング |

## 画面種別

一覧

## URL/ルーティング

| ルート名 | パス | 説明 |
|---------|------|------|
| conversation_unattended | `/accounts/:accountId/unattended/conversations` | 未対応会話一覧（詳細なし） |
| conversation_through_unattended | `/accounts/:accountId/unattended/conversations/:conversationId` | 未対応会話一覧（詳細あり） |

## 入出力項目

### ルートパラメータ

| 項目名 | 型 | 必須 | 説明 |
|--------|-----|------|------|
| accountId | Number | Yes | アカウントID |
| conversationId | Number | No | 表示する会話ID（詳細表示時） |

### コンポーネントProps

| 項目名 | 型 | 必須 | 説明 |
|--------|-----|------|------|
| conversationType | String | Yes | 'unattended'固定 |
| conversationId | String/Number | No | 選択中の会話ID |

## 表示項目

### ヘッダー部

| 項目名 | 型 | 説明 |
|--------|-----|------|
| page_title | String | 「Unattended」（i18n: CHAT_LIST.UNATTENDED_HEADING） |
| conversation_count | Number | フィルタリング結果の会話数 |
| filter_button | Button | 高度なフィルターモーダル表示ボタン |

### 会話一覧

| 項目名 | 型 | 説明 |
|--------|-----|------|
| conversation_card | Component | 会話カード（ConversationItem） |
| inbox_icon | Icon | インボックスのアイコン |
| contact_name | String | 連絡先名 |
| last_message_preview | String | 最新メッセージのプレビュー |
| last_activity_time | DateTime | 最終活動時刻 |
| unread_count | Number | 未読メッセージ数 |
| labels | Array | 付与されているラベル一覧 |
| waiting_time | Duration | 待機時間（顧客がいつから待っているか） |

### 担当者タブ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| me_tab | Tab | 自分に割り当てられた会話 |
| unassigned_tab | Tab | 未割り当ての会話 |
| all_tab | Tab | すべての会話 |
| tab_count | Number | 各タブの会話数 |

## イベント仕様

### 1-未対応一覧表示

**トリガー**: サイドバーの「Unattended」リンクをクリック

**処理フロー**:
1. Vue Routerでconversation_unattendedルートへ遷移
2. ConversationView.vueにconversationType='unattended'がpropsとして渡される
3. ChatList.vueのconversationFiltersでconversationType: 'unattended'が設定される
4. fetchConversations()でAPIリクエスト
5. サーバー側で未対応フィルタリングが適用された会話一覧を取得

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

### 2-会話選択

**トリガー**: 会話カードのクリック

**処理フロー**:
1. ConversationItemコンポーネントでクリックイベント発火
2. Vue Routerで`conversation_through_unattended`ルートへ遷移
3. ConversationView.vueのpropsが更新
4. setActiveChat()で選択会話をストアに設定
5. ConversationBox.vueで会話詳細を表示

### 3-リアルタイム更新

**トリガー**: WebSocket経由で新規会話または返信を受信

**処理フロー**:
1. WebSocketイベントを受信
2. isOnUnattendedView()で現在未対応ビューかどうか判定
3. 未対応ビューの場合、addUnattended()で会話を追加/更新
4. エージェントが返信した場合、会話がリストから削除される

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

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 未対応会話一覧取得 | conversations | SELECT | 未対応会話の取得 |
| 未対応会話一覧取得 | messages | SELECT | 最新メッセージの確認 |

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

#### conversations（SELECT）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | conversation_type = 'unattended' | APIパラメータ |
| SELECT | status | フィルター条件 | ステータスフィルター |
| SELECT | assignee_id | フィルター条件 | 担当者フィルター |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|---------|
| CHAT_LIST.UNATTENDED_HEADING | タイトル | Unattended | ページタイトル |
| CHAT_LIST.LIST.404 | 情報 | 会話が見つかりません | フィルタリング結果が0件 |
| CHAT_LIST.EOF | 情報 | すべての会話を表示しました | リスト末尾到達時 |

## 例外処理

| 例外状態 | 表示内容 | 処理 |
|---------|---------|------|
| 未対応会話が0件 | 「会話が見つかりません」メッセージ | |
| ネットワークエラー | ローディング継続または再試行 | |

## 備考

- 「未対応」の定義: 顧客からのメッセージに対してエージェントがまだ返信していない会話
- エージェントが返信すると、その会話は未対応一覧から自動的に削除される
- リアルタイム更新はWebSocket経由で実現
- isOnUnattendedView()ヘルパー関数で未対応ビュー判定

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | conversation.routes.js | `app/javascript/dashboard/routes/dashboard/conversation/conversation.routes.js` | 行156-178で未対応関連ルートを確認 |
| 1-2 | actionHelpers.js | `app/javascript/dashboard/store/modules/conversations/helpers/actionHelpers.js` | isOnUnattendedView()ヘルパー関数 |

**読解のコツ**: conversationType propsがどのように'unattended'として渡されるかを確認してください。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ConversationView.vue | `app/javascript/dashboard/routes/dashboard/conversation/ConversationView.vue` | 行47-50でconversationType propsの定義を確認 |

**主要処理フロー**:
1. **行47-50**: conversationType propsの定義（デフォルト空文字列）
2. **行199-208**: ChatListへのconversation-type propsの受け渡し

#### Step 3: フィルタリングロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ChatList.vue | `app/javascript/dashboard/components/ChatList.vue` | conversationTypeによるフィルタリング処理 |

**主要処理フロー**:
- **行79**: conversationType propsの定義
- **行277-288**: conversationFiltersでconversationTypeをフィルター条件に含める
- **行316-318**: pageTitleで「Unattended」を表示
- **行880-883**: watch(conversationType)で変更時にresetAndFetchData()呼び出し

#### Step 4: リアルタイム更新を理解する

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

**主要処理フロー**:
- **行385-389**: addUnattended - 未対応ビューの場合に会話を更新

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

```
ConversationView.vue
    |
    +-- ChatList.vue (conversationType='unattended' props受け渡し)
    |       |
    |       +-- conversationFilters (computed)
    |       |       conversationType: 'unattended'
    |       |
    |       +-- pageTitle (computed)
    |       |       $t('CHAT_LIST.UNATTENDED_HEADING')
    |       |
    |       +-- fetchConversations()
    |               |
    |               +-- ConversationApi.get({ conversation_type: 'unattended' })
    |
    +-- ConversationBox.vue (選択時)

WebSocket Event
    |
    +-- Vuex Action: addUnattended()
            |
            +-- isOnUnattendedView() -> true
            |
            +-- updateConversation()
```

### データフロー図

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

URL params          --> Vue Router                --> ConversationView.vue
(/unattended/...)       (route matching)              (conversationType='unattended')
                                                          |
                                                          v
                    --> ChatList.vue              --> conversationFilters
                        (conversationType props)      ({ conversationType: 'unattended' })
                                                          |
                                                          v
                    --> ConversationApi.get()     --> API Request
                        ({ conversation_type })       (GET /conversations?conversation_type=unattended)
                                                          |
                                                          v
                    --> Vuex Store                --> 会話一覧表示
                        (allConversations)

WebSocket           --> addUnattended()           --> 会話リスト更新
(new message event)     (リアルタイム更新)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ConversationView.vue | `app/javascript/dashboard/routes/dashboard/conversation/ConversationView.vue` | ソース | ルートコンポーネント |
| conversation.routes.js | `app/javascript/dashboard/routes/dashboard/conversation/conversation.routes.js` | ソース | 未対応ルート定義 |
| ChatList.vue | `app/javascript/dashboard/components/ChatList.vue` | ソース | 会話一覧・フィルタリング |
| conversation.js | `app/javascript/dashboard/api/inbox/conversation.js` | ソース | API（conversation_type パラメータ対応） |
| actionHelpers.js | `app/javascript/dashboard/store/modules/conversations/helpers/actionHelpers.js` | ソース | isOnUnattendedView()ヘルパー |
| conversations/actions.js | `app/javascript/dashboard/store/modules/conversations/actions.js` | ソース | addUnattendedアクション |
