# 画面設計書 34-カテゴリメタデータ編集画面

## 概要

本ドキュメントは、Medusa Admin Dashboard におけるカテゴリメタデータ編集画面の設計を記述したものです。

### 本画面の処理概要

商品カテゴリに紐付くメタデータ（キー・バリュー形式のカスタム属性）を編集するための画面です。動的にキーと値のペアを追加・削除・編集できます。

**業務上の目的・背景**：ECサイトの運用において、カテゴリごとに追加の属性情報（SEO用データ、外部システム連携用ID、カスタム設定など）を保持する必要があります。メタデータを柔軟に設定できることで、標準機能では対応できないビジネス要件にも対応可能となり、拡張性の高いシステム運用が実現できます。

**画面へのアクセス方法**：カテゴリ詳細画面のメタデータセクションの「編集」ボタン押下で、右側からドロワー形式で表示されます。

**主要な操作・処理内容**：
1. メタデータ一覧の表示 - 既存のキー・バリューペアを表示
2. キーの編集 - メタデータのキー名を変更
3. 値の編集 - メタデータの値を変更
4. 行の追加 - 上または下に新しいキー・バリューペアを挿入
5. 行の削除 - 不要なキー・バリューペアを削除
6. 保存 - 変更内容を確定してAPIに送信

**画面遷移**：カテゴリ詳細画面から遷移してきます。保存完了後またはキャンセル時にカテゴリ詳細画面に戻ります。

**権限による表示制御**：管理者権限を持つユーザーのみがメタデータを編集できます。認証されていないユーザーはログイン画面にリダイレクトされます。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 4 | 商品カテゴリ管理 | 主機能 | カテゴリメタデータの編集 |

## 画面種別

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

## URL/ルーティング

`/categories/:id/metadata`

## 入出力項目

| 項目名 | 項目ID | 型 | 必須 | 最大長 | 入力/出力 | 説明 |
|--------|--------|-----|------|--------|-----------|------|
| カテゴリID | id | string | - | - | 入力(URL) | 編集対象のカテゴリID |
| メタデータ配列 | metadata | array | 任意 | - | 入出力 | キー・バリューペアの配列 |
| キー | key | string | 任意 | - | 入出力 | メタデータのキー |
| 値 | value | any | 任意 | - | 入出力 | メタデータの値（文字列、数値、真偽値） |
| 編集不可フラグ | disabled | boolean | - | - | 内部 | 複雑な値（オブジェクト、配列）の場合はtrue |

## 表示項目

| 項目名 | 表示形式 | 説明 |
|--------|----------|------|
| ヘッダータイトル | テキスト | "Edit Metadata"（i18n: metadata.edit.header） |
| キーラベル | ラベル | "Key"（i18n: metadata.edit.labels.key） |
| 値ラベル | ラベル | "Value"（i18n: metadata.edit.labels.value） |
| キー入力欄 | Input | メタデータキーの入力フィールド |
| 値入力欄 | Input | メタデータ値の入力フィールド |
| 行操作メニュー | DropdownMenu | 上に挿入、下に挿入、削除 |
| 複雑データ警告 | InlineTip | 編集不可な複雑データがある場合の警告 |

## イベント仕様

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

メタデータの変更を確定し、APIを呼び出してカテゴリを更新します。

**処理フロー**:
1. parseValues関数でフォームデータを変換
   - 空のメタデータは null に変換
   - 削除されたキーは空文字を設定
   - "true"/"false" は真偽値に変換
   - 数値文字列は数値に変換
2. useUpdateProductCategory フックでAPI呼び出し
3. 成功時: トースト通知（metadata.edit.successToast）を表示し、モーダルを閉じる
4. 失敗時: エラーメッセージをトーストで表示

**API呼び出し**:
- エンドポイント: `POST /admin/product-categories/:id`
- リクエストボディ:
  ```json
  {
    "metadata": {
      "key1": "value1",
      "key2": 123,
      "key3": true,
      "deletedKey": ""
    }
  }
  ```

### 2-行追加（上に挿入）

現在の行の上に新しい空のキー・バリューペアを挿入します。

### 3-行追加（下に挿入）

現在の行の下に新しい空のキー・バリューペアを挿入します。

### 4-行削除

指定された行を削除します。最後の行を削除した場合は、自動的に空の行が追加されます。

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

変更を破棄し、ドロワーを閉じてカテゴリ詳細画面に戻ります。

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 保存ボタン押下 | product_category | UPDATE | カテゴリのmetadataカラムを更新 |

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

#### product_category

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | metadata | フォーム入力から変換されたオブジェクト | JSONB形式で保存 |
| UPDATE | updated_at | 現在日時 | 自動更新 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示タイミング |
|------------|------|--------------|--------------|
| metadata.edit.successToast | 成功 | メタデータが正常に更新されました | 保存成功時 |
| - | エラー | {error.message} | API呼び出し失敗時 |
| metadata.edit.complexRow.tooltip | 情報 | この行には複雑なデータが含まれており編集できません | 複雑データ行のツールチップ |
| metadata.edit.complexRow.label | 警告 | 編集不可のメタデータ | 複雑データがある場合 |
| metadata.edit.complexRow.description | 警告 | 一部のメタデータは複雑なデータ型のため編集できません | 複雑データがある場合 |

## 例外処理

| 例外状況 | 処理内容 |
|---------|---------|
| カテゴリが見つからない | エラーをスローし、エラーバウンダリで処理 |
| API通信エラー | トーストでエラーメッセージを表示 |
| 認証エラー | ログイン画面にリダイレクト |

## 備考

