# 画面設計書 8-商品編集画面

## 概要

本ドキュメントは、Medusa Admin Dashboard の商品編集画面の設計仕様を定義するものです。既存の商品の基本情報を編集するためのDrawer形式の画面です。

### 本画面の処理概要

本画面は、商品の基本情報（ステータス、タイトル、サブタイトル、ハンドル、素材、説明文、割引可否）を編集するためのフォーム画面です。商品詳細画面からDrawer（スライドイン）形式で表示されます。

**業務上の目的・背景**：ECサイト運営において、商品情報の更新は日常的な業務です。本画面は、商品の基本的な属性を素早く編集できるよう、コンパクトなDrawer形式で提供されています。ステータスの変更（下書き→公開）もここで行えます。

**画面へのアクセス方法**：
- 商品詳細画面の基本情報セクションの「編集」ボタンから
- ブラウザで直接 `/products/{id}/edit` へアクセス

**主要な操作・処理内容**：
1. ステータスの変更（draft/published/proposed/rejected）
2. タイトルの編集
3. サブタイトルの編集
4. ハンドル（URLスラッグ）の編集
5. 素材の編集
6. 説明文の編集
7. 割引可否の設定

**画面遷移**：
- 遷移元：商品詳細画面
- 遷移先：商品詳細画面（保存/キャンセル時）

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

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | 商品CRUD | 主機能 | 商品情報の更新 |

## 画面種別

編集画面（Drawer形式）

## URL/ルーティング

`/products/:id/edit`

## 入出力項目

| 項目名 | 項目ID | データ型 | 必須 | バリデーション | 説明 |
|--------|--------|----------|------|----------------|------|
| ステータス | status | enum | 必須 | draft/published/proposed/rejected | 商品のステータス |
| タイトル | title | string | 必須 | 1文字以上 | 商品名 |
| サブタイトル | subtitle | string | 任意 | - | 商品のサブタイトル |
| ハンドル | handle | string | 必須 | 1文字以上 | URL用のスラッグ |
| 素材 | material | string | 任意 | - | 素材情報 |
| 説明文 | description | string | 任意 | - | 商品の説明文 |
| 割引可能 | discountable | boolean | 必須 | - | 割引適用可否 |

## 表示項目

### ヘッダー

| 項目名 | 表示位置 | データソース | 説明 |
|--------|----------|--------------|------|
| タイトル | ヘッダー | i18n (products.edit.header) | 「商品を編集」 |
| 説明文（sr-only） | ヘッダー | i18n (products.edit.description) | スクリーンリーダー用説明 |

### フォーム本体

| 項目名 | 表示位置 | データソース | 説明 |
|--------|----------|--------------|------|
| ステータスセレクト | 本体上部 | product.status | ステータス選択ドロップダウン |
| タイトル入力 | 本体 | product.title | タイトル入力フィールド |
| サブタイトル入力 | 本体 | product.subtitle | サブタイトル入力フィールド（任意） |
| ハンドル入力 | 本体 | product.handle | ハンドル入力フィールド（/プレフィックス付き） |
| 素材入力 | 本体 | product.material | 素材入力フィールド（任意） |
| 説明文入力 | 本体 | product.description | 説明文テキストエリア（任意） |
| 割引可能スイッチ | 本体 | product.discountable | 割引可否トグル |
| 拡張フィールドゾーン | 本体下部 | FormExtensionZone | カスタムフィールド表示エリア |

### フッター

| 項目名 | 表示位置 | データソース | 説明 |
|--------|----------|--------------|------|
| キャンセルボタン | フッター右 | i18n (actions.cancel) | キャンセルボタン |
| 保存ボタン | フッター右 | i18n (actions.save) | 保存ボタン |

## イベント仕様

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

**トリガー**: 「保存」ボタンのクリックまたは Cmd/Ctrl + Enter

**処理フロー**:
1. フォームのバリデーションを実行
2. バリデーション成功時、`useUpdateProduct` フックで商品を更新
3. オプション項目は `transformNullableFormData` で変換
4. 成功時:
   - `toast.success` で成功メッセージを表示（{title}を更新しました）
   - `handleSuccess()` でDrawerを閉じる
5. 失敗時:
   - `toast.error` でエラーメッセージを表示

**遷移先**: 商品詳細画面（Drawerが閉じる）

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

**トリガー**: 「キャンセル」ボタンのクリック

**処理フロー**:
1. RouteDrawer.Close によりDrawerを閉じる
2. フォームの変更は破棄される

**遷移先**: 商品詳細画面（Drawerが閉じる）

### 3-Escキー押下

**トリガー**: Escキーの押下

**処理フロー**:
1. RouteDrawerのデフォルト動作でDrawerを閉じる

**遷移先**: 商品詳細画面（Drawerが閉じる）

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 商品更新 | product | UPDATE | 商品レコードの更新 |

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

