# 画面設計書 84-顧客メタデータ編集画面

## 概要

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

### 本画面の処理概要

顧客メタデータ編集画面は、顧客エンティティに紐づくカスタムメタデータ（キー・バリュー形式の任意データ）を編集するためのドロワー形式の画面である。共通コンポーネント MetadataForm を使用して実装されている。

**業務上の目的・背景**：ECサイト運営において、標準フィールドでは対応できない顧客固有の情報を管理する必要がある。例えば、会員ランク、特別な配送指示、営業担当者情報、外部システム連携用のIDなど、ビジネス要件に応じた柔軟なデータ拡張が求められる。本画面は、プログラミングなしで顧客データを拡張できるメタデータ管理機能を提供し、システムのカスタマイズ性を高める。

**画面へのアクセス方法**：顧客詳細画面のメタデータセクションから「メタデータを編集」アクションを選択するか、URLパス `/customers/:id/metadata` に直接アクセスすることで本画面を開く。画面は右側からスライドインするドロワー形式で表示される。

**主要な操作・処理内容**：
1. 既存メタデータのキー・バリュー一覧表示
2. キーと値の編集
3. 新規行の追加（上または下に挿入）
4. 既存行の削除
5. 保存ボタンで変更を確定

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

**権限による表示制御**：認証済みの管理者ユーザーのみがアクセス可能。オブジェクト型・配列型の複雑なメタデータ値は編集不可として表示され、ツールチップで説明が表示される。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 25 | 顧客CRUD | 主機能 | 顧客メタデータの編集 |

## 画面種別

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

## URL/ルーティング

`/customers/:id/metadata`

## 入出力項目

| 項目名 | 項目ID | データ型 | 必須 | 最大長 | 入力/出力 | バリデーション | 備考 |
|--------|--------|----------|------|--------|-----------|----------------|------|
| メタデータ配列 | metadata | Array | - | - | 入力 | zodスキーマによる検証 | キー・バリュー・無効フラグの配列 |
| キー | metadata[].key | string | - | - | 入力 | なし | メタデータのキー名 |
| 値 | metadata[].value | any | - | - | 入力 | なし | 文字列・数値・真偽値に対応 |
| 無効フラグ | metadata[].disabled | boolean | - | - | 内部 | - | 複雑な型の場合true |

## 表示項目

| 項目名 | 表示位置 | 説明 |
|--------|----------|------|
| ヘッダータイトル | ドロワーヘッダー | 「メタデータを編集」（metadata.edit.header） |
| キーラベル | グリッドヘッダー左 | 「Key」（metadata.edit.labels.key） |
| 値ラベル | グリッドヘッダー右 | 「Value」（metadata.edit.labels.value） |
| メタデータ行 | グリッド本体 | キー・値の入力フィールドペア |
| 行操作メニュー | 各行右側（ホバー時） | 行の追加・削除アクション |
| 複雑データ警告 | ボディ下部 | 編集不可のメタデータがある場合の説明 |
| キャンセルボタン | フッター | モーダルを閉じる |
| 保存ボタン | フッター | 変更を保存 |

## イベント仕様

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

ユーザーが保存ボタンを押下すると、以下の処理が実行される：
1. フォームデータを解析し、メタデータオブジェクトに変換
2. `useUpdateCustomer` フックを通じて API を呼び出し
3. 成功時：成功トーストを表示し、モーダルを閉じる
4. 失敗時：エラートーストを表示

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

モーダルを閉じて顧客詳細画面に戻る。

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

行操作メニューから「上に行を追加」を選択すると、現在の行の上に空の行が追加される。

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

行操作メニューから「下に行を追加」を選択すると、現在の行の下に空の行が追加される。

### 5-行削除

行操作メニューから「行を削除」を選択すると、現在の行が削除される。最後の行を削除した場合は、自動的に空の行が追加される。

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

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

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

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

#### customer

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | metadata | 編集されたメタデータオブジェクト | JSON形式で保存。削除されたキーは空文字で送信 |

## メッセージ仕様

| 種別 | メッセージキー | 表示条件 | メッセージ内容（参考） |
|------|---------------|----------|----------------------|
| 成功 | metadata.edit.successToast | 保存成功時 | メタデータを更新しました |
| エラー | (動的) | API エラー時 | エラーメッセージを表示 |
| 警告 | metadata.edit.complexRow.label | 複雑なデータあり | 一部のメタデータは編集できません |
| 情報 | metadata.edit.complexRow.tooltip | 編集不可行 | この値は複雑な型のため編集できません |
| 説明 | metadata.edit.complexRow.description | 複雑なデータあり | オブジェクトや配列は直接編集できません |

