# 画面設計書 136-サービスゾーン作成画面

## 概要

本ドキュメントは、Medusa Admin Dashboardにおけるサービスゾーン作成画面の設計仕様を定義するものである。

### 本画面の処理概要

サービスゾーン作成画面は、ロケーションのフルフィルメントセット内に新しいサービスゾーン（配送対象地域）を作成するための画面である。

**業務上の目的・背景**：ECサイト運営において、各ロケーション（倉庫、店舗）が配送可能な地域を定義する必要がある。サービスゾーンは国単位で配送対象エリアをグループ化し、そのゾーン内で利用可能な配送オプションを設定するための基盤となる。例えば「国内配送ゾーン」「北米配送ゾーン」「EU配送ゾーン」などを作成し、それぞれに適した配送オプションを紐付ける。

**画面へのアクセス方法**：ロケーション詳細画面 > フルフィルメントセットのアクションメニュー > 「Add Service Zone」、または「Create Service Zone」リンクをクリックすることでアクセスできる。URLパスは `/settings/locations/{location_id}/fulfillment-set/{fset_id}/service-zones/create`。

**主要な操作・処理内容**：
1. サービスゾーン名の入力（必須）
2. 対象国の選択（最低1カ国必須）
3. 「保存」ボタンでサービスゾーンを作成
4. 「キャンセル」ボタンで操作を中断

**画面遷移**：
- 遷移元：ロケーション詳細画面（`/settings/locations/{location_id}`）
- 遷移先：作成完了後、ロケーション詳細画面へ自動的に戻る

**権限による表示制御**：管理者権限を持つユーザーのみがこの画面にアクセスし、サービスゾーンを作成できる。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 42 | サービスゾーン管理 | 主機能 | サービスゾーンの作成 |
| 47 | 地理ゾーン管理 | 補助機能 | 地理的範囲の設定 |

## 画面種別

登録画面（フルページモーダル形式）

## URL/ルーティング

- パス: `/settings/locations/:location_id/fulfillment-set/:fset_id/service-zones/create`
- コンポーネント: `LocationCreateServiceZone`
- ルートファイル: `packages/admin/dashboard/src/routes/locations/location-service-zone-create/location-service-zone-create.tsx`

## 入出力項目

| 項目名 | 項目ID | 入出力 | 型 | 必須 | バリデーション | 説明 |
|--------|--------|--------|-----|------|---------------|------|
| サービスゾーン名 | name | 入力 | string | Yes | 1文字以上 | サービスゾーンの名称 |
| 対象国 | countries | 入力 | array | Yes | 最低1カ国 | ISO 3166-1 alpha-2形式の国コードと表示名の配列 |

## 表示項目

| 項目名 | 説明 |
|--------|------|
| ヘッダータイトル | フルフィルメントセットタイプに応じた動的タイトル（Shipping/Pickup） |
| ヒントメッセージ | サービスゾーンの設定についてのヒント |

### 対象国選択UI

| 項目名 | 説明 |
|--------|------|
| 選択済み国一覧 | 追加された国のリスト |
| 国追加ボタン | 新しい国を追加するためのスタックモーダルを開く |
| 削除ボタン | 選択済み国を削除 |

## イベント仕様

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

**トリガー**: 「Save」ボタンのクリック、または `Cmd/Ctrl + Enter` キーボードショートカット

**処理フロー**:
1. フォームバリデーションを実行
2. バリデーション成功時、`useCreateFulfillmentSetServiceZone`フックを通じてAPIを呼び出し
3. APIリクエスト: `POST /admin/fulfillment-sets/{fset_id}/service-zones`
4. 成功時: トースト通知を表示し、ロケーション詳細画面へ遷移
5. 失敗時: エラーメッセージをトースト通知で表示

**送信データ**:
```typescript
{
  name: string,
  geo_zones: Array<{
    country_code: string,
    type: "country"
  }>
}
```

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

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

**処理フロー**:
1. モーダルを閉じる
2. ロケーション詳細画面に戻る

### 3-国追加

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

**処理フロー**:
1. スタックモーダル（`GeoZoneForm.AreaDrawer`）を開く
2. 国一覧から選択
3. 選択確定で`countries`フィールドに追加

### 4-国削除

**トリガー**: 選択済み国の削除ボタンクリック

**処理フロー**:
1. `countries`フィールドから該当国を削除

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 保存ボタン押下 | service_zone | INSERT | サービスゾーンレコードの作成 |
| 保存ボタン押下 | geo_zone | INSERT | 地理ゾーンレコードの作成（国ごと） |

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

#### service_zone

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | システム自動生成（UUID） | 主キー |
| INSERT | name | フォーム入力値 | サービスゾーン名 |
| INSERT | fulfillment_set_id | URLパラメータのfset_id | 外部キー |
| INSERT | created_at | システム自動設定 | 作成日時 |
| INSERT | updated_at | システム自動設定 | 更新日時 |

#### geo_zone

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | システム自動生成（UUID） | 主キー |
| INSERT | service_zone_id | 作成されたservice_zoneのid | 外部キー |
| INSERT | country_code | 選択された国のISO 3166-1 alpha-2コード | 国コード |
| INSERT | type | "country" | 地理ゾーンタイプ |

