# 画面設計書 73-カスタム属性一覧画面

## 概要

本ドキュメントはChatwootのカスタム属性一覧画面に関する設計書です。会話属性とコンタクト属性の定義・管理を行う画面について記述しています。

### 本画面の処理概要

本画面では、会話やコンタクトに付与するカスタム属性を定義・管理します。カスタム属性を使用することで、企業固有の情報（顧客ID、契約プラン、優先度など）を会話やコンタクトに関連付けて管理できます。

**業務上の目的・背景**：企業ごとに異なる顧客管理の要件に対応するため、標準項目だけでなく独自の属性を定義できる機能が必要です。カスタム属性により、CRMとしての柔軟性が向上し、顧客情報の一元管理が可能になります。また、プリチャットフォームでの情報収集や、会話のクローズ時に必須入力とする設定も可能です。

**画面へのアクセス方法**：サイドバーの「設定」から「カスタム属性」メニューを選択してアクセスします。または、URLで直接 `/app/accounts/:accountId/settings/custom-attributes/list` にアクセスすることもできます。

**主要な操作・処理内容**：
1. カスタム属性の一覧表示：会話属性/コンタクト属性をタブで切り替えて表示
2. 新しい属性の追加：属性名、キー、タイプ、説明などを設定して作成
3. 既存属性の編集：属性の表示名や説明を編集
4. 属性の削除：不要な属性を削除
5. バッジ表示：プリチャットフォームで使用中/解決必須属性のマーク表示

**画面遷移**：
- 遷移元：設定ホーム画面
- 遷移先：なし（モーダルで操作完結）

**権限による表示制御**：管理者（administrator）権限を持つユーザーのみがアクセス可能です。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 10 | カスタム属性定義 | 主機能 | カスタム属性の一覧と管理 |

## 画面種別

一覧

## URL/ルーティング

| 項目 | 値 |
|------|-----|
| パス | `/app/accounts/:accountId/settings/custom-attributes/list` |
| ルート名 | `attributes_list` |
| コンポーネント | `Index.vue` |

## 入出力項目

### 属性追加フォーム

| 項目名 | 項目ID | 種別 | 型 | 必須 | バリデーション | 説明 |
|--------|--------|------|-----|------|---------------|------|
| 属性モデル | attributeModel | 入力 | Number | Yes | required | 0:会話属性、1:コンタクト属性 |
| 表示名 | displayName | 入力 | String | Yes | required, minLength(1) | 属性の表示名 |
| キー | attributeKey | 入力 | String | Yes | required, スペース不可 | 属性のキー（slug形式） |
| 説明 | description | 入力 | String | Yes | required | 属性の説明 |
| タイプ | attributeType | 入力 | Number | Yes | required | 属性の型（テキスト、数値、リンク等） |
| リスト値 | values | 入力 | Array | 条件付き | リスト型の場合必須 | 選択肢の値 |
| 正規表現パターン | regexPattern | 入力 | String | No | - | バリデーション用正規表現 |
| 正規表現ヒント | regexCue | 入力 | String | No | - | 入力ヒントメッセージ |

## 表示項目

| 項目名 | データソース | 説明 |
|--------|-------------|------|
| 属性一覧 | derivedAttributes | フィルタされた属性リスト |
| 表示名 | attribute.attribute_display_name | 属性の表示名 |
| タイプ | attribute.attribute_display_type | 属性の型 |
| キー | attribute.attribute_key | 属性のキー |
| バッジ | buildBadges() | pre-chat/resolution必須のバッジ |

## イベント仕様

### 1-タブ切り替え

**トリガー**: タブバーのクリック

**処理フロー**:
1. `onClickTabChange`で`selectedTabIndex`を更新
2. `attributeModel`がcomputedで再計算
3. `attributes`がcomputedで該当モデルの属性のみフィルタ

### 2-属性追加ボタン押下

**トリガー**: 「Add Attribute」ボタンのクリック

**処理フロー**:
1. `toggleAddPopup(true)`で追加モーダルを表示
2. AddAttributeコンポーネントがレンダリング
3. フォーム入力後、送信時に`attributes/create`アクションを呼び出し
4. 成功時、ストアに属性を追加しモーダルを閉じる

### 3-属性編集ボタン押下

**トリガー**: 各属性行の「Edit」ボタンのクリック