#### product

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | status | 入力値 | draft/published/proposed/rejected |
| UPDATE | title | 入力値 | 必須 |
| UPDATE | subtitle | 入力値またはnull | 任意 |
| UPDATE | handle | 入力値 | 必須 |
| UPDATE | material | 入力値またはnull | 任意 |
| UPDATE | description | 入力値またはnull | 任意 |
| UPDATE | discountable | 入力値 | 必須 |
| UPDATE | updated_at | 現在日時 | 自動設定 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| products.edit.successToast | 成功 | {title}を更新しました | 商品更新成功時 |
| validation.required | エラー | この項目は必須です | 必須項目が未入力の場合 |

## 例外処理

| 例外条件 | 処理内容 | ユーザーへの表示 |
|---------|---------|-----------------|
| 商品データ取得失敗 | エラーをスロー | エラー境界でキャッチして表示 |
| バリデーションエラー | フィールドにエラー表示 | フィールド下にエラーメッセージ |
| 商品更新API失敗 | toast.errorで表示 | エラーメッセージをトーストで表示 |

## 備考

- 本画面は認証済みユーザーのみアクセス可能なプロテクト画面です
- RouteDrawer形式で商品詳細画面上にオーバーレイ表示されます
- useExtendableFormにより拡張フォームフィールドに対応しています
- FormExtensionZoneによりカスタムフィールドの追加が可能です
- KeyboundFormコンポーネントにより、Cmd/Ctrl + Enter での送信に対応しています
- useDocumentDirectionでRTL（右から左）レイアウトに対応しています
- ハンドル入力フィールドには「/」プレフィックスが視覚的に表示されます
- オプション項目（subtitle, material, description）は空文字列の場合nullに変換されます

---

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

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

### 推奨読解順序

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

まず、編集フォームのスキーマ定義を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | edit-product-form.tsx | `packages/admin/dashboard/src/routes/products/product-edit/components/edit-product-form/edit-product-form.tsx` | 22-30行目: EditProductSchema |

**スキーマ定義**:
- status: draft/published/proposed/rejectedのenum
- title: 1文字以上の必須文字列
- subtitle: オプション文字列
- handle: 1文字以上の必須文字列
- material: オプション文字列
- description: オプション文字列
- discountable: 必須boolean

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

ProductEditコンポーネントから処理の流れを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | product-edit.tsx | `packages/admin/dashboard/src/routes/products/product-edit/product-edit.tsx` | メインコンポーネント |

**主要処理フロー**:
1. **10行目**: useParamsでIDを取得
2. **13-17行目**: useProductでデータ取得（不要なリレーションを除外）
3. **19-21行目**: エラー時のスロー
4. **23-35行目**: RouteDrawerとEditProductFormの表示

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

EditProductFormコンポーネントの詳細を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | edit-product-form.tsx | `packages/admin/dashboard/src/routes/products/product-edit/components/edit-product-form/edit-product-form.tsx` | フォーム実装 |

**主要処理フロー**:
1. **33-39行目**: 依存関係の取得（useTranslation, useRouteModal, useExtension）
2. **40-53行目**: useExtendableFormでフォーム初期化
3. **55行目**: useUpdateProductフック取得
4. **57-82行目**: handleSubmit関数（更新処理とエラーハンドリング）
5. **84-244行目**: UIレンダリング

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

```
ProductEdit (product-edit.tsx)
    │
    ├─ useParams ─▶ { id }
    ├─ useProduct
    │
    └─ RouteDrawer
           │
           └─ EditProductForm (edit-product-form.tsx)
                  │
                  ├─ useRouteModal ─▶ handleSuccess
                  ├─ useExtension ─▶ getFormFields, getFormConfigs
                  ├─ useDocumentDirection
                  ├─ useExtendableForm
                  │      └─ EditProductSchema
                  │
                  ├─ useUpdateProduct
                  │      │
                  │      └─ mutateAsync
                  │            ├─ [成功] → toast.success → handleSuccess
                  │            └─ [失敗] → toast.error
                  │
                  └─ KeyboundForm
                         ├─ Form.Field (status)
                         ├─ Form.Field (title)
                         ├─ Form.Field (subtitle)
                         ├─ Form.Field (handle)
                         ├─ Form.Field (material)
                         ├─ Form.Field (description)
                         ├─ SwitchBox (discountable)
                         └─ FormExtensionZone
```

### データフロー図

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

URL /products/:id/edit
        │
        ▼
   useProduct
        │
        ▼
  product データ
        │
        ▼
useExtendableForm (初期値設定)
        │
        ▼
   ユーザー入力
        │
        ▼
   handleSubmit
        │
        ├─ transformNullableFormData (オプション項目)
        │
        ▼
  useUpdateProduct.mutateAsync
        │
        ├─ [成功]
        │     ├─ toast.success
        │     └─ handleSuccess (Drawer閉じる)
        │
        └─ [失敗]
              └─ toast.error
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| product-edit.tsx | `packages/admin/dashboard/src/routes/products/product-edit/product-edit.tsx` | ソース | メインコンポーネント |
| edit-product-form.tsx | `packages/admin/dashboard/src/routes/products/product-edit/components/edit-product-form/edit-product-form.tsx` | ソース | フォームコンポーネント |
| index.ts | `packages/admin/dashboard/src/routes/products/product-edit/index.ts` | ソース | エクスポート定義 |
