# 画面設計書 104-FAQ一覧画面

## 概要

Captain AIアシスタントのFAQ（よくある質問と回答）を管理するための一覧画面設計書です。承認済みのFAQ回答の表示、検索、新規作成、編集、削除、一括操作を提供します。

### 本画面の処理概要

**業務上の目的・背景**：Captain AIが顧客からの問い合わせに正確に回答するためのナレッジベースとして機能するFAQを管理します。FAQ回答は手動で作成するか、会話から自動生成され、承認後にAIアシスタントの回答に利用されます。管理者は回答の品質を維持するために、FAQの追加・編集・削除を行います。

**画面へのアクセス方法**：Captain機能のサイドナビゲーションから「FAQs」を選択してアクセスします。URLパスは `/app/accounts/:accountId/captain/:assistantId/faqs` です。

**主要な操作・処理内容**：
1. 承認済みFAQ一覧の表示（ページネーション対応）
2. FAQ検索（デバウンス処理付き）
3. 新規FAQ回答の作成
4. 既存FAQの編集
5. FAQの削除（単体・一括）
6. 一括選択と一括削除
7. 保留中FAQへのナビゲーション

**画面遷移**：
- 遷移元：Captainアシスタント一覧画面、サイドナビゲーション
- 遷移先：保留中FAQ一覧画面、会話詳細画面（関連会話リンク）

**権限による表示制御**：administrator および agent ロールがアクセス可能です。CAPTAINフィーチャーフラグが有効で、CloudまたはEnterpriseインストールタイプの場合のみ利用可能です。新規作成ボタンはadministratorのみ表示されます。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 93 | アシスタントレスポンス | 主機能 | FAQ回答の一覧表示、CRUD操作 |

## 画面種別

一覧 / 登録（ダイアログ） / 編集（ダイアログ）

## URL/ルーティング

| パス | ルート名 |
|------|----------|
| `/app/accounts/:accountId/captain/:assistantId/faqs` | `captain_assistants_responses_index` |

## 入出力項目

### 検索・フィルタ入力

| 項目名 | 物理名 | 型 | 必須 | バリデーション | 備考 |
|--------|--------|-----|------|---------------|------|
| 検索クエリ | searchQuery | string | No | - | 500msデバウンス |
| ページ番号 | page | number | No | - | デフォルト1 |

### FAQ作成/編集フォーム

| 項目名 | 物理名 | 型 | 必須 | バリデーション | 備考 |
|--------|--------|-----|------|---------------|------|
| 質問 | question | string | Yes | 必須 | FAQ質問文 |
| 回答 | answer | string | Yes | 必須 | FAQ回答文 |

### 出力項目（一覧）

| 項目名 | 物理名 | 型 | 備考 |
|--------|--------|-----|------|
| FAQ ID | id | number | 内部識別子 |
| 質問 | question | string | FAQ質問文 |
| 回答 | answer | string | FAQ回答文 |
| アシスタント | assistant | object | 関連アシスタント情報 |
| 関連ドキュメント | documentable | object | 元となったドキュメント情報 |
| ステータス | status | string | approved/pending |
| 作成日時 | created_at | datetime | 作成日時 |
| 更新日時 | updated_at | datetime | 更新日時 |

## 表示項目

### ヘッダー領域

| 項目名 | 表示形式 | 備考 |
|--------|----------|------|
| タイトル | "FAQs" | ヘッダータイトル |
| Know moreボタン | ポップオーバー | Feature Spotlight表示 |
| Add newボタン | プライマリボタン | administratorのみ表示 |

### 検索・フィルタ領域

| 項目名 | 表示形式 | 備考 |
|--------|----------|------|
| 検索入力フィールド | テキスト入力 | プレースホルダー付き |
| 一括選択バー | セレクションバー | 選択時のみ表示 |

### バナー表示

| 項目名 | 表示形式 | 条件 | 備考 |
|--------|----------|------|------|
| 制限バナー | LimitBanner | 80%超過時 | 使用量警告 |
| 保留中FAQバナー | Banner (blue) | pendingCount > 0 | 保留中件数通知 |

