# 画面設計書 83-顧客グループ追加画面

## 概要

本ドキュメントは、Medusa Admin Dashboard における顧客グループ追加画面の設計仕様を定義したものである。特定の顧客を複数の顧客グループに追加するためのモーダル形式の画面について記述する。

### 本画面の処理概要

顧客グループ追加画面は、選択した顧客を既存の顧客グループに追加するためのフルスクリーンモーダル形式の画面である。顧客グループの一覧をテーブル形式で表示し、複数のグループを選択して一括で追加できる。

**業務上の目的・背景**：ECサイト運営において、顧客グループは顧客セグメンテーションの基盤となる。VIP顧客、法人顧客、特定地域の顧客など、様々な属性でグループ化することで、ターゲットを絞ったプロモーション、価格設定、マーケティング施策が可能になる。本画面は、個々の顧客を適切なグループに所属させる機能を提供し、顧客関係管理（CRM）の効率化に貢献する。

**画面へのアクセス方法**：顧客詳細画面のグループセクションから「グループを追加」アクションを選択するか、URLパス `/customers/:id/add-customer-groups` に直接アクセスすることで本画面を開く。画面はフルスクリーンモーダル形式で表示される。

**主要な操作・処理内容**：
1. 顧客グループ一覧の表示（ページネーション付き）
2. グループの検索・フィルタリング
3. ソート順の変更（名前、作成日、更新日）
4. チェックボックスによる複数グループの選択
5. 既にメンバーのグループは選択不可として表示
6. 保存ボタンで選択したグループへの追加を確定

**画面遷移**：顧客詳細画面から遷移してくる。保存成功後またはキャンセル時に顧客詳細画面に戻る。

**権限による表示制御**：認証済みの管理者ユーザーのみがアクセス可能。顧客が既に所属しているグループは、チェックボックスが無効化され、ツールチップで説明が表示される。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 28 | 顧客グループメンバー管理 | 主機能 | 顧客をグループに追加 |
| 27 | 顧客グループ管理 | API連携 | 顧客グループ一覧の取得 |

## 画面種別

選択・追加（モーダル/フルスクリーンモーダル形式）

## URL/ルーティング

`/customers/:id/add-customer-groups`

## 入出力項目

| 項目名 | 項目ID | データ型 | 必須 | 最大長 | 入力/出力 | バリデーション | 備考 |
|--------|--------|----------|------|--------|-----------|----------------|------|
| 顧客グループID配列 | customer_group_ids | string[] | 必須 | - | 入力 | 最低1件選択（zod.array(zod.string()).min(1)） | 複数選択可 |

## 表示項目

| 項目名 | 表示位置 | 説明 |
|--------|----------|------|
| 検索ボックス | テーブルヘッダー | グループ名で検索 |
| 選択チェックボックス | 各行先頭 | グループ選択用 |
| グループ名 | テーブルカラム | 顧客グループの名前 |
| 作成日 | テーブルカラム | グループの作成日時 |
| 更新日 | テーブルカラム | グループの更新日時 |
| ソートオプション | テーブルヘッダー | 名前、作成日、更新日でソート |
| バリデーションエラー | ヘッダー | 選択件数不足時のエラー表示 |
| キャンセルボタン | フッター | モーダルを閉じる |
| 保存ボタン | フッター | グループ追加を確定 |

## イベント仕様

### 1-保存ボタン押下

ユーザーが保存ボタンを押下すると、以下の処理が実行される：
1. フォームのバリデーションを実行（最低1件のグループが選択されているか確認）
2. バリデーション成功時、`useBatchCustomerCustomerGroups` フックを通じて API を呼び出し
3. 成功時：成功トーストを表示（追加したグループ名を含む）し、顧客詳細画面 `/customers/:id` に遷移
4. 失敗時：エラートーストを表示

### 2-キャンセルボタン押下

モーダルを閉じて顧客詳細画面に戻る。

### 3-グループ選択

チェックボックスをクリックすると、rowSelection 状態が更新され、フォームの customer_group_ids にも反映される。既に顧客が所属しているグループは選択不可。

### 4-検索

検索ボックスへの入力でリアルタイムにフィルタリングが実行される。

### 5-ページネーション

テーブル下部のページネーションコントロールでページを切り替え可能。1ページあたり10件表示。

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 保存ボタン押下 | customer_group_customer | INSERT | 顧客とグループの紐付けを作成 |

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

#### customer_group_customer（中間テーブル）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | customer_id | URL パラメータの id | 対象顧客 |
| INSERT | customer_group_id | 選択されたグループID | 複数レコード作成 |

## メッセージ仕様

| 種別 | メッセージキー | 表示条件 | メッセージ内容（参考） |
|------|---------------|----------|----------------------|
| 成功 | customers.groups.add.success | 保存成功時 | グループ {グループ名リスト} に追加しました |
| エラー | (動的) | API エラー時 | エラーメッセージを表示 |
| エラー | (フォームエラー) | グループ未選択時 | 最低1件のグループを選択してください |
| 情報 | customers.groups.alreadyAddedTooltip | 既存メンバー時 | この顧客は既にこのグループに所属しています |
| 空状態 | customers.groups.add.list.noRecordsMessage | 検索結果なし時 | 該当するグループがありません |

## 例外処理

| 例外条件 | 処理内容 |
|----------|----------|
| グループ一覧取得エラー | エラーをスローしてエラー境界で処理 |
| API 追加エラー | エラートーストを表示 |
| バリデーションエラー | ヘッダーにエラーメッセージを表示 |

## 備考

