# 画面設計書 59-プロモーション作成画面

## 概要

本ドキュメントは、Medusa Admin Dashboard における「プロモーション作成画面」の設計仕様を定義するものである。

### 本画面の処理概要

**業務上の目的・背景**：EC運営において、新しいプロモーション（割引クーポン、キャンペーン等）を作成する必要がある。本画面は、プロモーションの種類選択、詳細設定、キャンペーン紐付けを3段階のウィザード形式で行うためのインターフェースを提供する。

**画面へのアクセス方法**：プロモーション一覧画面から「新規作成」ボタンを押下することでアクセスする。

**主要な操作・処理内容**：
1. テンプレート（プロモーションタイプ）を選択する
2. プロモーションの詳細（コード、適用方法、値、ルール等）を設定する
3. キャンペーンへの紐付けを設定する（任意）
4. 保存ボタンを押下してプロモーションを作成する

**画面遷移**：
- 遷移元：プロモーション一覧画面（`/promotions`）
- 遷移先：作成完了後、プロモーション詳細画面（`/promotions/:id`）に遷移

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

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 36 | プロモーション管理 | 主機能 | プロモーションの作成 |
| 37 | キャンペーン管理 | API連携 | キャンペーンの選択・新規作成 |

## 画面種別

作成（モーダル形式・プログレスタブ）

## URL/ルーティング

```
/promotions/create
```

## 入出力項目

### タブ1: テンプレート選択

| 項目名 | 項目ID | データ型 | 必須 | 入力形式 | バリデーション | 説明 |
|--------|--------|----------|------|----------|----------------|------|
| テンプレート | template_id | string | ○ | ラジオボタン | - | プロモーションタイプの選択 |

### タブ2: 詳細設定

| 項目名 | 項目ID | データ型 | 必須 | 入力形式 | バリデーション | 説明 |
|--------|--------|----------|------|----------|----------------|------|
| 適用方法 | is_automatic | string | ○ | ラジオボタン | "true"/"false" | コード入力 or 自動適用 |
| ステータス | status | string | ○ | ラジオボタン | draft/active | 下書き or 有効 |
| プロモーションコード | code | string | ○ | テキスト | - | 一意のコード |
| 税込設定 | is_tax_inclusive | boolean | - | スイッチ | - | 値が税込かどうか |
| タイプ | type | string | - | ラジオボタン | standard/buyget | 標準 or 購入時特典 |
| ルール | rules | Array | - | 動的フォーム | - | 適用条件ルール |
| 値タイプ | application_method.type | string | - | ラジオボタン | fixed/percentage | 固定額 or 割合 |
| 値 | application_method.value | string | ○ | 通貨/パーセント | - | 割引値 |
| 最大数量 | application_method.max_quantity | number | - | 数値 | - | 適用最大数量 |
| 配分方法 | application_method.allocation | string | - | ラジオボタン | each/across/once | 配分方法 |
| 対象ルール | application_method.target_rules | Array | - | 動的フォーム | - | 対象商品ルール |
| 購入ルール | application_method.buy_rules | Array | - | 動的フォーム | - | 購入条件ルール（buygetのみ） |
| 使用制限 | limit | number | - | 数値 | - | 使用回数上限 |

### タブ3: キャンペーン設定

| 項目名 | 項目ID | データ型 | 必須 | 入力形式 | バリデーション | 説明 |
|--------|--------|----------|------|----------|----------------|------|
| キャンペーン選択 | campaign_choice | string | ○ | ラジオボタン | none/existing/new | キャンペーン紐付け方法 |
| 既存キャンペーン | campaign_id | string | - | セレクト | - | 既存キャンペーンを選択 |
| 新規キャンペーン | campaign | object | - | フォーム | - | 新規キャンペーン情報 |

## 表示項目

| 項目名 | 表示形式 | 説明 |
|--------|----------|------|
| プログレスタブ | ProgressTabs | 3段階の進行状況表示 |
| テンプレート選択肢 | RadioGroup.ChoiceBox | 各テンプレートの説明付きカード |
| 通貨記号 | テキスト | 選択された通貨の記号 |

## イベント仕様

### 1-テンプレート選択

テンプレートが選択された場合：
- 選択されたテンプレートのデフォルト値をフォームに適用
- 不要なフィールドをリセット
- allocationが"across"の場合、max_quantityをnullに設定

### 2-タブ遷移

続行ボタンまたはタブクリックで遷移する場合：
- TYPE -> PROMOTION: タブ状態を更新
- PROMOTION -> CAMPAIGN: フォームバリデーションを実行、成功時のみ遷移
- タブ状態（not-started/in-progress/completed）を適切に更新

### 3-値タイプ変更

値タイプ（fixed/percentage）が変更された場合：
- 入力コンポーネントを切り替え（CurrencyInput/PercentageInput）
- 値をリセット

### 4-通貨ルール変更

ルールでcurrency_codeが変更された場合：
- application_method.currency_codeを同期

### 5-配分方法変更

配分方法が"across"に変更された場合：
- max_quantityをnullに設定

### 6-キャンペーン選択変更

キャンペーン選択が変更された場合：
- "existing"以外: campaign_idをundefinedに
- "new"以外: campaignをundefinedに
- "new": デフォルトのキャンペーン値を設定

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

