# 画面設計書 128-商品追加画面

## 概要

本ドキュメントは、Medusa Admin Dashboardにおける販売チャネルへの商品追加画面の設計を記述したものです。販売チャネルに商品を追加するためのモーダル形式のフルスクリーン画面です。

### 本画面の処理概要

この画面では、販売チャネルに追加する商品を選択し、一括で紐付けを行います。商品一覧から複数の商品を選択して追加できます。

**業務上の目的・背景**：ECサイト運営において、商品の販売範囲を管理することは重要な業務です。特定の販売チャネル（Webサイト、アプリ、マーケットプレイスなど）に商品を公開するには、その商品をチャネルに紐付ける必要があります。この画面は、商品一覧から複数の商品を効率的に選択し、一括でチャネルに追加するためのインターフェースを提供します。

**画面へのアクセス方法**：販売チャネル詳細画面（No.126）の商品セクションで「追加」ボタンをクリックしてアクセスします。URLパスは `/settings/sales-channels/:id/add-products` です。RouteFocusModal形式（フルスクリーンモーダル）で表示されます。

**主要な操作・処理内容**：
1. 商品一覧の表示（ページネーション、検索、フィルタ対応）
2. 追加する商品のチェックボックス選択（複数選択可）
3. 既にチャネルに追加済みの商品は選択不可（チェックボックス無効化）
4. 保存ボタンクリックで選択した商品をチャネルに追加
5. キャンセルボタンクリックでモーダルを閉じる

**画面遷移**：
- 遷移元：販売チャネル詳細画面（No.126）
- 遷移先：保存完了後、販売チャネル詳細画面へ戻る

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

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 71 | 販売チャネル商品紐付け | 主機能 | 販売チャネルに商品を追加 |
| 1 | 商品CRUD | API連携 | 商品一覧の取得 |

## 画面種別

登録（モーダル形式）

## URL/ルーティング

`/settings/sales-channels/:id/add-products`

## 入出力項目

| 項目名 | 入力/出力 | データ型 | 必須 | 説明 |
|--------|----------|----------|------|------|
| id | 入力（URLパラメータ） | string | Yes | 販売チャネルID |
| product_ids | 入力（フォーム） | string[] | Yes | 選択された商品IDの配列（1件以上） |
| products | 出力 | AdminProduct[] | - | 商品一覧 |

## 表示項目

| 項目名 | データ型 | 説明 |
|--------|----------|------|
| 商品一覧 | DataTable | 選択可能な商品のテーブル |
| チェックボックス | Checkbox | 商品選択用（追加済みは無効化） |
| タイトル | string | 商品名 |
| ステータス | StatusBadge | 商品の公開状態 |
| サムネイル | Image | 商品画像 |
| バリデーションエラー | Hint | 商品未選択時のエラーメッセージ |

## イベント仕様

### 1-保存ボタンクリック

保存ボタンをクリックすると、選択した商品がチャネルに追加されます。

- バリデーション:
  - product_ids: 必須、1件以上の配列
- 処理内容:
  - `POST /admin/sales-channels/:id/products/batch` APIを呼び出し（useSalesChannelAddProductsフック）
  - リクエストボディ: `{ add: product_ids }`
  - 成功時: トースト通知を表示し、モーダルを閉じる
  - 失敗時: エラーメッセージをトースト通知で表示

### 2-キャンセルボタンクリック

キャンセルボタンをクリックすると、選択を破棄してモーダルを閉じます。

### 3-商品選択（チェックボックス）

チェックボックスをクリックすると、商品が選択/選択解除されます。

- 処理内容:
  - rowSelectionステートを更新
  - product_idsフォームフィールドを更新

### 4-ヘッダーチェックボックス

ヘッダーのチェックボックスをクリックすると、現在のページの全商品を選択/選択解除します。

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面表示 | sales_channel | SELECT | 販売チャネル情報の取得 |
| 画面表示 | product | SELECT | 商品一覧の取得（sales_channels含む） |
| 保存ボタン押下 | sales_channel_product | INSERT | 商品紐付けの追加 |

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

#### product

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, title, status, thumbnail, variants, sales_channels | ページネーション条件に基づく | sales_channels情報も取得 |

#### sales_channel_product

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | sales_channel_id | 販売チャネルID | - |
| INSERT | product_id | 選択された商品ID（複数） | - |

## メッセージ仕様

| メッセージ種別 | 表示条件 | メッセージ内容（i18nキー） |
|--------------|----------|------------------------|
| 成功 | 追加成功時 | salesChannels.toast.update |
| エラー | API呼び出し失敗時 | error.message |
| バリデーションエラー | 商品未選択時 | フォームエラーメッセージ |
| ツールチップ | 追加済み商品 | salesChannels.productAlreadyAdded |
| 空状態 | 商品なし時 | salesChannels.products.add.list.noRecordsMessage |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| 販売チャネルが見つからない | エラーをスロー（isError状態）してエラーバウンダリでハンドリング |
| ローディング中 | AddProductsToSalesChannelFormを表示しない |
| バリデーションエラー | Hintコンポーネントでエラー表示 |

