# 画面設計書 22-画像バリアント編集画面

## 概要

本ドキュメントは、Medusa Admin Dashboard における「画像バリアント編集画面」の設計仕様を定義するものである。

### 本画面の処理概要

画像バリアント編集画面は、商品画像に対して関連付けるバリアントを選択・管理するためのドロワー形式の画面である。商品の特定の画像がどのバリアントに適用されるかを設定できる。

**業務上の目的・背景**：ECサイトでは、同一商品でも色やサイズなどのバリエーションごとに異なる画像を表示することが一般的である。この画面により、商品画像とバリアントの紐付けを柔軟に管理でき、顧客に対して正確な商品イメージを提供できる。例えば、赤いTシャツの画像は「赤・Sサイズ」「赤・Mサイズ」「赤・Lサイズ」のバリアントに紐付けるといった設定が可能となる。

**画面へのアクセス方法**：商品詳細画面のメディアセクションで、特定の画像の編集アクションから「バリアントを管理」を選択することでこの画面が表示される。URLは `/products/:id/images/:image_id/variants` の形式となる。

**主要な操作・処理内容**：
1. 商品に属する全バリアントの一覧表示
2. 各バリアントのチェックボックスによる選択・解除
3. 全選択・全解除の一括操作
4. バリアントのタイトル、SKU、サムネイル状態の表示
5. 検索によるバリアントの絞り込み
6. ページネーションによる大量バリアントの表示

**画面遷移**：商品詳細画面からドロワーとして開き、保存またはキャンセル後に商品詳細画面に戻る。

**権限による表示制御**：商品の編集権限を持つユーザーのみが操作可能。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 8 | 商品画像管理 | 主機能 | 画像とバリアントの紐付け編集 |

## 画面種別

編集（モーダル/ドロワー形式）

## URL/ルーティング

`/products/:id/images/:image_id/variants`

## 入出力項目

| 項目名 | 項目ID | データ型 | 必須 | 入力/出力 | 説明 |
|--------|--------|----------|------|-----------|------|
| 商品ID | id | string | 必須 | 入力（URLパラメータ） | 対象の商品ID |
| 画像ID | image_id | string | 必須 | 入力（URLパラメータ） | 編集対象の画像ID |
| 選択バリアントID配列 | variants | string[] | - | 入出力 | 画像に紐付けるバリアントIDの配列 |
| 検索クエリ | q | string | - | 入力 | バリアント検索用クエリ |

## 表示項目

| 項目名 | データ型 | 説明 |
|--------|----------|------|
| 画像プレビュー | image | 編集対象の画像サムネイル |
| バリアント一覧テーブル | table | 選択可能なバリアントの一覧 |
| チェックボックス列 | checkbox | バリアントの選択状態 |
| タイトル列 | string | バリアントのタイトル |
| SKU列 | string | バリアントのSKU（モノスペースフォント） |
| サムネイル列 | boolean | この画像がバリアントのサムネイルかどうか |

## イベント仕様

### 1-バリアント選択チェックボックス

個別のバリアントをクリックして選択状態を切り替える。選択されたバリアントはフォームの `variants` 配列に追加される。

### 2-全選択チェックボックス

テーブルヘッダーのチェックボックスをクリックすると、現在のページの全バリアントを一括で選択または解除する。一部選択時は「indeterminate」状態で表示される。

### 3-検索

検索ボックスにテキストを入力すると、バリアントのタイトルやSKUで絞り込みが行われる。

### 4-ページネーション

20件ごとにページ分割され、ページ切り替えが可能。

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

現在の選択状態と初期状態を比較し、追加されたバリアントと削除されたバリアントを特定してAPIを呼び出す。成功時はトースト通知を表示し、ドロワーを閉じる。

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

変更を破棄し、ドロワーを閉じて商品詳細画面に戻る。

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 保存ボタン押下 | image_variant（中間テーブル） | INSERT/DELETE | 画像とバリアントの紐付けを更新 |

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

#### image_variant（画像-バリアント中間テーブル）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | image_id, variant_id | 新規選択されたバリアントとの紐付け | addパラメータで指定 |
| DELETE | image_id, variant_id | 解除されたバリアントとの紐付け | removeパラメータで指定 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| products.variantMedia.successToast | 成功 | バリアントメディアが正常に更新されました | 保存成功時 |
| - | エラー | {エラーメッセージ} | API呼び出し失敗時 |

## 例外処理

| 例外条件 | 処理内容 |
|----------|----------|
| 画像が見つからない | 404エラーをthrowし、エラーページを表示 |
| 商品IDまたは画像IDが未指定 | 画面を描画せずnullを返す |
| API呼び出し失敗 | トースト通知でエラーメッセージを表示 |

## 備考

- useBatchImageVariantsフックを使用して、追加と削除を一括でAPI呼び出しする
- 初期選択状態は画像データのvariantsプロパティから取得
- テーブルはTanStack Tableを使用して実装
- ページサイズは20件固定

---

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

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

### 推奨読解順序

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

画像とバリアントの関係、フォームスキーマを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | variants-table-form.tsx | `packages/admin/dashboard/src/routes/products/product-image-variants-edit/components/variants-table-form/variants-table-form.tsx` | BatchImageVariantsSchema（33-35行目）でフォーム構造を確認 |
| 1-2 | variants-table-form.tsx | `packages/admin/dashboard/src/routes/products/product-image-variants-edit/components/variants-table-form/variants-table-form.tsx` | VariantsTableFormProps（28-31行目）でpropsの型を確認 |