保存ボタンが押下された場合：
1. フォームデータをパース（disguisedルールの処理、値の型変換等）
2. `useCreatePromotion`フックを使用してAPI呼び出し
3. APIエンドポイント：`POST /admin/promotions`
4. 成功時：トースト通知を表示し、プロモーション詳細画面に遷移
5. 失敗時：エラーメッセージをトーストで表示

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 保存ボタン押下 | promotion | INSERT | プロモーションの作成 |
| 保存ボタン押下 | promotion_rule | INSERT | プロモーションルールの作成 |
| 保存ボタン押下 | campaign | INSERT | 新規キャンペーンの作成（選択時） |

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

#### promotion

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | UUID自動生成 | - |
| INSERT | code | 入力されたコード | - |
| INSERT | type | standard/buyget | - |
| INSERT | status | draft/active | - |
| INSERT | is_automatic | true/false | - |
| INSERT | is_tax_inclusive | true/false | - |
| INSERT | campaign_id | 選択/作成されたキャンペーンID | 任意 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| promotionCreateSuccess | 成功 | 「プロモーション{code}を作成しました」 | 作成成功時 |
| error | エラー | APIから返されたエラーメッセージ | 作成失敗時 |
| promotionTabError | エラー | 「詳細タブにエラーがあります」 | 詳細タブにエラーがある場合 |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| バリデーションエラー | タブ遷移をブロック、エラー表示 |
| 重複コード | APIエラーとして処理 |
| ネットワークエラー | エラートーストを表示 |

## 備考

- テンプレートにより非表示フィールド（hiddenFields）が異なる
- 通貨コードはrulesから自動取得される
- disguisedルールはapplication_methodの直接プロパティとして送信
- キャンペーンのbudget設定は通貨コードと連動

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | PromotionCreate | `packages/admin/dashboard/src/routes/promotions/promotion-create/promotion-create.tsx` | 画面コンポーネント |

**主要処理フロー**:
1. **5行目**: RouteFocusModalコンポーネントでラップ
2. **6行目**: CreatePromotionFormコンポーネントをレンダリング

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | CreatePromotionForm | `packages/admin/dashboard/src/routes/promotions/promotion-create/components/create-promotion-form/create-promotion-form.tsx` | フォームの実装（1087行の大規模コンポーネント） |

**主要処理フロー**:
- **51-71行目**: デフォルト値定義
- **75-89行目**: useFormによるフォーム初期化
- **92行目**: useCreatePromotionフックの取得
- **94-188行目**: フォーム送信ハンドラ（データパース含む）
- **190-231行目**: タブ遷移ハンドラ
- **233-250行目**: 続行ボタンハンドラ
- **252-285行目**: テンプレート変更時の副作用
- **378-1084行目**: JSXレンダリング（3タブ構成）

#### Step 3: スキーマを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | form-schema.ts | `packages/admin/dashboard/src/routes/promotions/promotion-create/components/create-promotion-form/form-schema.ts` | Zodスキーマ定義 |

#### Step 4: テンプレートを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | templates.ts | `packages/admin/dashboard/src/routes/promotions/promotion-create/components/create-promotion-form/templates.ts` | テンプレート定義 |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | useCreatePromotion | `packages/admin/dashboard/src/hooks/api/promotions.tsx` | プロモーション作成フック（188-204行目） |

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

```
PromotionCreate (promotion-create.tsx)
    │
    └─ CreatePromotionForm (create-promotion-form.tsx)
           │
           ├─ templates.ts (テンプレート定義)
           │
           ├─ form-schema.ts (バリデーションスキーマ)
           │
           ├─ useCampaigns (キャンペーン一覧取得)
           │
           ├─ RulesFormField (ルール入力)
           │
           ├─ AddCampaignPromotionFields (キャンペーン設定)
           │
           └─ useCreatePromotion (promotions.tsx)
                  └─ sdk.admin.promotion.create
                         └─ POST /admin/promotions
```

### データフロー図

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

テンプレート選択 ───▶ デフォルト値適用 ───┐
                                          │
適用方法選択 ─────────────────────────────┤
                                          │
詳細設定入力 ─────────────────────────────┼───▶ CreatePromotionForm
                                          │          │
ルール設定 ───────────────────────────────┤          │
                                          │          ▼
キャンペーン設定 ─────────────────────────┘    フォームバリデーション
                                                     │
                                                     ▼
                                               データパース
                                               (disguisedルール処理)
                                                     │
                                                     ▼
                                            useCreatePromotion
                                                     │
                                              ┌──────┴──────┐
                                              ▼             ▼
                                        promotion     promotion_rule
                                          (DB)            (DB)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| promotion-create.tsx | `packages/admin/dashboard/src/routes/promotions/promotion-create/promotion-create.tsx` | ソース | 画面コンポーネント |
| create-promotion-form.tsx | `packages/admin/dashboard/src/routes/promotions/promotion-create/components/create-promotion-form/create-promotion-form.tsx` | ソース | フォームコンポーネント |
| form-schema.ts | `packages/admin/dashboard/src/routes/promotions/promotion-create/components/create-promotion-form/form-schema.ts` | ソース | Zodスキーマ |
| templates.ts | `packages/admin/dashboard/src/routes/promotions/promotion-create/components/create-promotion-form/templates.ts` | ソース | テンプレート定義 |
| constants.ts | `packages/admin/dashboard/src/routes/promotions/promotion-create/components/create-promotion-form/constants.ts` | ソース | 定数定義 |
| promotions.tsx | `packages/admin/dashboard/src/hooks/api/promotions.tsx` | ソース | プロモーションAPIフック |
| campaigns.tsx | `packages/admin/dashboard/src/hooks/api/campaigns.tsx` | ソース | キャンペーンAPIフック |
