# 画面設計書 89-顧客追加画面

## 概要

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

### 本画面の処理概要

顧客追加画面は、特定の顧客グループに対して顧客を追加するためのフルスクリーンモーダル形式の画面である。顧客一覧から複数の顧客を選択し、一括でグループに追加することができる。

**業務上の目的・背景**：ECサイト運営において、顧客グループを活用したセグメンテーションは効果的なマーケティングの基盤となる。本画面は、既存の顧客を特定のグループに追加することで、VIP顧客、リピーター、特定キャンペーン対象者などのセグメントを構築できる。これにより、ターゲットを絞ったプロモーションや価格戦略の実施が可能となる。

**画面へのアクセス方法**：顧客グループ詳細画面の「顧客を追加」ボタンをクリックすることで本画面を開く。画面はフルスクリーンモーダル形式で表示される。

**主要な操作・処理内容**：
1. 顧客一覧から追加したい顧客を選択（複数選択可能）
2. 検索・フィルター・ソートによる顧客の絞り込み
3. 保存ボタンで選択した顧客をグループに追加
4. 追加成功後、グループ詳細画面へ自動遷移

**画面遷移**：顧客グループ詳細画面から遷移してくる。追加成功後は顧客グループ詳細画面へ遷移、キャンセル時も詳細画面に戻る。

**権限による表示制御**：認証済みの管理者ユーザーのみがアクセス可能。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 27 | 顧客グループ管理 | 主機能 | グループへの顧客追加 |
| 25 | 顧客CRUD | 関連 | 顧客一覧の表示 |

## 画面種別

登録（モーダル/フルスクリーンモーダル形式）

## URL/ルーティング

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

## 入出力項目

| 項目名 | 項目ID | データ型 | 必須 | 最大長 | 入力/出力 | バリデーション | 備考 |
|--------|--------|----------|------|--------|-----------|----------------|------|
| 顧客ID配列 | customer_ids | string[] | 必須 | - | 入力 | 最小1件（zod.array(zod.string()).min(1)） | 選択した顧客のID配列 |

## 表示項目

| 項目名 | 表示位置 | 説明 |
|--------|----------|------|
| 検索ボックス | ヘッダー | 顧客の検索（autofocus） |
| フィルター | テーブル上部 | 顧客一覧のフィルタリング |
| ソート | テーブル上部 | email, first_name, last_name, has_account, created_at, updated_at でソート可能 |
| 顧客一覧テーブル | メインエリア | 選択可能な顧客一覧 |
| 選択チェックボックス | テーブル行 | 各顧客を選択するチェックボックス |
| 既存メンバーツールチップ | チェックボックス | 既にグループに所属している顧客は選択不可、ツールチップで説明表示 |
| キャンセルボタン | フッター | モーダルを閉じる |
| 保存ボタン | フッター | 選択した顧客をグループに追加 |

## テーブルカラム

| カラム名 | 項目ID | データ型 | ソート | 説明 |
|---------|--------|----------|--------|------|
| 選択 | select | - | - | チェックボックス（複数選択可能） |
| メール | email | string | 可 | 顧客のメールアドレス |
| 名 | first_name | string | 可 | 顧客の名 |
| 姓 | last_name | string | 可 | 顧客の姓 |
| アカウント | has_account | boolean | 可 | アカウント登録の有無 |
| 作成日 | created_at | datetime | 可 | 作成日時 |
| 更新日 | updated_at | datetime | 可 | 更新日時 |

## イベント仕様

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

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

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

モーダルを閉じて顧客グループ詳細画面に戻る。選択状態は破棄される。

### 3-顧客選択

テーブル行のチェックボックスをクリックすることで顧客を選択/解除する。選択状態は React Hook Form と同期され、選択された顧客IDが `customer_ids` フィールドに反映される。

### 4-全選択/全解除

ヘッダーのチェックボックスをクリックすることで、現在のページの全顧客を選択/解除する（既にグループに所属している顧客は除く）。

### 5-検索・フィルター・ソート

顧客一覧の検索、フィルタリング、ソートを行う。検索ボックスには自動でフォーカスが当たる。

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

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

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

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

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

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | customer_group_id | URL パラメータ | グループID |
| INSERT | customer_id | 選択した顧客ID | 顧客ID（複数レコード） |
| INSERT | created_at | 現在日時 | 自動設定 |

## メッセージ仕様

| 種別 | メッセージキー | 表示条件 | メッセージ内容（参考） |
|------|---------------|----------|----------------------|
| 成功 | customerGroups.customers.add.successToast | 追加成功時 | {count} 件の顧客を追加しました |
| エラー | (動的) | API エラー時 | エラーメッセージを表示 |
| 空状態 | customerGroups.customers.add.list.noRecordsMessage | 顧客が見つからない場合 | 顧客が見つかりません |
| ツールチップ | customerGroups.customers.alreadyAddedTooltip | 既存メンバーにホバー時 | 既にグループに追加されています |