### FAQカード

| 項目名 | 表示形式 | 備考 |
|--------|----------|------|
| 質問 | テキスト（太字） | カードタイトル |
| 回答 | テキスト | カード本文 |
| アシスタント情報 | バッジ | 関連アシスタント |
| 関連ドキュメント | リンク | クリックで詳細表示 |
| 作成/更新日時 | 相対日時 | フォーマット済み |
| アクションメニュー | ドロップダウン | 編集/削除 |
| 選択チェックボックス | チェックボックス | ホバー時または選択モード時表示 |

### ページネーション

| 項目名 | 表示形式 | 備考 |
|--------|----------|------|
| 現在ページ | テキスト | ページ番号表示 |
| 総件数 | テキスト | 総レコード数 |
| ページ送りボタン | ボタン群 | 前後ナビゲーション |

## イベント仕様

### 1-FAQ新規作成

1. 「Add new」ボタンをクリック
2. `handleCreate()` が呼び出される
3. `dialogType` を 'create' に設定
4. `CreateResponseDialog` がモーダル表示される
5. フォーム入力後、Submit時に `captainResponses/create` アクションが dispatch される
6. API成功時：成功メッセージ表示、ダイアログクローズ、一覧更新
7. API失敗時：エラーメッセージ表示

### 2-FAQ編集

1. FAQカードのメニューから「Edit」を選択
2. `handleAction({action: 'edit', id})` が呼び出される
3. `selectedResponse` に該当FAQをセット
4. `dialogType` を 'edit' に設定
5. `CreateResponseDialog` が編集モードで表示される

### 3-FAQ削除（単体）

1. FAQカードのメニューから「Delete」を選択
2. `handleAction({action: 'delete', id})` が呼び出される
3. `DeleteDialog` が表示される
4. 確認後、`captainResponses/delete` アクションが dispatch される
5. API成功時：成功メッセージ表示、一覧更新

### 4-一括削除

1. FAQカードのチェックボックスで複数選択
2. BulkSelectBarの削除ボタンをクリック
3. `BulkDeleteDialog` が表示される
4. 確認後、一括削除処理実行
5. `fetchResponseAfterBulkAction()` で一覧更新

### 5-検索

1. 検索フィールドに入力
2. 500msのデバウンス後、`debouncedSearch()` が実行
3. `fetchResponses(1)` でページ1から再取得
4. URLクエリパラメータも更新

### 6-ページ遷移

1. ページネーションボタンをクリック
2. `onPageChange(page)` が呼び出される
3. `fetchResponses(page)` でデータ取得
4. 選択状態がある場合はクリア

### 7-保留中FAQへの遷移

1. 保留中FAQバナーの「Review」ボタンをクリック
2. `navigateToPendingFAQs()` が呼び出される
3. `captain_assistants_responses_pending` へ遷移

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| FAQ一覧取得 | captain_assistant_responses | SELECT | 承認済みFAQ取得 |
| FAQ作成 | captain_assistant_responses | INSERT | 新規FAQ作成 |
| FAQ編集 | captain_assistant_responses | UPDATE | FAQ内容更新 |
| FAQ削除 | captain_assistant_responses | DELETE | FAQ削除 |
| 保留中件数取得 | captain_assistant_responses | SELECT | 保留中件数カウント |

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

#### captain_assistant_responses

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | - | `status = 'approved' AND assistant_id = :assistantId` | 一覧取得条件 |
| SELECT | COUNT(*) | `status = 'pending' AND assistant_id = :assistantId` | 保留中件数 |
| INSERT | question | フォーム入力値 | 質問文 |
| INSERT | answer | フォーム入力値 | 回答文 |
| INSERT | status | 'approved' | 手動作成は即承認 |
| INSERT | assistant_id | URLパラメータ | アシスタントID |
| UPDATE | question, answer | フォーム入力値 | 編集対象項目 |
| DELETE | - | `id = :responseId` | 指定ID削除 |

## メッセージ仕様

