# 画面設計書 72-価格表作成画面

## 概要

本ドキュメントは、Medusa Admin Dashboardにおける「価格表作成画面」の画面設計書です。新規価格表（Price List）を作成するためのウィザード形式のモーダル画面について記述します。

### 本画面の処理概要

この画面では、新規価格表を3ステップのウィザード形式で作成します。詳細設定、対象商品選択、価格設定の順に入力を進め、最終的に価格表を登録します。

**業務上の目的・背景**：価格表の作成は、セール価格や会員限定価格などの特別価格を設定する業務において重要な機能です。複雑な価格設定を分かりやすいステップに分割することで、入力ミスを防ぎ、効率的な価格表作成を支援します。ウィザード形式を採用することで、必要な情報を段階的に収集し、ユーザーの認知負荷を軽減しています。

**画面へのアクセス方法**：価格表一覧画面（/price-lists）の「Create」ボタンをクリック、またはURL `/price-lists/create` に直接アクセスすることで表示されます。

**主要な操作・処理内容**：
1. Step 1（Details）：価格表の基本情報（タイプ、タイトル、ステータス、説明、開始日・終了日、顧客グループ）の入力
2. Step 2（Products）：価格表に含める商品の選択
3. Step 3（Prices）：選択した商品の各バリアントに対する価格設定
4. 各ステップ間でのバリデーションと状態管理
5. 価格表の保存とAPI呼び出し

**画面遷移**：
- 遷移元：価格表一覧画面
- 遷移先：作成成功時に価格表詳細画面（/price-lists/{id}）、キャンセル時に価格表一覧画面

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

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 53 | 価格リスト管理 | 主機能 | 新規価格表の作成 |

## 画面種別

モーダル形式（RouteFocusModal）/ ウィザード形式

## URL/ルーティング

`/price-lists/create`

## 入出力項目

### Step 1: Details（詳細設定）

| 項目名 | フィールド名 | 型 | 必須 | バリデーション | 説明 |
|--------|-------------|-----|------|---------------|------|
| タイプ | type | enum | Yes | "sale" または "override" | 価格表のタイプ |
| タイトル | title | string | Yes | 1文字以上 | 価格表の名称 |
| ステータス | status | enum | Yes | "draft" または "active" | 公開ステータス |
| 説明 | description | string | Yes | 1文字以上 | 価格表の説明 |
| 開始日時 | starts_at | Date | No | - | 価格表の有効開始日時 |
| 終了日時 | ends_at | Date | No | - | 価格表の有効終了日時 |
| 顧客グループ | rules.customer_group_id | array | No | - | 適用対象の顧客グループ |

### Step 2: Products（商品選択）

| 項目名 | フィールド名 | 型 | 必須 | バリデーション | 説明 |
|--------|-------------|-----|------|---------------|------|
| 商品ID配列 | product_ids | array | Yes | 1件以上 | 価格設定対象の商品 |

### Step 3: Prices（価格設定）

| 項目名 | フィールド名 | 型 | 必須 | バリデーション | 説明 |
|--------|-------------|-----|------|---------------|------|
| 商品別価格 | products | object | Yes | - | 商品・バリアント別の価格設定 |
| 通貨別価格 | currency_prices | object | No | 数値 | 通貨コードごとの価格 |
| リージョン別価格 | region_prices | object | No | 数値 | リージョンIDごとの価格 |

## 表示項目

### ヘッダー部

| 項目名 | 説明 |
|--------|------|
| ProgressTabs | 3ステップのプログレスインジケータ（Details/Products/Prices） |

### Step 1: Details フォーム

| 項目名 | コンポーネント | 説明 |
|--------|--------------|------|
| タイプ選択 | RadioGroup.ChoiceBox | Sale/Overrideの選択 |
| タイトル入力 | Input | テキスト入力 |
| ステータス選択 | Select | Active/Draftの選択 |
| 説明入力 | Textarea | 複数行テキスト入力 |
| 開始日時 | DatePicker | 日時選択（分単位） |
| 終了日時 | DatePicker | 日時選択（分単位） |
| 顧客グループ | StackedFocusModal | グループ選択モーダル |

### Step 2: Products フォーム

| 項目名 | コンポーネント | 説明 |
|--------|--------------|------|
| 商品選択テーブル | DataTable | 商品一覧から選択 |

### Step 3: Prices フォーム

| 項目名 | コンポーネント | 説明 |
|--------|--------------|------|
| 価格入力グリッド | DataGrid | バリアント×通貨/リージョンのマトリクス |

## イベント仕様

### 1-タブ切り替え

ProgressTabsのタブをクリックすると、該当ステップに移動します。前のステップへの移動は常に可能ですが、次のステップへの移動時は現在のステップのバリデーションが実行されます。

### 2-Continueボタン押下（Step 1, 2）

