# 画面設計書 120-ユーザー招待画面

## 概要

本ドキュメントは、Medusa Admin Dashboardにおけるユーザー招待画面に関する設計書です。この画面により、新しいユーザーをシステムに招待し、保留中の招待を管理することができます。

### 本画面の処理概要

**業務上の目的・背景**：ECサイト管理チームに新しいメンバーを追加する際、招待メールを送信して登録を促します。招待機能により、セキュアなユーザー登録プロセスを実現しています。

**画面へのアクセス方法**：ユーザー一覧画面（/settings/users）の「ユーザーを招待」ボタンから、またはURL直接アクセス（/settings/users/invite）。

**主要な操作・処理内容**：
1. 招待するユーザーのメールアドレスを入力
2. 招待メール送信
3. 保留中の招待一覧を表示
4. 招待の再送信
5. 招待リンクのコピー
6. 招待の削除（取り消し）

**画面遷移**：
- 遷移元：ユーザー一覧画面（/settings/users）
- 遷移先：キャンセル時・完了時ともにユーザー一覧画面に戻る

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

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 69 | ユーザー招待 | 主機能 | 招待の作成・再送信・削除 |

## 画面種別

モーダル形式（RouteFocusModal）の入力画面

## URL/ルーティング

`/settings/users/invite`

## 入出力項目

| 項目名 | 項目ID | 型 | 必須 | 説明 |
|--------|--------|------|------|------|
| メールアドレス | email | string | 必須 | 招待するユーザーのメールアドレス（email形式） |

## 表示項目

### 招待フォーム

| 項目名 | 項目ID | 型 | 説明 |
|--------|--------|------|------|
| メールアドレス入力 | email | string | 招待先メールアドレス |
| エラーメッセージ | root.message | string | API呼び出しエラー時のメッセージ |

### 保留中の招待一覧

| 項目名 | 項目ID | 型 | 説明 |
|--------|--------|------|------|
| メールアドレス | email | string | 招待先のメールアドレス |
| ステータス | accepted/expired | StatusBadge | 承諾済み/期限切れ/保留中 |
| アクションメニュー | actions | - | 再送信/リンクコピー/削除 |

## イベント仕様

### 1-招待送信

**トリガー**：「招待を送信」ボタンをクリック

**処理内容**：
1. Zodスキーマによるメールアドレスバリデーション
2. useCreateInviteフックでAPIを呼び出し
3. 成功時：フォームをリセット、招待一覧を自動更新
4. 失敗時（400エラー）：フォームにルートエラーを設定してAlert表示

### 2-招待再送信

**トリガー**：招待一覧の行アクションメニューから「招待を再送信」を選択

**処理内容**：
1. useResendInviteフックでAPIを呼び出し
2. 招待メールを再送信
3. 招待一覧のキャッシュを無効化して更新

### 3-招待リンクコピー

**トリガー**：招待一覧の行アクションメニューから「招待リンクをコピー」を選択

**処理内容**：
1. 招待トークンを含むURLを生成
2. クリップボードにコピー（copy-to-clipboard使用）
3. URLフォーマット: `{origin}{base}/invite?token={token}`

### 4-招待削除

**トリガー**：招待一覧の行アクションメニューから「削除」を選択

**処理内容**：
1. 確認プロンプトを表示
2. 確認後、useDeleteInviteフックでAPIを呼び出し
3. 招待一覧のキャッシュを無効化して更新

### 5-検索・ソート

**トリガー**：検索フィールド入力、ソートボタンクリック

**処理内容**：
1. useUserInviteTableQueryでクエリパラメータ管理
2. ソート対象：email, created_at, updated_at
3. ページネーション：10件ずつ表示

### 6-キャンセル

**トリガー**：モーダルのクローズ操作

**処理内容**：
1. モーダルを閉じてユーザー一覧画面に戻る

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 招待送信 | invite | INSERT | 新規招待レコード作成 |
| 招待再送信 | invite | UPDATE | 招待トークンの再生成・有効期限更新 |
| 招待削除 | invite | DELETE | 招待レコード削除 |

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

#### invite

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | email | フォーム入力値 | 招待先メールアドレス |
| INSERT | token | 自動生成 | 招待トークン |
| INSERT | expires_at | 現在日時 + 有効期間 | 招待有効期限 |
| INSERT | accepted | false | 初期値 |
| UPDATE (resend) | token | 再生成 | 新しい招待トークン |
| UPDATE (resend) | expires_at | 現在日時 + 有効期間 | 有効期限リセット |
| DELETE | - | id一致 | 招待の削除 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|--------------|---------|
| users.inviteUserHint | 情報 | メールアドレスを入力して招待を送信してください | フォーム説明 |
| users.deleteInviteWarning | 警告 | {email}への招待を削除しますか？ | 削除確認時 |
| users.inviteStatus.accepted | 情報 | 承諾済み | 招待が承諾された場合 |
| users.inviteStatus.expired | 警告 | 期限切れ | 招待の有効期限が切れた場合 |
| users.inviteStatus.pending | 情報 | 保留中 | 招待が未承諾の場合 |
| users.acceptedOnDate | 情報 | {date}に承諾 | 承諾日表示（ツールチップ） |
| users.expiredOnDate | 情報 | {date}に期限切れ | 期限切れ日表示（ツールチップ） |
| users.validFromUntil | 情報 | {from}から{until}まで有効 | 有効期間表示（ツールチップ） |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| 招待一覧取得エラー | エラーをスローしてエラーバウンダリで処理 |
| 招待作成API失敗（400） | フォームにルートエラーを設定してAlert表示 |
| 招待作成API失敗（その他） | 標準エラーハンドリング |
| 重複メールアドレス | 400エラーとしてAlertで表示 |

