# 画面設計書 90-グループメタデータ編集画面

## 概要

本ドキュメントは、Medusa Admin Dashboard における顧客グループのメタデータ編集画面の設計仕様を定義したものである。顧客グループに付与されたカスタムメタデータを編集するためのドロワー形式の画面について記述する。

### 本画面の処理概要

グループメタデータ編集画面は、顧客グループに関連付けられたカスタムメタデータ（キー・バリュー形式）を編集するためのドロワー形式の画面である。メタデータを追加、編集、削除することで、顧客グループに柔軟な追加情報を持たせることができる。

**業務上の目的・背景**：ECサイト運営において、顧客グループに対して標準フィールド以外のカスタム情報を付与したいケースがある。例えば、外部システムとの連携用ID、カスタム属性、マーケティングフラグなど。本画面は、キー・バリュー形式でメタデータを柔軟に管理し、拡張性の高い顧客グループ管理を実現する。

**画面へのアクセス方法**：顧客グループ詳細画面のメタデータセクションから「編集」アクションをクリックすることで本画面を開く。画面はドロワー形式で右側からスライドインして表示される。

**主要な操作・処理内容**：
1. 既存のメタデータの表示と編集
2. 新しいメタデータ行の追加（上に挿入、下に挿入）
3. メタデータ行の削除
4. 保存ボタンで変更を反映

**画面遷移**：顧客グループ詳細画面から遷移してくる。保存成功後またはキャンセル時は詳細画面に戻る。

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

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 27 | 顧客グループ管理 | 主機能 | グループメタデータの更新 |

## 画面種別

更新（ドロワー形式）

## URL/ルーティング

`/customer-groups/:id/metadata`

## 入出力項目

| 項目名 | 項目ID | データ型 | 必須 | 最大長 | 入力/出力 | バリデーション | 備考 |
|--------|--------|----------|------|--------|-----------|----------------|------|
| メタデータ | metadata | array | - | - | 入力 | zod.array(MetadataFieldSchema) | キー・バリューの配列 |
| キー | key | string | - | - | 入力 | zod.string() | メタデータのキー |
| 値 | value | any | - | - | 入力 | zod.any() | メタデータの値 |
| 無効化フラグ | disabled | boolean | - | - | 内部 | zod.boolean().optional() | 複雑な値（オブジェクト・配列）の場合true |

## 表示項目

| 項目名 | 表示位置 | 説明 |
|--------|----------|------|
| ヘッダータイトル | ドロワーヘッダー | 「メタデータを編集」（metadata.edit.header） |
| キーラベル | テーブルヘッダー | 「キー」（metadata.edit.labels.key） |
| 値ラベル | テーブルヘッダー | 「値」（metadata.edit.labels.value） |
| メタデータグリッド | メインエリア | キー・バリューの入力グリッド |
| 行アクションメニュー | 各行右側 | 上に行を挿入、下に行を挿入、行を削除 |
| 複雑な値の警告 | グリッド下 | 編集不可の値がある場合の警告メッセージ |
| キャンセルボタン | フッター | ドロワーを閉じる |
| 保存ボタン | フッター | メタデータを保存 |

## イベント仕様

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

ユーザーが保存ボタンを押下すると、以下の処理が実行される：
1. フォームのバリデーションを実行
2. 値のパース処理（文字列をboolean/number/stringに変換）
3. 削除されたキーの検出と空文字への変換
4. `useUpdateCustomerGroup` フックを通じて API を呼び出し
5. 成功時：成功トーストを表示し、`handleSuccess()` を呼び出してドロワーを閉じる
6. 失敗時：エラートーストを表示

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

ドロワーを閉じて顧客グループ詳細画面に戻る。未保存の変更は破棄される。

### 3-行アクションメニュー

各行の右側に表示されるメニューから以下の操作が可能：
- **上に行を挿入**：現在の行の上に新しい空行を追加
- **下に行を挿入**：現在の行の下に新しい空行を追加
- **行を削除**：現在の行を削除（最後の1行を削除すると新しい空行が自動追加）

### 4-フォーム入力

キー・値フィールドへの入力は React Hook Form の useFieldArray によって管理される。

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 保存ボタン押下 | customer_group | UPDATE | メタデータJSONフィールドの更新 |

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

#### customer_group

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | metadata | パース済みメタデータオブジェクト | JSON形式で保存 |
| UPDATE | updated_at | 現在日時 | 自動設定 |

## メッセージ仕様

| 種別 | メッセージキー | 表示条件 | メッセージ内容（参考） |
|------|---------------|----------|----------------------|
| 成功 | metadata.edit.successToast | 更新成功時 | メタデータを更新しました |
| エラー | (動的) | API エラー時 | エラーメッセージを表示 |
| 警告 | metadata.edit.complexRow.label | 複雑な値がある場合 | 編集できない値があります |
| ツールチップ | metadata.edit.complexRow.tooltip | 複雑な値にホバー時 | この値は複雑なため編集できません |

## 例外処理

| 例外条件 | 処理内容 |
|----------|----------|
| API 更新エラー | エラートーストを表示 |
| グループ取得エラー | エラーをスロー（isError 時） |

## 備考