- モーダルは RouteFocusModal コンポーネントを使用しており、フルスクリーン形式で表示
- フォームは KeyboundForm を使用しており、キーボードショートカットによる操作が可能
- テーブルは _DataTable コンポーネントを使用し、ページネーション、ソート、フィルタリング機能を提供
- 顧客が既に所属しているグループは `enableRowSelection` で選択不可に設定
- 1ページあたりの表示件数は10件（PAGE_SIZE = 10）
- グループ取得時には顧客情報も含めて取得（fields: "*customers"）

---

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

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

### 推奨読解順序

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

グループ追加のスキーマと選択状態を把握することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | add-customer-groups-form.tsx | `packages/admin/dashboard/src/routes/customers/customers-add-customer-group/components/add-customers-form/add-customer-groups-form.tsx` | AddCustomerGroupsSchema（31-33行目）で定義されるフォームのバリデーションスキーマ |

**読解のコツ**: zod ライブラリによるスキーマ定義。`zod.array(zod.string()).min(1)` で最低1件の選択を要求。

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

ルートコンポーネントの構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | customer-add-customer-groups.tsx | `packages/admin/dashboard/src/routes/customers/customers-add-customer-group/customer-add-customer-groups.tsx` | CustomerAddCustomerGroups コンポーネント（5-13行目）。useParams で顧客IDを取得し、フォームに渡す |

**主要処理フロー**:
1. **6行目**: `useParams()` で URL から顧客 ID を取得
2. **9行目**: RouteFocusModal でフルスクリーンモーダルを表示
3. **10行目**: AddCustomerGroupsForm に customerId を渡す

#### Step 3: フォームコンポーネントを理解する

フォームの実装詳細を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | add-customer-groups-form.tsx | `packages/admin/dashboard/src/routes/customers/customers-add-customer-group/components/add-customers-form/add-customer-groups-form.tsx` | フォームの実装全体（37-257行目） |

**主要処理フロー**:
- **44-45行目**: useBatchCustomerCustomerGroups フックでバッチ更新用ミューテーションを取得
- **47-52行目**: useForm でフォーム初期化、空配列をデフォルト値として設定
- **56行目**: useState で行選択状態を管理
- **58-67行目**: useEffect で rowSelection から customer_group_ids へ同期
- **74-84行目**: useCustomerGroups でグループ一覧を取得（顧客情報含む）
- **100-114行目**: useDataTable でテーブル設定。enableRowSelection で既存メンバーは選択不可
- **116-136行目**: handleSubmit で保存処理を実装

#### Step 4: API フックを理解する

バックエンドとの通信処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | customers.tsx | `packages/admin/dashboard/src/hooks/api/customers.tsx` | useBatchCustomerCustomerGroups フック（123-153行目） |

**主要処理フロー**:
- **132-133行目**: sdk.admin.customer.batchCustomerGroups(id, payload) で API 呼び出し
- **134-147行目**: 成功時にキャッシュを無効化（顧客グループ、顧客の両方）

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

```
CustomerAddCustomerGroups (customer-add-customer-groups.tsx)
    │
    ├─ useParams() ─── URL から顧客 ID 取得
    │
    └─ RouteFocusModal
           │
           └─ AddCustomerGroupsForm (add-customer-groups-form.tsx)
                  │
                  ├─ useForm() ─── フォーム状態管理
                  │
                  ├─ useBatchCustomerCustomerGroups(customerId)
                  │      └─ sdk.admin.customer.batchCustomerGroups()
                  │             └─ queryClient.invalidateQueries()
                  │
                  ├─ useCustomerGroups() ─── グループ一覧取得
                  │
                  ├─ useDataTable() ─── テーブル状態管理
                  │
                  └─ _DataTable ─── テーブル表示
                         │
                         └─ useColumns() ─── カラム定義（チェックボックス含む）
```

### データフロー図

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

URL パラメータ (id) ───▶ useParams() ───▶ 顧客ID (customerId)
                              │
                              ▼
API呼び出し ───────────▶ useCustomerGroups() ───▶ グループ一覧
                              │
                              ▼
チェックボックス ──────▶ rowSelection ───▶ customer_group_ids
                              │
                              ▼
                        useBatchCustomerCustomerGroups() ───▶ API バッチ更新
                              │
                              ▼
                        成功/エラー ───▶ トースト通知 + 画面遷移
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| customer-add-customer-groups.tsx | `packages/admin/dashboard/src/routes/customers/customers-add-customer-group/customer-add-customer-groups.tsx` | ソース | ルートコンポーネント |
| add-customer-groups-form.tsx | `packages/admin/dashboard/src/routes/customers/customers-add-customer-group/components/add-customers-form/add-customer-groups-form.tsx` | ソース | フォームコンポーネント |
| index.ts | `packages/admin/dashboard/src/routes/customers/customers-add-customer-group/index.ts` | ソース | エクスポート定義 |
| customers.tsx | `packages/admin/dashboard/src/hooks/api/customers.tsx` | ソース | 顧客 API フック |
| customer-groups.tsx | `packages/admin/dashboard/src/hooks/api/customer-groups.tsx` | ソース | 顧客グループ API フック |
| RouteFocusModal | `packages/admin/dashboard/src/components/modals/` | ソース | フルスクリーンモーダルコンポーネント |
| _DataTable | `packages/admin/dashboard/src/components/table/data-table/` | ソース | データテーブルコンポーネント |
| useCustomerGroupTableColumns | `packages/admin/dashboard/src/hooks/table/columns/` | ソース | グループテーブルカラム定義 |