現在のステップのフォームをバリデーションし、成功すれば次のステップに進みます。失敗した場合はエラーメッセージが表示されます。

### 3-Saveボタン押下（Step 3）

全てのフォームデータを検証し、`useCreatePriceList`フックを使用してAPIを呼び出します。成功時は作成された価格表の詳細画面に遷移します。

### 4-Cancelボタン押下

モーダルを閉じ、価格表一覧画面に戻ります。未保存のデータは破棄されます。

### 5-顧客グループ選択

StackedFocusModalが開き、顧客グループを検索・選択できます。選択したグループはチップ形式で表示され、個別に削除可能です。

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| Save押下 | price_list | INSERT | 価格表レコードの作成 |
| Save押下 | price | INSERT | 価格レコードの作成（複数） |
| Save押下 | price_list_rule | INSERT | ルールレコードの作成（顧客グループ指定時） |

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

#### price_list

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | title | フォーム入力値 | 必須 |
| INSERT | type | "sale" または "override" | 必須 |
| INSERT | status | "draft" または "active" | 必須 |
| INSERT | description | フォーム入力値 | 必須 |
| INSERT | starts_at | フォーム入力値（ISO8601形式） | 任意 |
| INSERT | ends_at | フォーム入力値（ISO8601形式） | 任意 |

#### price

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | amount | 入力された価格（数値変換済み） | 必須 |
| INSERT | currency_code | 通貨コード | 必須 |
| INSERT | variant_id | バリアントID | 必須 |
| INSERT | rules | リージョン指定時は `{ region_id: xxx }` | 任意 |

## メッセージ仕様

| 種別 | 条件 | メッセージ |
|------|------|-----------|
| 成功 | 価格表作成成功時 | Price list "{title}" was successfully created. |
| エラー | バリデーション失敗時 | 各フィールドにエラーメッセージ表示 |
| エラー | API呼び出し失敗時 | エラーメッセージをトースト表示 |

## 例外処理

| 状態 | 処理 |
|------|------|
| バリデーションエラー | フォームフィールドにエラー表示、ステップ進行をブロック |
| API エラー発生時 | エラーメッセージをトースト表示 |
| 通貨/リージョンデータ未取得 | データ取得完了まで画面表示を遅延 |

## 備考

- フォームはreact-hook-formとzodを使用したバリデーションを実装
- 価格入力はDataGridコンポーネントを使用したスプレッドシート風UI
- 顧客グループ選択はStackedFocusModalを使用したネストモーダル

---

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

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

### 推奨読解順序

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

まず、フォームのスキーマ定義とデータ構造を理解することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | schema.ts | `packages/admin/dashboard/src/routes/price-lists/price-list-create/components/price-list-create-form/schema.ts` | PricingCreateSchemaの構造、各ステップのフィールド定義 |
| 1-2 | schemas.ts | `packages/admin/dashboard/src/routes/price-lists/common/schemas.ts` | 共通スキーマ（PriceListCreateProductsSchema等） |

**読解のコツ**: zodスキーマは型安全なバリデーションを提供します。PricingCreateSchema.pick()で各ステップのサブスキーマを定義している点に注目してください。

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

ページコンポーネントとデータ取得処理を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | price-list-create.tsx | `packages/admin/dashboard/src/routes/price-lists/price-list-create/price-list-create.tsx` | RouteFocusModalの構成、usePriceListCurrencyDataによる事前データ取得 |

**主要処理フロー**:
1. **6-7行目**: usePriceListCurrencyDataで通貨・リージョン・価格設定を取得
2. **10-18行目**: isReady判定後にPriceListCreateFormをレンダリング

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

ウィザード全体の制御ロジックを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | price-list-create-form.tsx | `packages/admin/dashboard/src/routes/price-lists/price-list-create/components/price-list-create-form/price-list-create-form.tsx` | ウィザードステップ管理、フォーム送信処理 |

**主要処理フロー**:
- **30-44行目**: Tab列挙型とTabState型定義
- **63-78行目**: useFormフックでフォーム初期化、zodResolverでバリデーション
- **82-116行目**: handleSubmit - API呼び出しとデータ変換
- **118-146行目**: partialFormValidation - ステップ別バリデーション
- **174-239行目**: handleChangeTab - タブ切り替えとバリデーション制御

#### Step 4: 各ステップのフォームを理解する

各ステップの個別フォームコンポーネントを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | price-list-details-form.tsx | `packages/admin/dashboard/src/routes/price-lists/price-list-create/components/price-list-create-form/price-list-details-form.tsx` | Step 1のフォーム構成、顧客グループ選択モーダル |
| 4-2 | price-list-products-form.tsx | `packages/admin/dashboard/src/routes/price-lists/price-list-create/components/price-list-create-form/price-list-products-form.tsx` | Step 2の商品選択テーブル |
| 4-3 | price-list-prices-form.tsx | `packages/admin/dashboard/src/routes/price-lists/price-list-create/components/price-list-create-form/price-list-prices-form.tsx` | Step 3の価格入力グリッド |