**読解のコツ**: zodスキーマでvariantsは文字列配列として定義されており、選択されたバリアントIDを保持する。

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

ルートコンポーネントの処理を追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | product-image-variants-edit.tsx | `packages/admin/dashboard/src/routes/products/product-image-variants-edit/product-image-variants-edit.tsx` | useParamsでURLパラメータを取得（17-20行目） |
| 2-2 | product-image-variants-edit.tsx | `packages/admin/dashboard/src/routes/products/product-image-variants-edit/product-image-variants-edit.tsx` | useProductで商品・画像データ取得（22-32行目） |
| 2-3 | product-image-variants-edit.tsx | `packages/admin/dashboard/src/routes/products/product-image-variants-edit/product-image-variants-edit.tsx` | 画像が見つからない場合の404処理（40-42行目） |

**主要処理フロー**:
1. **17-20行目**: URLパラメータから商品IDと画像IDを取得
2. **22-32行目**: useProductフックで商品データ（画像含む）をフェッチ
3. **34行目**: 画像IDで対象画像を検索
4. **40-42行目**: 画像が見つからない場合は404エラー
5. **59-62行目**: VariantsTableFormに必要なpropsを渡す

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

バリアント選択テーブルの動作を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | variants-table-form.tsx | `packages/admin/dashboard/src/routes/products/product-image-variants-edit/components/variants-table-form/variants-table-form.tsx` | 初期選択状態の設定（50-56行目）useStateでvariantSelectionを管理 |
| 3-2 | variants-table-form.tsx | `packages/admin/dashboard/src/routes/products/product-image-variants-edit/components/variants-table-form/variants-table-form.tsx` | handleSubmit（74-107行目）で保存処理の差分計算ロジックを確認 |
| 3-3 | variants-table-form.tsx | `packages/admin/dashboard/src/routes/products/product-image-variants-edit/components/variants-table-form/variants-table-form.tsx` | useDataTable（207-219行目）でテーブル設定を確認 |

**主要処理フロー**:
- **50-56行目**: 画像に紐付いているバリアントIDをRowSelectionState形式で初期化
- **74-88行目**: handleSubmitで初期状態と現在の選択状態を比較し、追加・削除対象を計算
- **92-106行目**: mutateAsyncで追加・削除を一括API呼び出し
- **178-187行目**: updater関数でチェックボックス変更時にフォーム値を更新

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

```
ProductImageVariantsEdit (product-image-variants-edit.tsx:14-65)
    │
    ├─ useParams() → 商品ID・画像ID取得
    │
    ├─ useProduct(id) → 商品・画像データ取得
    │      └─ sdk.admin.product.retrieve()
    │
    └─ VariantsTableForm (variants-table-form.tsx:40-263)
           │
           ├─ useForm() → フォーム状態管理
           │
           ├─ useState(variantSelection) → 選択状態管理
           │
           ├─ useBatchImageVariants() → API呼び出し準備
           │      └─ sdk.admin.product.batchImageVariants()
           │
           ├─ useProductVariants() → バリアント一覧取得
           │      └─ sdk.admin.productVariant.list()
           │
           ├─ useDataTable() → テーブル設定
           │
           └─ handleSubmit()
                  └─ mutateAsync({ add, remove })
```

### データフロー図

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

URLパラメータ(id, image_id) ───▶ useProduct ───▶ 商品・画像データ取得
                                       │
                                       ▼
                                 画像のvariants取得
                                       │
                                       ▼
                              初期選択状態(RowSelectionState)
                                       │
チェックボックス操作 ─────────────────▶ updater関数
                                       │
                                       ▼
                              variantSelection更新
                                       │
保存ボタン押下 ─────────────────────▶ handleSubmit
                                       │
                                       ▼
                              差分計算(add/remove)
                                       │
                                       ▼
                              mutateAsync ───▶ API更新
                                       │
                                       ▼
                              handleSuccess ───▶ ドロワーを閉じる
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| product-image-variants-edit.tsx | `packages/admin/dashboard/src/routes/products/product-image-variants-edit/product-image-variants-edit.tsx` | ソース | 画面のエントリーポイント |
| variants-table-form.tsx | `packages/admin/dashboard/src/routes/products/product-image-variants-edit/components/variants-table-form/variants-table-form.tsx` | ソース | バリアント選択テーブルフォーム |
| index.ts | `packages/admin/dashboard/src/routes/products/product-image-variants-edit/index.ts` | ソース | エクスポート定義 |
| useProduct | `packages/admin/dashboard/src/hooks/api/products.ts` | ソース | 商品データ取得フック |
| useBatchImageVariants | `packages/admin/dashboard/src/hooks/api/products.ts` | ソース | 画像バリアント一括更新フック |
| useProductVariants | `packages/admin/dashboard/src/hooks/api/products.ts` | ソース | バリアント一覧取得フック |
| RouteDrawer | `packages/admin/dashboard/src/components/modals/` | ソース | ドロワーモーダルコンポーネント |
| _DataTable | `packages/admin/dashboard/src/components/table/data-table/` | ソース | データテーブルコンポーネント |