## メッセージ仕様

| メッセージ種別 | メッセージキー | 表示条件 | 表示内容 |
|--------------|---------------|----------|---------|
| 成功 | stockLocations.serviceZones.create.successToast | サービスゾーン作成成功時 | 「{name} created successfully」 |
| エラー | - | API呼び出し失敗時 | APIから返却されたエラーメッセージ |
| バリデーション | - | 必須項目未入力時 | 該当フィールドにエラー表示 |

## 例外処理

| 例外状況 | 処理内容 |
|---------|---------|
| フルフィルメントセットが見つからない | 404エラーをスロー |
| ネットワークエラー | トースト通知でエラーメッセージを表示 |
| サーバーエラー（5xx） | トースト通知でエラーメッセージを表示 |
| 認証エラー（401） | ログイン画面へリダイレクト |

## 備考

- 画面はフルページモーダル（`RouteFocusModal`）として実装されている
- フォームは`react-hook-form`と`zod`によるバリデーションを使用
- 国選択はスタックモーダル（`StackedFocusModal`）内の`GeoZoneForm.AreaDrawer`で行う
- フルフィルメントセットのタイプ（Pickup/Shipping）によってヘッダータイトルが変化
- `InlineTip`コンポーネントでユーザーへのヒントを表示
- `prev`プロパティでモーダルを閉じた際の遷移先を指定

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | create-service-zone-form.tsx | `packages/admin/dashboard/src/routes/locations/location-service-zone-create/components/create-service-zone-form/create-service-zone-form.tsx` | L22-27: `CreateServiceZoneSchema`でフォームスキーマを定義。名前と国の配列 |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | location-service-zone-create.tsx | `packages/admin/dashboard/src/routes/locations/location-service-zone-create/location-service-zone-create.tsx` | L8-47: URLパラメータからfset_idとlocation_idを取得し、フルフィルメントセットを検索 |

**主要処理フロー**:
1. **L9**: `useParams`でURLパラメータを取得
2. **L11-14**: `useStockLocation`でロケーションとフルフィルメントセット情報を取得
3. **L16-18**: URLのfset_idに一致するフルフィルメントセットを検索
4. **L20-23**: フルフィルメントセットのタイプ（Pickup/Shipping）を判定
5. **L36-46**: フォームコンポーネントを表示

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | create-service-zone-form.tsx | `packages/admin/dashboard/src/routes/locations/location-service-zone-create/components/create-service-zone-form/create-service-zone-form.tsx` | L35-146: フォーム初期化、国選択UI、API呼び出し |

**主要処理フロー**:
- **L43-49**: `useForm`でフォーム状態を初期化
- **L51-53**: `useCreateFulfillmentSetServiceZone`フックでAPI呼び出し関数を取得
- **L55-79**: `handleSubmit`で国データをgeo_zones形式に変換してAPI呼び出し
- **L124**: `GeoZoneForm`コンポーネントで国選択UIを表示
- **L127**: `GeoZoneForm.AreaDrawer`でスタックモーダル内の国選択を実装

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

```
LocationCreateServiceZone (location-service-zone-create.tsx)
    │
    ├─ useStockLocation (フルフィルメントセット情報取得)
    │
    └─ CreateServiceZoneForm (create-service-zone-form.tsx)
           │
           ├─ useForm (react-hook-form)
           │      └─ zodResolver (CreateServiceZoneSchema)
           │
           ├─ GeoZoneForm (国選択表示)
           │      └─ GeoZoneForm.AreaDrawer (スタックモーダル)
           │
           └─ useCreateFulfillmentSetServiceZone
                  └─ POST /admin/fulfillment-sets/{fset_id}/service-zones
```

### データフロー図

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

ゾーン名入力 ───▶ zodバリデーション ───▶ バリデーションエラー表示
                                              │
国選択 ──────────▶ GeoZoneForm ───────▶ countries配列更新
      │
      └─(save)──▶ geo_zones変換 ───▶ useCreateFulfillmentSetServiceZone
                          │
                          ├─(success)──▶ toast.success + 詳細画面遷移
                          │
                          └─(error)──▶ toast.error
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| location-service-zone-create.tsx | `packages/admin/dashboard/src/routes/locations/location-service-zone-create/location-service-zone-create.tsx` | ソース | 画面エントリーポイント |
| create-service-zone-form.tsx | `packages/admin/dashboard/src/routes/locations/location-service-zone-create/components/create-service-zone-form/create-service-zone-form.tsx` | ソース | フォームコンポーネント |
| geo-zone-form.tsx | `packages/admin/dashboard/src/routes/locations/common/components/geo-zone-form/geo-zone-form.tsx` | ソース | 国選択コンポーネント |
| constants.ts | `packages/admin/dashboard/src/routes/locations/common/constants.ts` | ソース | 定数定義（FulfillmentSetType等） |
| fulfillment-sets.tsx | `packages/admin/dashboard/src/hooks/api/fulfillment-sets.tsx` | ソース | フルフィルメントセットAPI |