**主要処理フロー（price-list-details-form.tsx）**:
- **35-39行目**: useFieldArrayで顧客グループ配列を管理
- **43-58行目**: handleAddCustomerGroup - グループ追加・削除ロジック
- **69-110行目**: タイプ選択（RadioGroup.ChoiceBox）
- **179-206行目**: 開始日時選択（DatePicker）
- **267-292行目**: 顧客グループ選択モーダル（StackedFocusModal）

#### Step 5: API呼び出しを理解する

価格表作成APIとデータ変換処理を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | price-lists.tsx | `packages/admin/dashboard/src/hooks/api/price-lists.tsx` | useCreatePriceListフック |
| 5-2 | utils.ts | `packages/admin/dashboard/src/routes/price-lists/common/utils.ts` | exctractPricesFromProducts関数 |

**主要処理フロー（price-lists.tsx）**:
- **67-86行目**: useCreatePriceList - ミューテーション定義、キャッシュ無効化

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

```
PriceListCreate (price-list-create.tsx)
    │
    ├─ usePriceListCurrencyData() - 通貨/リージョン/価格設定取得
    │
    └─ PriceListCreateForm (price-list-create-form.tsx)
           │
           ├─ useForm() - フォーム状態管理
           │
           ├─ useCreatePriceList() - 作成API
           │
           ├─ ProgressTabs - ステップ表示
           │      │
           │      ├─ Tab.DETAIL
           │      │      └─ PriceListDetailsForm
           │      │             ├─ RadioGroup (type)
           │      │             ├─ Input (title)
           │      │             ├─ Select (status)
           │      │             ├─ Textarea (description)
           │      │             ├─ DatePicker (starts_at/ends_at)
           │      │             └─ StackedFocusModal (customer_group)
           │      │                    └─ PriceListCustomerGroupRuleForm
           │      │
           │      ├─ Tab.PRODUCT
           │      │      └─ PriceListProductsForm
           │      │             └─ DataTable (商品選択)
           │      │
           │      └─ Tab.PRICE
           │             └─ PriceListPricesForm
           │                    └─ DataGrid (価格入力)
           │
           └─ PrimaryButton - Continue/Save ボタン
```

### データフロー図

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

通貨/リージョン ──────▶ usePriceListCurrencyData() ──────▶ currencies, regions
                                                              pricePreferences

フォーム入力 ──────▶ useForm() ──────▶ form state
                        │
                        └─▶ zodResolver(PricingCreateSchema)

Step切替 ──────▶ handleChangeTab()
                   │
                   └─▶ partialFormValidation()

Save押下 ──────▶ handleSubmit()
                   │
                   ├─▶ exctractPricesFromProducts()
                   │
                   └─▶ useCreatePriceList()
                          │
                          └─▶ sdk.admin.priceList.create()
                                 │
                                 └─▶ 成功: handleSuccess(`../${price_list.id}`)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| price-list-create.tsx | `packages/admin/dashboard/src/routes/price-lists/price-list-create/price-list-create.tsx` | ソース | ページコンポーネント |
| price-list-create-form.tsx | `packages/admin/dashboard/src/routes/price-lists/price-list-create/components/price-list-create-form/price-list-create-form.tsx` | ソース | メインフォーム |
| schema.ts | `packages/admin/dashboard/src/routes/price-lists/price-list-create/components/price-list-create-form/schema.ts` | ソース | バリデーションスキーマ |
| price-list-details-form.tsx | `packages/admin/dashboard/src/routes/price-lists/price-list-create/components/price-list-create-form/price-list-details-form.tsx` | ソース | Step 1フォーム |
| price-list-products-form.tsx | `packages/admin/dashboard/src/routes/price-lists/price-list-create/components/price-list-create-form/price-list-products-form.tsx` | ソース | Step 2フォーム |
| price-list-prices-form.tsx | `packages/admin/dashboard/src/routes/price-lists/price-list-create/components/price-list-create-form/price-list-prices-form.tsx` | ソース | Step 3フォーム |
| price-lists.tsx | `packages/admin/dashboard/src/hooks/api/price-lists.tsx` | ソース | API呼び出しフック |
| utils.ts | `packages/admin/dashboard/src/routes/price-lists/common/utils.ts` | ソース | 価格抽出ユーティリティ |
| schemas.ts | `packages/admin/dashboard/src/routes/price-lists/common/schemas.ts` | ソース | 共通スキーマ |
| use-price-list-currency-data.tsx | `packages/admin/dashboard/src/routes/price-lists/common/hooks/use-price-list-currency-data.tsx` | ソース | 通貨データ取得フック |
