# 画面設計書 96-カテゴリ一覧画面

## 概要

本ドキュメントは、Chatwootのヘルプセンター機能における「カテゴリ一覧画面」の設計仕様を記述したものである。

### 本画面の処理概要

カテゴリ一覧画面は、ヘルプセンターポータル内の記事カテゴリを一覧表示し、カテゴリの作成・編集・削除を行う画面である。カテゴリをクリックすると、そのカテゴリに属する記事一覧を表示できる。

**業務上の目的・背景**：ヘルプセンターの記事を論理的に分類・整理するために、カテゴリ機能が必要である。ユーザーが目的の情報を素早く見つけられるよう、記事を適切なカテゴリに分類することで、ナレッジベースのナビゲーション性を向上させる。多言語対応のため、カテゴリはロケールごとに管理される。

**画面へのアクセス方法**：ヘルプセンターのサイドナビゲーションから「Categories」タブをクリック、または直接URL `/app/accounts/:accountId/portals/:portalSlug/:locale/categories` にアクセスする。

**主要な操作・処理内容**：
1. カテゴリ一覧を取得して表示
2. ロケール切り替えによるカテゴリの再取得
3. カテゴリカードクリックでそのカテゴリの記事一覧へ遷移
4. 「New category」ボタンでカテゴリ作成ダイアログを開く
5. カテゴリの編集・削除

**画面遷移**：
- 遷移元：サイドナビゲーション、記事一覧画面
- 遷移先：カテゴリ別記事一覧画面（portals_categories_articles_index）

**権限による表示制御**：administrator、agent、knowledge_base_manage権限を持つユーザーがアクセス可能。HELP_CENTERフィーチャーフラグが有効である必要がある。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 153 | ポータルカテゴリ | 主機能 | 記事カテゴリの一覧表示 |

## 画面種別

一覧

## URL/ルーティング

| 項目 | 値 |
|------|-----|
| パス | `/app/accounts/:accountId/portals/:portalSlug/:locale/categories` |
| ルート名 | `portals_categories_index` |
| コンポーネント | `PortalsCategoriesIndexPage.vue` |

## 入出力項目

### 入力項目（URLパラメータ）

| 項目名 | 型 | 必須 | 説明 |
|--------|-----|------|------|
| accountId | Number | Yes | アカウントID |
| portalSlug | String | Yes | ポータルのスラッグ |
| locale | String | Yes | ロケールコード |

### 出力項目

| 項目名 | 型 | 説明 |
|--------|-----|------|
| categories | Array | カテゴリ一覧 |
| allowedLocales | Array | 利用可能ロケール一覧 |

## 表示項目

### ヘッダーコントロール

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ロケールセレクト | Select | ロケール切り替え |
| カテゴリ数 | Text | "N categories" |
| 新規カテゴリボタン | Button | "New category" |

### カテゴリカード（CategoryCard）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | Number | カテゴリID |
| name | String | カテゴリ名 |
| description | String | カテゴリ説明 |
| slug | String | カテゴリスラッグ |
| articlesCount | Number | 所属記事数 |
| アクションメニュー | Dropdown | Edit / Delete |

## イベント仕様

### 1-画面初期表示

**トリガー**: コンポーネントのマウント時（onMounted）

**処理フロー**:
1. URLパラメータからportalSlug, localeを取得
2. `categories/index` アクションをディスパッチ
3. GET `/api/v1/accounts/:accountId/portals/:portalSlug/categories?locale=xx` を呼び出し
4. 取得したカテゴリをCategoryListコンポーネントで表示

### 2-ロケール変更

**トリガー**: ロケールセレクト変更

**処理フロー**:
1. handleLocaleChange関数を実行
2. ルートパラメータのlocaleを更新
3. `fetchCategories` イベントを発火
4. カテゴリ一覧を再取得

### 3-カテゴリクリック

**トリガー**: カテゴリカードクリック

**処理フロー**:
1. openCategoryArticles関数を実行
2. クリックされたカテゴリのslugを取得
3. `portals_categories_articles_index` ルートへ遷移

### 4-カテゴリ編集

**トリガー**: アクションメニューから「Edit」選択