## 備考

- ページサイズは50件
- 既にチャネルに追加済みの商品はチェックボックスが無効化され、ツールチップが表示される
- zodResolverを使用したフォームバリデーション
- DataTableで検索、フィルタ、ソート、ページネーションが可能

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | add-products-to-sales-channel-form.tsx | `packages/admin/dashboard/src/routes/sales-channels/sales-channel-add-products/components/add-products-to-sales-channel-form.tsx` | AddProductsToSalesChannelSchemaの定義（28-30行目） |

**読解のコツ**: product_idsは文字列配列で、.min(1)により最低1件の選択が必須です。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | sales-channel-add-products.tsx | `packages/admin/dashboard/src/routes/sales-channels/sales-channel-add-products/sales-channel-add-products.tsx` | RouteFocusModalによるフルスクリーンモーダル構成 |

**主要処理フロー**:
1. **7行目**: useParamsでURLからIDを取得
2. **8-13行目**: useSalesChannelでデータ取得
3. **19-25行目**: RouteFocusModalでモーダル構成、フォームを配置

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | add-products-to-sales-channel-form.tsx | `packages/admin/dashboard/src/routes/sales-channels/sales-channel-add-products/components/add-products-to-sales-channel-form.tsx` | 商品選択テーブル、フォーム管理 |

**主要処理フロー**:
- **40-45行目**: useFormでフォーム状態を初期化
- **49行目**: useSalesChannelAddProductsでミューテーション取得
- **51行目**: rowSelectionステートで選択状態を管理
- **53-64行目**: updater関数でチェックボックス変更時にproduct_idsを更新
- **66-81行目**: useProductsで商品一覧取得（sales_channels含む）
- **86-105行目**: useDataTableでテーブル設定（enableRowSelection関数で追加済み判定）
- **107-115行目**: handleSubmit関数で追加処理
- **178-240行目**: useColumnsでチェックボックスカラム定義（追加済みの場合は無効化+ツールチップ）

#### Step 4: API呼び出しフックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | sales-channels.tsx | `packages/admin/dashboard/src/hooks/api/sales-channels.tsx` | useSalesChannelAddProductsフックの実装（201-236行目） |

**主要処理フロー**:
- **210-211行目**: sdk.admin.salesChannel.batchProducts()を呼び出し
- **212-231行目**: 成功時に各種クエリを無効化

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

```
SalesChannelAddProducts (sales-channel-add-products.tsx)
    │
    ├─ useParams() → id取得
    │
    ├─ useSalesChannel(id)
    │
    └─ RouteFocusModal
           └─ AddProductsToSalesChannelForm
                  │
                  ├─ useForm() → react-hook-form
                  │      └─ zodResolver(Schema)
                  │
                  ├─ useSalesChannelAddProducts(id)
                  │
                  ├─ useProducts({ fields: "*sales_channels" })
                  │      └─ sdk.admin.product.list()
                  │
                  ├─ useDataTable()
                  │      └─ enableRowSelection: 追加済み判定
                  │
                  └─ DataTable
                         └─ Checkbox（追加済みは無効化）
                                │
                                └─ onSelect → updater → setValue("product_ids")
```

### データフロー図

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

URLパラメータ(:id) ───▶ useSalesChannel(id)      ───▶ sales_channel
                              │
                              ▼
                        useProducts({
                          fields: "*sales_channels"
                        })                          ───▶ products[]
                              │
                              ▼
                        enableRowSelection()
                        (追加済み判定)
                              │
                              ▼
                        DataTable表示
                              │
                              ▼
チェックボックス選択 ──▶ updater()
                              │
                              ▼
                        setValue("product_ids")
                              │
                              ▼
保存ボタン ────────────▶ handleSubmit()
                              │
                              ▼
                        useSalesChannelAddProducts()
                              │
                              ▼
{ add: product_ids }    sdk.admin.salesChannel.batchProducts()
                              │
                              ▼
                        toast.success()
                              │
                              ▼
                        handleSuccess() → モーダル閉じる
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| sales-channel-add-products.tsx | `packages/admin/dashboard/src/routes/sales-channels/sales-channel-add-products/sales-channel-add-products.tsx` | ソース | エントリーポイント |
| add-products-to-sales-channel-form.tsx | `packages/admin/dashboard/src/routes/sales-channels/sales-channel-add-products/components/add-products-to-sales-channel-form.tsx` | ソース | フォームコンポーネント |
| sales-channels.tsx | `packages/admin/dashboard/src/hooks/api/sales-channels.tsx` | ソース | APIフック |
| products.tsx | `packages/admin/dashboard/src/hooks/api/products.tsx` | ソース | 商品APIフック |
| use-product-table-columns.tsx | `packages/admin/dashboard/src/hooks/table/columns/use-product-table-columns.tsx` | ソース | 商品テーブルカラム定義 |