**処理フロー**:
1. `handleEditAttribute`で`selectedAttribute`に対象を設定
2. `toggleEditPopup(true)`で編集モーダルを表示
3. EditAttributeコンポーネントがレンダリング
4. フォーム編集後、送信時に`attributes/update`アクションを呼び出し
5. 成功時、ストアの属性を更新しモーダルを閉じる

### 4-属性削除ボタン押下

**トリガー**: 各属性行の「Delete」ボタンのクリック

**処理フロー**:
1. `handleDeleteAttribute`で`selectedAttribute`に対象を設定
2. `toggleDeletePopup(true)`で削除確認モーダルを表示
3. 確認入力後、`confirmDeleteAttribute`を実行
4. `attributes/delete`アクションを呼び出し
5. 成功時、ストアから属性を削除しモーダルを閉じる

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 属性追加 | custom_attribute_definitions | INSERT | 新しいカスタム属性定義の作成 |
| 属性編集 | custom_attribute_definitions | UPDATE | カスタム属性定義の更新 |
| 属性削除 | custom_attribute_definitions | DELETE | カスタム属性定義の削除 |

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

#### custom_attribute_definitions

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | account_id | 現在のアカウントID | - |
| INSERT | attribute_display_name | フォーム入力値 | 表示名 |
| INSERT | attribute_key | フォーム入力値（slug形式） | ユニークキー |
| INSERT | attribute_description | フォーム入力値 | 説明 |
| INSERT | attribute_model | 0 or 1 | 会話/コンタクト |
| INSERT | attribute_display_type | フォーム選択値 | 型ID |
| INSERT | attribute_values | フォーム入力値 | リスト型の場合の選択肢 |
| INSERT | regex_pattern | フォーム入力値 | バリデーション正規表現 |
| INSERT | regex_cue | フォーム入力値 | 入力ヒント |
| UPDATE | attribute_display_name | フォーム入力値 | 編集時 |
| UPDATE | attribute_description | フォーム入力値 | 編集時 |
| DELETE | id | selectedAttribute.id | 削除対象ID |

## メッセージ仕様

| 種別 | キー | メッセージ | 表示条件 |
|------|-----|-----------|---------|
| 成功 | ATTRIBUTES_MGMT.ADD.API.SUCCESS_MESSAGE | 属性が正常に作成されました | 属性追加成功時 |
| エラー | ATTRIBUTES_MGMT.ADD.API.ERROR_MESSAGE | 属性の作成に失敗しました | 属性追加失敗時 |
| 成功 | ATTRIBUTES_MGMT.DELETE.API.SUCCESS_MESSAGE | 属性が正常に削除されました | 属性削除成功時 |
| エラー | ATTRIBUTES_MGMT.DELETE.API.ERROR_MESSAGE | 属性の削除に失敗しました | 属性削除失敗時 |
| 空状態 | ATTRIBUTES_MGMT.LIST.EMPTY_RESULT.404 | 属性が見つかりません | 属性が0件の場合 |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| 権限不足 | 管理者権限がない場合、画面にアクセスできない |
| API通信エラー | エラーメッセージをアラートで表示 |
| 重複キー | バックエンドからのエラーメッセージを表示 |
| 必須項目未入力 | バリデーションエラーを表示 |

## 備考

- 属性タイプ：テキスト(0)、数値(1)、通貨(2)、パーセント(3)、リンク(4)、日付(5)、リスト(6)、チェックボックス(7)
- 属性キーは自動的にslug形式に変換される（displayNameから生成）
- プリチャットフォームで使用中の属性は「Pre-chat」バッジが表示される
- 会話のクローズ時に必須の属性は「Resolution」バッジが表示される
- 正規表現バリデーションはテキスト型の属性のみで利用可能

---

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

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

### 推奨読解順序

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

まず、カスタム属性のデータ構造を理解することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | attributes.js (store) | `app/javascript/dashboard/store/modules/attributes.js` | stateのrecords配列構造、attribute_modelによるフィルタリング |

**読解のコツ**: `getAttributesByModel` getterがattribute_modelでフィルタする仕組みを確認。'conversation_attribute'と'contact_attribute'の2種類がある。

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Index.vue | `app/javascript/dashboard/routes/dashboard/settings/attributes/Index.vue` | タブ切り替えとderived属性の計算ロジック |