**処理フロー**:
1. handleAction関数で action='edit' を検出
2. 選択されたカテゴリをselectedCategoryに設定
3. EditCategoryDialogを開く

### 5-カテゴリ削除

**トリガー**: アクションメニューから「Delete」選択

**処理フロー**:
1. handleAction関数で action='delete' を検出
2. deleteCategory関数を実行
3. `categories/delete` アクションをディスパッチ
4. DELETE `/api/v1/accounts/:accountId/portals/:portalSlug/categories/:categoryId` を呼び出し
5. 成功時：成功メッセージを表示
6. 失敗時：エラーメッセージを表示
7. イベントトラッキング（PORTALS_EVENTS.DELETE_CATEGORY）

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| カテゴリ取得 | categories | SELECT | ポータル・ロケールに紐づくカテゴリ一覧取得 |
| カテゴリ削除 | categories | DELETE | カテゴリレコードを削除 |
| カテゴリ削除 | articles | UPDATE | category_idをnullに更新（nullify） |

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

#### categories

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | portal_id, locale | 全カラム取得 |
| DELETE | - | id = categoryId | カスケード削除 |

#### articles（関連更新）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | category_id | NULL | カテゴリ削除時に自動 |

## メッセージ仕様

| 種別 | メッセージキー | メッセージ内容 | 表示条件 |
|------|--------------|--------------|----------|
| カテゴリ数 | HELP_CENTER.CATEGORY_PAGE.CATEGORY_HEADER.CATEGORIES_COUNT | "{n} category \| {n} categories" | 常時 |
| 新規作成ボタン | HELP_CENTER.CATEGORY_PAGE.CATEGORY_HEADER.NEW_CATEGORY | "New category" | 常時 |
| 空状態タイトル | HELP_CENTER.CATEGORY_PAGE.CATEGORY_EMPTY_STATE.TITLE | "No categories found" | カテゴリなし時 |
| 空状態サブタイトル | HELP_CENTER.CATEGORY_PAGE.CATEGORY_EMPTY_STATE.SUBTITLE | "Categories will appear here..." | カテゴリなし時 |
| 削除成功 | HELP_CENTER.CATEGORY_PAGE.CATEGORY_DIALOG.DELETE.API.SUCCESS_MESSAGE | "Category deleted successfully" | 削除成功時 |
| 削除失敗 | HELP_CENTER.CATEGORY_PAGE.CATEGORY_DIALOG.DELETE.API.ERROR_MESSAGE | "Unable to delete category" | 削除失敗時 |

## 例外処理

| 例外状況 | 処理内容 |
|---------|---------|
| カテゴリ取得失敗 | 空のリストを表示 |
| カテゴリ削除失敗 | useAlertでエラーメッセージを表示 |
| 権限不足 | ルートガードによりアクセス拒否 |

## 備考

- カテゴリ削除時、そのカテゴリに属する記事のcategory_idはnullになる（記事自体は削除されない）
- カテゴリはロケールごとに独立して管理される
- フィーチャーフラグ `HELP_CENTER` が有効である必要がある

---

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

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

### 推奨読解順序

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

カテゴリのデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Category (Model) | `app/models/category.rb` | カテゴリのスキーマ定義、articlesとの関連 |
| 1-2 | index.js (categories) | `app/javascript/dashboard/store/modules/helpCenterCategories/index.js` | Vuexストアの初期状態 |

**読解のコツ**: Category モデルの has_many :articles, dependent: :nullify を確認し、削除時の動作を理解する。

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

画面のエントリーポイントとコンポーネント構成を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | PortalsCategoriesIndexPage.vue | `app/javascript/dashboard/routes/dashboard/helpcenter/pages/PortalsCategoriesIndexPage.vue` | 画面コンポーネントのメイン処理 |
| 2-2 | CategoriesPage.vue | `app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoriesPage.vue` | UI表示ロジック |
| 2-3 | helpcenter.routes.js | `app/javascript/dashboard/routes/dashboard/helpcenter/helpcenter.routes.js` | ルート定義 |

**主要処理フロー**:
1. **35-40行目**: fetchCategoriesByPortalSlugAndLocale関数でカテゴリ取得
2. **54-56行目**: onMountedで初期データ取得