## 例外処理

| 例外条件 | 処理内容 |
|----------|----------|
| API 追加エラー | エラートーストを表示 |
| バリデーションエラー（選択なし） | ヘッダーにエラーメッセージを表示 |
| 顧客一覧取得エラー | エラーをスロー（isError 時） |

## 備考

- モーダルは RouteFocusModal コンポーネントを使用しており、フルスクリーン形式で表示
- フォームは KeyboundForm を使用しており、キーボードショートカットによる操作が可能
- 既にグループに所属している顧客はチェックボックスが無効化され、選択できない
- 無効化されたチェックボックスにはツールチップで理由が表示される
- ページネーションは 10 件単位
- 顧客一覧は `id, email, first_name, last_name, has_account, *groups` のフィールドを取得
- 選択状態は `rowSelection` ステートで管理され、`useEffect` で `customer_ids` フィールドと同期される
- 追加処理は `batchCustomers` API を使用（add パラメータ）

---

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

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

### 推奨読解順序

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

顧客追加のスキーマを把握することが重要。

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

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

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | customer-group-add-customers.tsx | `packages/admin/dashboard/src/routes/customer-groups/customer-group-add-customers/customer-group-add-customers.tsx` | CustomerGroupAddCustomers コンポーネント（5-13行目）。RouteFocusModal でフォームをラップ |

**主要処理フロー**:
1. **6行目**: useParams で URL パラメータから id を取得
2. **9-11行目**: RouteFocusModal で AddCustomersForm をラップして表示

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

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

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

**主要処理フロー**:
- **43-48行目**: useForm でフォーム初期化、空配列をデフォルト値として設定
- **52行目**: rowSelection ステートで選択状態を管理
- **54-63行目**: useEffect で rowSelection と customer_ids フィールドを同期
- **68-71行目**: useCustomers で顧客一覧を取得
- **88-102行目**: useDataTable でテーブルを初期化、既存メンバーは選択不可
- **104行目**: useAddCustomersToGroup フックで追加用ミューテーションを取得
- **106-121行目**: handleSubmit で保存処理を実装
- **115行目**: 成功時に詳細画面へ遷移

#### Step 4: テーブルカラム定義を理解する

テーブルカラムのカスタマイズを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | add-customers-form.tsx | `packages/admin/dashboard/src/routes/customer-groups/customer-group-add-customers/components/add-customers-form/add-customers-form.tsx` | useColumns フック（188-245行目） |

**主要処理フロー**:
- **194-238行目**: 選択列（select）の定義
- **211-237行目**: 既存メンバーの場合はチェックボックスを無効化し、ツールチップを表示

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | customer-groups.tsx | `packages/admin/dashboard/src/hooks/api/customer-groups.tsx` | useAddCustomersToGroup フック（154-179行目） |

**主要処理フロー**:
- **166-168行目**: sdk.admin.customerGroup.batchCustomers(id, { add: payload }) で API 呼び出し
- **170-176行目**: 成功時にキャッシュを無効化

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

```
CustomerGroupAddCustomers (customer-group-add-customers.tsx)
    │
    ├─ useParams() ─── URL から id 取得
    │
    └─ RouteFocusModal
           │
           └─ AddCustomersForm (add-customers-form.tsx)
                  │
                  ├─ useForm() ─── フォーム状態管理
                  │
                  ├─ useCustomers() ─── 顧客一覧取得
                  │
                  ├─ useDataTable() ─── テーブル状態管理
                  │      └─ enableRowSelection ─── 既存メンバーは選択不可
                  │
                  ├─ useAddCustomersToGroup()
                  │      └─ sdk.admin.customerGroup.batchCustomers()
                  │             └─ queryClient.invalidateQueries()
                  │
                  └─ _DataTable ─── 顧客一覧テーブル
                         │
                         └─ useColumns() ─── カラム定義
                                └─ select カラム（チェックボックス）
```

### データフロー図

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

URL パラメータ (id) ─────▶ useParams() ───▶ customerGroupId
                                │
                                ▼
                          useCustomers() ───▶ 顧客一覧データ
                                │
                                ▼
                          useDataTable() ───▶ テーブル状態
                                │
顧客選択操作 ─────────────────▶│───▶ rowSelection ステート
                                │
                                ▼
                          useEffect() ───▶ customer_ids フィールド更新
                                │
                                ▼
                        useAddCustomersToGroup() ───▶ API 追加リクエスト
                                │
                                ▼
                        成功 ───▶ 詳細画面遷移 (/customer-groups/:id)
                                │
                                ▼
                        トースト通知
```

### 関連ファイル一覧

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