**主要処理フロー**:
1. **行66-68**: `onMounted`で`attributes/get`アクションを呼び出し
2. **行70-72**: `attributeModel`でタブインデックスから属性モデルを計算
3. **行74-76**: `attributes`でモデルに応じた属性をフィルタ
4. **行138-146**: `derivedAttributes`でバッジ情報を付加
5. **行108-116**: `hasPreChatBadge`でインボックスのプリチャット設定を確認

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | attributes.js (API) | `app/javascript/dashboard/api/attributes.js` | getAttributesByModelメソッドの実装 |

**主要処理フロー**:
- **行9-11**: `getAttributesByModel`で全属性を取得

#### Step 4: 追加・編集フォームを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | AddAttribute.vue | `app/javascript/dashboard/routes/dashboard/settings/attributes/AddAttribute.vue` | 属性追加フォームの実装 |
| 4-2 | EditAttribute.vue | `app/javascript/dashboard/routes/dashboard/settings/attributes/EditAttribute.vue` | 属性編集フォームの実装 |

**主要処理フロー (AddAttribute)**:
- **行133-136**: `onDisplayNameChange`でキーを自動生成
- **行139-169**: `addAttributes`で属性作成処理

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

```
Index.vue (エントリーポイント)
    │
    ├─ onMounted
    │      └─ store.dispatch('attributes/get')
    │             └─ AttributeAPI.getAttributesByModel()
    │
    ├─ TabBar (タブ切り替え)
    │      └─ onClickTabChange
    │             └─ selectedTabIndex更新
    │                    └─ computed: attributeModel / attributes
    │
    ├─ AttributeListItem (各属性表示)
    │      ├─ @edit → handleEditAttribute
    │      └─ @delete → handleDeleteAttribute
    │
    ├─ AddAttribute (追加モーダル)
    │      └─ addAttributes()
    │             └─ store.dispatch('attributes/create')
    │                    └─ AttributeAPI.create()
    │
    ├─ EditAttribute (編集モーダル)
    │      └─ updateAttribute()
    │             └─ store.dispatch('attributes/update')
    │                    └─ AttributeAPI.update()
    │
    └─ woot-confirm-delete-modal
           └─ confirmDeleteAttribute()
                  └─ store.dispatch('attributes/delete')
                         └─ AttributeAPI.delete()
```

### データフロー図

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

Tab選択 ───────────────▶ selectedTabIndex ────────────▶ attributeModel
       │                        │
       │                        ▼
       │                 getAttributesByModel ────────▶ attributes
       │                        │
       │                        ▼
       └─────────────── buildBadges ──────────────────▶ derivedAttributes
                               │
                               ▼
                       AttributeListItem ─────────────▶ UI表示

[属性CRUD]

AddAttribute form ────▶ attributes/create ────────────▶ 属性追加
EditAttribute form ───▶ attributes/update ────────────▶ 属性更新
Delete confirm ───────▶ attributes/delete ────────────▶ 属性削除
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Index.vue | `app/javascript/dashboard/routes/dashboard/settings/attributes/Index.vue` | ソース | メイン画面コンポーネント |
| AddAttribute.vue | `app/javascript/dashboard/routes/dashboard/settings/attributes/AddAttribute.vue` | ソース | 属性追加モーダル |
| EditAttribute.vue | `app/javascript/dashboard/routes/dashboard/settings/attributes/EditAttribute.vue` | ソース | 属性編集モーダル |
| attributes.js (API) | `app/javascript/dashboard/api/attributes.js` | ソース | API通信クラス |
| attributes.js (store) | `app/javascript/dashboard/store/modules/attributes.js` | ソース | Vuexストアモジュール |
| constants.js | `app/javascript/dashboard/routes/dashboard/settings/attributes/constants.js` | ソース | 属性タイプ・モデル定数 |
| attributes.routes.js | `app/javascript/dashboard/routes/dashboard/settings/attributes/attributes.routes.js` | ソース | ルーティング定義 |
| AttributeListItem.vue | `app/javascript/dashboard/components-next/ConversationWorkflow/AttributeListItem.vue` | ソース | 属性一覧アイテムコンポーネント |
| TabBar.vue | `app/javascript/dashboard/components-next/tabbar/TabBar.vue` | ソース | タブバーコンポーネント |