#### Step 3: 子コンポーネントを理解する

カテゴリ一覧表示のUIコンポーネントを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | CategoriesPage.vue | `app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoriesPage.vue` | ヘッダーコントロール、カテゴリリスト |
| 3-2 | CategoryHeaderControls.vue | `app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoryHeaderControls.vue` | ロケール選択・新規作成UI |
| 3-3 | CategoryList.vue | `app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoryList.vue` | カテゴリカードリスト |

**主要処理フロー**:
- **CategoriesPage.vue 59-61行目**: openCategoryArticles関数でカテゴリ記事一覧へ遷移
- **CategoriesPage.vue 68-88行目**: deleteCategory関数で削除処理
- **CategoriesPage.vue 90-100行目**: handleAction関数でアクション振り分け

#### Step 4: Vuex Actionsを理解する

カテゴリ取得・削除のAPI呼び出し処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | actions.js (categories) | `app/javascript/dashboard/store/modules/helpCenterCategories/actions.js` | index, deleteアクション |
| 4-2 | categories.js (API) | `app/javascript/dashboard/api/helpCenter/categories.js` | get, delete APIメソッド |

**主要処理フロー**:
- **5-24行目**: indexアクション - カテゴリ一覧取得
- **72-94行目**: deleteアクション - カテゴリ削除

#### Step 5: バックエンドAPIを理解する

RailsコントローラーでのAPI処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | categories_controller.rb | `app/controllers/api/v1/accounts/categories_controller.rb` | index, destroyアクション |

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

```
PortalsCategoriesIndexPage.vue
    │
    ├─ onMounted()
    │      └─ fetchCategoriesByPortalSlugAndLocale()
    │             └─ store.dispatch('categories/index')
    │                    └─ categoriesAPI.get()
    │                           └─ GET /api/v1/.../categories
    │
    └─ CategoriesPage.vue
           │
           ├─ CategoryHeaderControls.vue
           │      └─ @locale-change → handleLocaleChange()
           │             └─ emit('fetchCategories')
           │
           ├─ CategoryList.vue
           │      ├─ @click → openCategoryArticles()
           │      │      └─ router.push('portals_categories_articles_index')
           │      │
           │      └─ @action → handleAction()
           │             ├─ edit → editCategoryDialog.open()
           │             └─ delete → deleteCategory()
           │                    └─ store.dispatch('categories/delete')
           │
           └─ EditCategoryDialog.vue
```

### データフロー図

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

URLパラメータ ────────▶ fetchCategories() ───────────▶ categories[]
  - portalSlug              │
  - locale                  ▼
                       categories/index action
                            │
                            ▼
                       CategoriesAPI.get()
                            │
                            ▼
                       categories (state)
                            │
                            ▼
                       CategoriesPage.vue
                            │
                            ├─▶ CategoryList.vue
                            │      └─▶ CategoryCard[]
                            │
                            └─▶ @click
                                   └─▶ portals_categories_articles_index
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| PortalsCategoriesIndexPage.vue | `app/javascript/dashboard/routes/dashboard/helpcenter/pages/PortalsCategoriesIndexPage.vue` | ソース | 画面コンポーネント |
| CategoriesPage.vue | `app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoriesPage.vue` | ソース | カテゴリ一覧UI |
| CategoryHeaderControls.vue | `app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoryHeaderControls.vue` | ソース | ヘッダーコントロール |
| CategoryList.vue | `app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoryList.vue` | ソース | カテゴリリスト |
| EditCategoryDialog.vue | `app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/EditCategoryDialog.vue` | ソース | 編集ダイアログ |
| helpcenter.routes.js | `app/javascript/dashboard/routes/dashboard/helpcenter/helpcenter.routes.js` | ソース | ルート定義 |
| actions.js | `app/javascript/dashboard/store/modules/helpCenterCategories/actions.js` | ソース | Vuexアクション |
| categories.js | `app/javascript/dashboard/api/helpCenter/categories.js` | ソース | APIクライアント |
| category.rb | `app/models/category.rb` | ソース | モデル定義 |