| 種別 | メッセージキー | メッセージ内容 |
|------|---------------|---------------|
| 成功 | CAPTAIN.RESPONSES.CREATE.SUCCESS_MESSAGE | FAQ作成成功 |
| エラー | CAPTAIN.RESPONSES.CREATE.ERROR_MESSAGE | FAQ作成失敗 |
| 成功 | CAPTAIN.RESPONSES.EDIT.SUCCESS_MESSAGE | FAQ編集成功 |
| エラー | CAPTAIN.RESPONSES.EDIT.ERROR_MESSAGE | FAQ編集失敗 |
| 成功 | CAPTAIN.RESPONSES.DELETE.SUCCESS_MESSAGE | FAQ削除成功 |
| エラー | CAPTAIN.RESPONSES.DELETE.ERROR_MESSAGE | FAQ削除失敗 |

## 例外処理

| 例外条件 | 処理内容 | 表示 |
|----------|----------|------|
| データ取得中 | isFetchingフラグでローディング表示 | Spinner |
| FAQ0件 | 空状態表示 | ResponsePageEmptyState |
| 検索結果0件 | 空状態表示 | 検索結果なしメッセージ |
| API通信エラー | アラート表示 | エラーメッセージ |
| 最後の1件削除後 | 前のページに遷移 | 自動ページ調整 |
| Captain未有効 | Paywallコンポーネント表示 | アップグレード案内 |

## 備考

- 検索はdebounce処理（500ms）で不要なAPI呼び出しを抑制
- URLクエリパラメータに検索条件とページ番号を保持（ブラウザバック対応）
- 一括選択モードではカードのアクションメニューが非表示になる
- ホバー時にチェックボックスを表示するインタラクティブなUI
- LimitBannerで使用量が80%を超えた場合に警告表示

---

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

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

### 推奨読解順序

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

まず、FAQデータの構造とストアの状態管理を理解することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | response.js (store) | `app/javascript/dashboard/store/captain/response.js` | ストア定義、getPendingCount、fetchPendingCountを確認 |
| 1-2 | response.js (api) | `app/javascript/dashboard/api/captain/response.js` | APIクライアント、getメソッドのパラメータを確認 |

**読解のコツ**: `createStore` ベースで基本CRUDが自動生成され、カスタムで `getPendingCount`、`fetchPendingCount`、`removeBulkResponses`、`updateBulkResponses` が追加されています。

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Index.vue (responses) | `app/javascript/dashboard/routes/dashboard/captain/responses/Index.vue` | メインコンポーネントの構造を確認 |

**主要処理フロー**:
1. **27-31行目**: ストアゲッターの取得（uiFlags, responseMeta, responses）
2. **44行目**: `pendingCount` ゲッター
3. **98-112行目**: `fetchResponses()` でデータ取得、URLパラメータ更新
4. **180-182行目**: `debouncedSearch` でデバウンス検索
5. **196-202行目**: `onMounted` で初期化

#### Step 3: 一括操作を理解する

一括選択と一括削除の処理フローを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Index.vue | `app/javascript/dashboard/routes/dashboard/captain/responses/Index.vue` | bulkSelectedIds、handleCardSelect、BulkSelectBarを確認 |
| 3-2 | BulkSelectBar.vue | `app/javascript/dashboard/components-next/captain/assistant/BulkSelectBar.vue` | 一括選択バーの実装 |

**主要処理フロー**:
- **115行目**: `bulkSelectedIds` はSetで管理
- **136-140行目**: `handleCardSelect` でトグル操作
- **142-158行目**: `fetchResponseAfterBulkAction` で削除後の再取得処理

#### Step 4: API通信層を理解する

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | response.js (api) | `app/javascript/dashboard/api/captain/response.js` | getメソッドでstatus, search, assistantId, documentIdパラメータ対応 |

**主要処理フロー**:
- **5行目**: `captain/assistant_responses` エンドポイント
- **9-19行目**: `get` メソッドでフィルタリングパラメータ対応

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