- ドロワーは RouteDrawer コンポーネントを使用しており、右側からスライドインして表示
- フォームは KeyboundForm を使用しており、キーボードショートカットによる操作が可能
- 複雑な値（オブジェクト・配列）は編集不可で、ツールチップで説明を表示
- 複雑な値がある場合は、グリッド下に警告メッセージ（InlineTip）を表示
- 値の型変換が自動で行われる：
  - "true" / "false" → boolean
  - 数値形式の文字列 → number
  - その他 → string
- 空のメタデータ（キー・値が両方空）の場合、metadata は null として保存
- 削除されたキーは空文字として送信され、サーバー側で削除される
- グリッドのローディング中はスケルトン表示

---

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

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

### 推奨読解順序

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

メタデータ編集のスキーマを把握することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | metadata-form.tsx | `packages/admin/dashboard/src/components/forms/metadata-form/metadata-form.tsx` | MetadataFieldSchema（42-46行目）およびMetadataSchema（48-50行目）で定義されるフォームのバリデーションスキーマ |

**読解のコツ**:
- MetadataFieldSchema: key, disabled, value の3フィールドを持つオブジェクト
- MetadataSchema: MetadataFieldSchema の配列

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

ルートコンポーネントの構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | customer-metadata.tsx | `packages/admin/dashboard/src/routes/customer-groups/customer-group-metadata/customer-metadata.tsx` | CustomerGroupMetadata コンポーネント（8-26行目）。useCustomerGroup と useUpdateCustomerGroup でデータを取得・更新し、MetadataForm に渡す |

**主要処理フロー**:
1. **9行目**: useParams で URL パラメータから id を取得
2. **11行目**: useCustomerGroup でグループデータ（metadata含む）を取得
3. **12行目**: useUpdateCustomerGroup で更新用ミューテーションを取得
4. **19-24行目**: MetadataForm に metadata, hook, isPending, isMutating を渡して描画

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

MetadataForm コンポーネントの実装詳細を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | metadata-form.tsx | `packages/admin/dashboard/src/components/forms/metadata-form/metadata-form.tsx` | MetadataForm コンポーネント（52-69行目）とInnerForm コンポーネント（74-295行目） |

**主要処理フロー**:
- **84-89行目**: useForm でフォーム初期化、getDefaultValues で既存メタデータを変換
- **91-108行目**: handleSubmit で保存処理を実装、parseValues で値を変換
- **110-113行目**: useFieldArray でメタデータ配列を管理
- **115-126行目**: deleteRow 関数（最後の行を削除すると空行を追加）
- **128-134行目**: insertRow 関数（上または下に行を挿入）
- **156-264行目**: メタデータ行のレンダリング（キー・値入力、アクションメニュー）

#### 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行目） |

**主要処理フロー**:
- **getDefaultValues**: 既存メタデータをフォーム用の配列に変換、複雑な値は disabled: true
- **parseValues**: フォームデータをAPI用オブジェクトに変換、型変換（boolean/number/string）を実行
- **getHasUneditableRows**: 編集不可の値があるかをチェック

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

```
CustomerGroupMetadata (customer-metadata.tsx)
    │
    ├─ useParams() ─── URL から id 取得
    │
    ├─ useCustomerGroup(id)
    │      └─ グループデータ（metadata含む）取得
    │
    ├─ useUpdateCustomerGroup(id)
    │      └─ 更新用ミューテーション取得
    │
    └─ MetadataForm (metadata-form.tsx)
           │
           ├─ RouteDrawer ─── ドロワーコンテナ
           │
           └─ InnerForm
                  │
                  ├─ useForm() ─── フォーム状態管理
                  │      └─ getDefaultValues() ─── 初期値変換
                  │
                  ├─ useFieldArray() ─── 配列フィールド管理
                  │
                  ├─ handleSubmit()
                  │      ├─ parseValues() ─── 値のパース・型変換
                  │      └─ hook(mutateAsync) ─── API 呼び出し
                  │
                  └─ メタデータグリッド
                         ├─ GridInput ─── キー入力
                         ├─ GridInput ─── 値入力
                         └─ DropdownMenu ─── 行アクション
```

### データフロー図

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

URL パラメータ (id) ─────▶ useParams() ───▶ id
                                │
                                ▼
                          useCustomerGroup() ───▶ customer_group.metadata
                                │
                                ▼
                          getDefaultValues() ───▶ フォーム用配列に変換
                                │                 （複雑な値は disabled: true）
                                ▼
                          useForm() ───▶ フォーム状態
                                │
メタデータ編集操作 ─────────────▶│───▶ useFieldArray で管理
                                │
                                ▼
                          handleSubmit() ───▶ parseValues() ───▶ 型変換済みオブジェクト
                                │
                                ▼
                          hook(mutateAsync) ───▶ API 更新リクエスト
                                │
                                ▼
                          成功 ───▶ handleSuccess() ───▶ ドロワーを閉じる
                                │
                                ▼
                          トースト通知
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| customer-metadata.tsx | `packages/admin/dashboard/src/routes/customer-groups/customer-group-metadata/customer-metadata.tsx` | ソース | ルートコンポーネント |
| index.ts | `packages/admin/dashboard/src/routes/customer-groups/customer-group-metadata/index.ts` | ソース | エクスポート定義 |
| metadata-form.tsx | `packages/admin/dashboard/src/components/forms/metadata-form/metadata-form.tsx` | ソース | 共通メタデータフォームコンポーネント |
| customer-groups.tsx | `packages/admin/dashboard/src/hooks/api/customer-groups.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/` | ソース | 条件付きツールチップ |