## 例外処理

| 例外条件 | 処理内容 |
|----------|----------|
| 顧客データ取得エラー | エラーをスローしてエラー境界で処理 |
| API 更新エラー | エラートーストを表示 |

## 備考

- モーダルは RouteDrawer コンポーネントを使用しており、URL ベースのルーティングで管理される
- フォームは KeyboundForm を使用しており、キーボードショートカットによる操作が可能
- メタデータの値は自動的に型変換される（"true"/"false" -> boolean、数値文字列 -> number）
- オブジェクト型・配列型の値は表示のみ可能で、編集はできない（プレースホルダーとして "{ ... }" または "[ ... ]" を表示）
- 共通コンポーネント MetadataForm を使用しており、他のエンティティ（商品、注文など）と同じUIを提供
- 削除されたキーは空文字として API に送信され、バックエンドで処理される

---

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

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

### 推奨読解順序

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

メタデータの型定義とフォームスキーマを把握することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | metadata-form.tsx | `packages/admin/dashboard/src/components/forms/metadata-form/metadata-form.tsx` | MetadataFieldSchema（42-46行目）とMetadataSchema（48-50行目）で定義されるスキーマ |

**読解のコツ**: zod ライブラリによるスキーマ定義。key（文字列）、disabled（真偽値、任意）、value（任意型）の構造。

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | customer-metadata.tsx | `packages/admin/dashboard/src/routes/customers/customer-metadata/customer-metadata.tsx` | CustomerMetadata コンポーネント（5-23行目）。useCustomer と useUpdateCustomer を取得し、MetadataForm に渡す |

**主要処理フロー**:
1. **6行目**: `useParams()` で URL から顧客 ID を取得
2. **8行目**: `useCustomer(id!)` で顧客データを取得
3. **9行目**: `useUpdateCustomer(id!)` で更新用ミューテーションを取得
4. **15-22行目**: MetadataForm に必要な props を渡す

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

共通 MetadataForm の実装詳細を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | metadata-form.tsx | `packages/admin/dashboard/src/components/forms/metadata-form/metadata-form.tsx` | フォームの実装全体（52-438行目） |

**主要処理フロー**:
- **84-89行目**: useForm でフォーム初期化、getDefaultValues で既存メタデータを変換
- **91-108行目**: handleSubmit で保存処理、parseValues でデータを変換
- **110-113行目**: useFieldArray でメタデータ行を動的に管理
- **115-126行目**: deleteRow で行削除、最後の行の場合は空行を追加
- **128-134行目**: insertRow で行追加（上または下）
- **333-367行目**: getDefaultValues で既存メタデータを行データに変換
- **369-428行目**: parseValues でフォームデータをメタデータオブジェクトに変換

#### Step 4: API フックを理解する

バックエンドとの通信処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | customers.tsx | `packages/admin/dashboard/src/hooks/api/customers.tsx` | useUpdateCustomer フック（81-99行目） |

**主要処理フロー**:
- **89行目**: sdk.admin.customer.update(id, payload) で API 呼び出し
- **91-93行目**: 成功時にキャッシュを無効化

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

```
CustomerMetadata (customer-metadata.tsx)
    │
    ├─ useParams() ─── URL から ID 取得
    │
    ├─ useCustomer(id) ─── 顧客データ取得
    │
    ├─ useUpdateCustomer(id) ─── 更新用ミューテーション
    │
    └─ MetadataForm (metadata-form.tsx)
           │
           ├─ RouteDrawer ─── ドロワーコンテナ
           │
           └─ InnerForm
                  │
                  ├─ useForm() ─── フォーム状態管理
                  │
                  ├─ useFieldArray() ─── 動的フィールド管理
                  │
                  ├─ getDefaultValues() ─── 初期値生成
                  │
                  ├─ parseValues() ─── 送信データ生成
                  │
                  └─ Form.Field × n ─── メタデータ行
```

### データフロー図

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

URL パラメータ (id) ───▶ useParams() ───▶ 顧客ID
                              │
                              ▼
API呼び出し ───────────▶ useCustomer() ───▶ 顧客データ (metadata含む)
                              │
                              ▼
既存メタデータ ─────────▶ getDefaultValues() ───▶ フォーム初期値
                              │
                              ▼
キー・値入力 ─────────▶ useFieldArray() ───▶ 動的行管理
                              │
                              ▼
保存時 ────────────────▶ parseValues() ───▶ メタデータオブジェクト
                              │
                              ▼
                        useUpdateCustomer() ───▶ API 更新リクエスト
                              │
                              ▼
                        成功/エラー ───▶ トースト通知 + モーダルクローズ
```

### 関連ファイル一覧

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