- ドロワー形式（RouteDrawer）で表示
- 共通のMetadataFormコンポーネントを使用
- 複雑なデータ型（オブジェクト、配列）は編集不可で表示のみ
- オブジェクトは "{ ... }"、配列は "[ ... ]" として表示
- キー・バリューのグリッドレイアウトで表示
- 行のホバー時に操作メニューボタンが表示される
- useFieldArrayで動的な行の追加・削除を管理
- 値の型変換: "true"→true、"false"→false、数値文字列→数値

---

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

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

### 推奨読解順序

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

メタデータのスキーマを理解することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | metadata-form.tsx | `packages/admin/dashboard/src/components/forms/metadata-form/metadata-form.tsx` | MetadataFieldSchema（42-46行目）とMetadataSchema（48-50行目）を確認 |

**読解のコツ**: key, value, disabledの3つのフィールドで各行を管理し、配列としてフォーム全体を構成します。

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

ルートコンポーネントから画面がどのように構成されるかを理解します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | categories-metadata.tsx | `packages/admin/dashboard/src/routes/categories/categories-metadata/categories-metadata.tsx` | useProductCategoryでデータ取得、useUpdateProductCategoryで更新、MetadataFormに渡す |

**主要処理フロー**:
1. **11行目**: useParamsでURLからカテゴリIDを取得
2. **13行目**: useProductCategoryでカテゴリデータをフェッチ
3. **14行目**: useUpdateProductCategoryで更新用ミューテーションを取得
4. **22-27行目**: MetadataFormにmetadata, hook, isPending, isMutatingを渡す

#### Step 3: メタデータフォームを理解する

共通のメタデータフォームコンポーネントの実装を把握します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | metadata-form.tsx | `packages/admin/dashboard/src/components/forms/metadata-form/metadata-form.tsx` | フォーム初期化、行操作、値変換の実装を確認 |

**主要処理フロー**:
- **84-89行目**: useFormでフォーム初期化、getDefaultValuesで既存メタデータを変換
- **110-113行目**: useFieldArrayで動的行操作（fields, insert, remove）
- **91-108行目**: handleSubmitでparseValuesを呼び出し、hookでAPI更新
- **115-126行目**: deleteRowで行削除（最後の行削除時は空行を追加）
- **128-134行目**: insertRowで行挿入（上または下）
- **369-428行目**: parseValuesで値の型変換（boolean, number）と削除キーの処理

#### Step 4: ヘルパー関数を理解する

メタデータの変換ロジックを把握します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | metadata-form.tsx | `packages/admin/dashboard/src/components/forms/metadata-form/metadata-form.tsx` | getDefaultValues（333-367行目）、parseValues（369-428行目）、getHasUneditableRows（430-438行目） |

**主要処理フロー**:
- **333-367行目**: getDefaultValuesで既存メタデータをフォーム用配列に変換
- **347-353行目**: 複雑なデータ型はdisabled: trueで編集不可に設定
- **413-424行目**: "true"/"false"のboolean変換、数値文字列の数値変換
- **386-393行目**: 削除されたキーに空文字を設定（APIで削除される）

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

```
CategoriesMetadata (categories-metadata.tsx)
    │
    ├─ useParams() → カテゴリID取得
    │
    ├─ useProductCategory(id) → カテゴリデータ取得
    │      └─ sdk.admin.productCategory.retrieve()
    │
    ├─ useUpdateProductCategory(id) → 更新ミューテーション
    │      └─ sdk.admin.productCategory.update()
    │
    └─ MetadataForm (metadata-form.tsx)
           │
           ├─ RouteDrawer → ドロワー表示
           │
           ├─ useForm() → フォーム状態管理
           │
           ├─ useFieldArray() → 動的行管理
           │      ├─ fields → 行データ配列
           │      ├─ insert → 行挿入
           │      └─ remove → 行削除
           │
           ├─ getDefaultValues() → 初期値変換
           │
           ├─ parseValues() → 保存時の値変換
           │
           └─ handleSubmit() → フォーム送信
                  ├─ hook() → API呼び出し
                  ├─ toast.success() → 成功通知
                  └─ handleSuccess() → モーダルクローズ
```

### データフロー図

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

URLパラメータ(:id) ───▶ useProductCategory ───▶ カテゴリデータ取得
                              │
                              ▼
metadata ─────────────▶ getDefaultValues ───▶ フォーム初期値
                              │
                              ▼
フォーム入力 ─────────▶ useFieldArray ───▶ 動的行管理
                              │
                              ▼
保存ボタン押下 ────────▶ parseValues ───▶ API用データ変換
                              │
                              ├─ boolean変換 ("true"/"false")
                              ├─ 数値変換
                              └─ 削除キーに空文字設定
                              │
                              ▼
                        hook(mutateAsync) ───▶ API更新
                              │
                              ▼
                        クエリキャッシュ無効化 ───▶ UI更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| categories-metadata.tsx | `packages/admin/dashboard/src/routes/categories/categories-metadata/categories-metadata.tsx` | ソース | ルートコンポーネント |
| index.ts | `packages/admin/dashboard/src/routes/categories/categories-metadata/index.ts` | ソース | エクスポート定義 |
| metadata-form.tsx | `packages/admin/dashboard/src/components/forms/metadata-form/metadata-form.tsx` | ソース | 共通メタデータフォーム |
| categories.tsx | `packages/admin/dashboard/src/hooks/api/categories.tsx` | ソース | カテゴリAPI フック |
| RouteDrawer | `packages/admin/dashboard/src/components/modals/` | ソース | ドロワーモーダルコンポーネント |
| Form | `packages/admin/dashboard/src/components/common/form/` | ソース | フォームコンポーネント |
| KeyboundForm | `packages/admin/dashboard/src/components/utilities/keybound-form/` | ソース | キーバインド対応フォーム |
| ConditionalTooltip | `packages/admin/dashboard/src/components/common/conditional-tooltip/` | ソース | 条件付きツールチップ |