```
Index.vue (responses)
    |
    +-- PageLayout.vue
    |       |
    |       +-- ヘッダー、ボタン、ページネーション
    |
    +-- FeatureSpotlightPopover.vue
    |
    +-- Input.vue (検索)
    |       |
    |       +-- @input -> debouncedSearch()
    |
    +-- BulkSelectBar.vue
    |       |
    |       +-- 一括選択/解除、一括削除
    |
    +-- LimitBanner.vue
    |       |
    |       +-- 使用量警告
    |
    +-- Banner.vue (保留中通知)
    |       |
    |       +-- @action -> navigateToPendingFAQs()
    |
    +-- ResponseCard.vue (複数)
    |       |
    |       +-- @action -> handleAction()
    |       +-- @select -> handleCardSelect()
    |       +-- @hover -> handleCardHover()
    |       +-- @navigate -> handleNavigationAction()
    |
    +-- DeleteDialog.vue
    |       |
    |       +-- 単体削除確認
    |
    +-- BulkDeleteDialog.vue
    |       |
    |       +-- 一括削除確認
    |
    +-- CreateResponseDialog.vue
            |
            +-- 作成/編集フォーム
            +-- captainResponses/create or update
```

### データフロー図

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

URLアクセス ──────────▶ onMounted
                              │
                              ├── initializeFromURL()
                              │       │
                              │       +── fetchResponses(page)
                              │
                              └── fetchPendingCount()
                                      │
                                      ▼
                        GET /api/v1/.../captain/assistant_responses
                              │
                              ▼
                        Vuex State更新
                              │
                              ▼
                        ResponseCard描画 ────────▶ FAQ一覧表示

検索入力 ────────────▶ debouncedSearch (500ms)
                              │
                              ▼
                        fetchResponses(1)
                              │
                              ▼
                        URLクエリ更新 + State更新

一括選択 ────────────▶ handleCardSelect
                              │
                              ▼
                        bulkSelectedIds (Set)
                              │
                              ▼
                        BulkSelectBar表示

一括削除 ────────────▶ BulkDeleteDialog
                              │
                              ▼
                        captainBulkActions/handleBulkDelete
                              │
                              ▼
                        fetchResponseAfterBulkAction()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Index.vue | `app/javascript/dashboard/routes/dashboard/captain/responses/Index.vue` | ソース | メイン画面コンポーネント |
| captain.routes.js | `app/javascript/dashboard/routes/dashboard/captain/captain.routes.js` | 設定 | ルーティング定義 |
| response.js (store) | `app/javascript/dashboard/store/captain/response.js` | ソース | Vuexストアモジュール |
| response.js (api) | `app/javascript/dashboard/api/captain/response.js` | ソース | APIクライアント |
| ResponseCard.vue | `app/javascript/dashboard/components-next/captain/assistant/ResponseCard.vue` | ソース | FAQカードコンポーネント |
| BulkSelectBar.vue | `app/javascript/dashboard/components-next/captain/assistant/BulkSelectBar.vue` | ソース | 一括選択バー |
| CreateResponseDialog.vue | `app/javascript/dashboard/components-next/captain/pageComponents/response/CreateResponseDialog.vue` | ソース | 作成/編集ダイアログ |
| DeleteDialog.vue | `app/javascript/dashboard/components-next/captain/pageComponents/DeleteDialog.vue` | ソース | 削除確認ダイアログ |
| BulkDeleteDialog.vue | `app/javascript/dashboard/components-next/captain/pageComponents/BulkDeleteDialog.vue` | ソース | 一括削除確認ダイアログ |
| ResponsePageEmptyState.vue | `app/javascript/dashboard/components-next/captain/pageComponents/emptyStates/ResponsePageEmptyState.vue` | ソース | 空状態表示 |
| LimitBanner.vue | `app/javascript/dashboard/components-next/captain/pageComponents/response/LimitBanner.vue` | ソース | 使用量警告バナー |
| PageLayout.vue | `app/javascript/dashboard/components-next/captain/PageLayout.vue` | ソース | ページレイアウト |
| integrations.json | `app/javascript/dashboard/i18n/locale/en/integrations.json` | 設定 | 多言語リソース |