## 備考

- ページサイズは10件固定（PAGE_SIZE = 10）
- 招待URLのベースパスは`__BASE__`変数で動的に設定
- KeyboundFormを使用してEnterキーでのフォーム送信に対応
- 招待ステータスはexpires_atとacceptedフィールドから算出
- 招待一覧はinvitesのAPIを使用（usersのAPIとは別）

---

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

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

### 推奨読解順序

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

招待データの構造を理解します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | invite-user-form.tsx | `packages/admin/dashboard/src/routes/users/user-invite/components/invite-user-form/invite-user-form.tsx` | InviteUserSchemaの定義（37-39行目） |

**読解のコツ**: Zodスキーマでemail項目がemail形式で必須であることを確認します。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | user-invite.tsx | `packages/admin/dashboard/src/routes/users/user-invite/user-invite.tsx` | コンポーネントのエントリーポイント |

**主要処理フロー**:
1. **4-9行目**: RouteFocusModalでInviteUserFormをラップ

#### Step 3: フォームロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | invite-user-form.tsx | `packages/admin/dashboard/src/routes/users/user-invite/components/invite-user-form/invite-user-form.tsx` | メインフォームコンポーネント |

**主要処理フロー**:
- **37-39行目**: Zodスキーマ定義（email形式バリデーション）
- **41-45行目**: 定数定義（PAGE_SIZE, PREFIX, INVITE_URL）
- **50-55行目**: useFormでフォーム初期化
- **57-68行目**: useUserInviteTableQuery, useInvitesで招待一覧取得
- **70-80行目**: useDataTableでテーブル設定
- **82行目**: useCreateInviteで招待作成フック取得
- **84-97行目**: handleSubmit - 招待送信処理（400エラー時はルートエラー設定）
- **159-178行目**: 保留中の招待一覧テーブル表示
- **188-254行目**: InviteActionsコンポーネント（再送信/コピー/削除）
- **258-345行目**: useColumnsでカラム定義（email, accepted/status, actions）

#### Step 4: ステータス表示ロジックを理解する

**主要処理フロー**:
- **269-336行目**: acceptedカラムのcell関数
  - accepted=trueの場合：「承諾済み」（緑色）
  - expires_at < 現在日時の場合：「期限切れ」（赤色）
  - それ以外：「保留中」（オレンジ色）
- 各ステータスにツールチップで詳細日時を表示

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | invites.tsx | `packages/admin/dashboard/src/hooks/api/invites.tsx` | 招待APIフック |

**主要処理フロー**:
- **38-57行目**: useInvites - 招待一覧取得
- **59-74行目**: useCreateInvite - 招待作成（成功時にリスト無効化）
- **76-89行目**: useResendInvite - 招待再送信
- **91-108行目**: useDeleteInvite - 招待削除

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

```
UserInvite (user-invite.tsx)
    │
    └─ InviteUserForm
           │
           ├─ useForm()
           │      └─ フォーム状態管理
           │
           ├─ useUserInviteTableQuery()
           │      └─ テーブルクエリパラメータ管理
           │
           ├─ useInvites()
           │      └─ sdk.admin.invite.list()
           │
           ├─ useDataTable()
           │      └─ テーブル状態管理
           │
           ├─ useCreateInvite()
           │      └─ sdk.admin.invite.create()
           │
           └─ InviteActions
                  ├─ useDeleteInvite()
                  │      └─ sdk.admin.invite.delete()
                  │
                  ├─ useResendInvite()
                  │      └─ sdk.admin.invite.resend()
                  │
                  └─ handleCopyInviteLink()
                         └─ copy-to-clipboard
```

### データフロー図

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

メールアドレス入力 ────▶ useForm() ────────────────▶ フォーム状態
                              │
                              ▼
招待送信ボタン ────────▶ handleSubmit() ───────────▶ useCreateInvite()
                              │                           │
                              │                           ▼
                              │                   sdk.admin.invite.create()
                              │                           │
                              │                           ▼
                         ┌────┴────────────────────┐  [Invite APIサーバー]
                         │                         │
                         ▼                         ▼
                    成功時                    失敗時(400)
                    form.reset()             form.setError('root')
                              │                    │
                              ▼                    ▼
                    一覧キャッシュ無効化      Alert表示
                              │
                              ▼
useInvites() ─────────▶ 招待一覧取得 ────────────▶ _DataTable表示
                              │
                              ▼
InviteActions ─────────▶ 再送信/コピー/削除 ────▶ 各種API呼び出し
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| user-invite.tsx | `packages/admin/dashboard/src/routes/users/user-invite/user-invite.tsx` | ソース | エントリーポイント |
| invite-user-form.tsx | `packages/admin/dashboard/src/routes/users/user-invite/components/invite-user-form/invite-user-form.tsx` | ソース | メインフォームコンポーネント |
| invites.tsx | `packages/admin/dashboard/src/hooks/api/invites.tsx` | ソース | 招待APIフック |
| use-user-invite-table-query.ts | `packages/admin/dashboard/src/hooks/table/query/use-user-invite-table-query.ts` | ソース | 招待テーブルクエリフック |
| route-focus-modal.tsx | `packages/admin/dashboard/src/components/modals/route-focus-modal/` | ソース | モーダルコンポーネント |
| keybound-form.tsx | `packages/admin/dashboard/src/components/utilities/keybound-form/keybound-form.tsx` | ソース | キーバインドフォームラッパー |
| action-menu.tsx | `packages/admin/dashboard/src/components/common/action-menu/` | ソース | アクションメニューコンポーネント |
