# 画面設計書 27-チームメンバー管理画面

## 概要

本ドキュメントは、Giselle Studioのチームメンバー管理画面の設計仕様を記載したものです。

### 本画面の処理概要

この画面は、チームメンバーの一覧表示、新規メンバーの招待、既存メンバーの削除を行うための画面です。プランに応じたメンバー数の制限と、招待状の管理が可能です。

**業務上の目的・背景**：チームでの共同作業を円滑に行うため、メンバーの管理機能を提供します。プランごとに設定されたメンバー数の上限管理、招待の送信・期限管理、権限（admin/member）の管理を一元化します。

**画面へのアクセス方法**：設定画面のサイドバーから「Members」、または「/settings/team/members」パスに直接アクセスします。

**主要な操作・処理内容**：
1. チームメンバー一覧の表示（メールアドレス、ロール、ステータス）
2. 新規メンバーの招待（メールアドレス入力）
3. 招待状の管理（期限切れ、再送信など）
4. メンバーの削除（管理者のみ）
5. シート使用状況の表示

**画面遷移**：この画面からはチーム設定画面（/settings/team）へのリンクがあります。

**権限による表示制御**：
- ログイン済みユーザーで、チームに所属しているユーザーのみアクセス可能
- 招待・削除操作は管理者（admin）ロールのみ可能
- プランによってチームメンバー機能の利用可否が異なる

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 97 | チーム管理 | 主機能 | チームメンバーの一覧表示、招待、削除処理 |

## 画面種別

一覧・管理

## URL/ルーティング

`/settings/team/members`

## 入出力項目

| 項目名 | 型 | 入出力 | 必須 | 説明 |
|--------|-----|--------|------|------|
| inviteEmail | string | 入力 | 必須 | 招待先メールアドレス |

## 表示項目

| 項目名 | 説明 | データソース |
|--------|------|-------------|
| メンバー一覧 | チームのメンバーリスト | getTeamMembers関数 |
| メンバーメール | メンバーのメールアドレス | member.email |
| メンバーロール | メンバーの権限（admin/member） | member.role |
| 招待状一覧 | 保留中の招待リスト | listInvitations関数 |
| 招待先メール | 招待先のメールアドレス | invitation.email |
| 招待期限 | 招待の有効期限 | invitation.expiredAt |
| シート使用状況 | 使用中/最大シート数 | memberCount + pendingCount / maxMembers |
| 残りシート数 | 残りの招待可能数 | maxMembers - totalSeatsUsed |
| プラン名 | 現在のプラン表示 | team.plan |

## イベント仕様

### 1-Invite Member押下

InviteMemberDialogが表示され、メールアドレスを入力して招待を送信します。シート上限に達している場合はボタンが無効化されます。

### 2-招待送信

招待ダイアログでSubmit押下時、サーバーアクションが呼び出され、招待メールが送信されます。既存メンバーや招待済みのメールアドレスは除外されます。

### 3-メンバー削除

TeamMembersListコンポーネントで各メンバーの削除が可能です（管理者のみ）。

### 4-招待取り消し/再送信

招待状の取り消しや再送信が可能です（管理者のみ）。

### 5-ドキュメントリンク押下

About Membersリンク押下時、外部ドキュメント（docs.giselles.ai）が新しいタブで開きます。

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面表示 | teamMemberships, users, invitations | SELECT | メンバー・招待一覧を取得 |
| メンバー招待 | invitations | INSERT | 新規招待レコードを作成 |
| メンバー削除 | teamMemberships | DELETE | メンバーシップを削除 |
| 招待取り消し | invitations | DELETE | 招待レコードを削除 |

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

#### teamMemberships

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | 全項目 | teamId = 現在のチーム | getTeamMembers関数で取得 |
| DELETE | - | membershipId | メンバー削除時 |

#### invitations

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | 全項目 | teamId = 現在のチーム | listInvitations関数で取得 |
| INSERT | email, teamId, expiredAt | 招待送信時 | 新規招待作成 |
| DELETE | - | invitationId | 招待取り消し時 |

## メッセージ仕様

| 種別 | メッセージ | 表示条件 |
|------|----------|----------|
| メンバー取得エラー | Failed to load team members | メンバー一覧取得失敗時 |
| ロール取得エラー | Failed to get current user role | ロール取得失敗時 |
| シート上限到達 | You have used all seats included in your {plan} plan. Remove a member or upgrade to invite more teammates. | 上限到達時 |
| 残りシート表示 | {n} seat(s) remaining in your {plan} plan. | 通常時 |
| プランアップグレード案内 | Team collaboration is not included in the {plan} plan | Freeプラン時 |
| 招待カウント案内 | Pending invitation(s) count toward this limit. | 保留中招待がある場合 |
| 上限到達警告 | Maximum capacity reached | シート上限時 |

## 例外処理

| 状態 | 処理 |
|------|------|
| メンバー一覧取得エラー | "Failed to load team members"メッセージを表示 |
| ロール取得エラー | "Failed to get current user role"メッセージを表示 |
| プランで機能利用不可 | プランアップグレード案内を表示 |
| シート上限到達 | 招待ボタン無効化＋警告表示 |

## 備考

- プランによってメンバー数の上限が異なります（Free: 1名、Pro: 10名など）
- 招待状の有効期限は作成から一定期間（expiredAt）です
- 保留中の招待もシート数にカウントされます
- canManageTeamMembers関数でプランごとの機能利用可否を判定します
- 現在のユーザーのロールがadminでない場合、招待・削除ボタンは非表示になります

---

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

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

### 推奨読解順序

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

チームメンバー管理で使用されるデータ構造を把握します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | schema.ts | `apps/studio.giselles.ai/db/schema.ts` | teamMemberships、invitationsテーブル定義 |
| 1-2 | types.ts | `apps/studio.giselles.ai/services/teams/types.ts` | TeamPlan型定義 |

**読解のコツ**: TeamPlanは"free", "pro", "team", "enterprise", "internal"の5種類があります。

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

ページコンポーネントの構造を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | page.tsx | `apps/studio.giselles.ai/app/(main)/settings/team/members/page.tsx` | データ取得とコンポーネント構成 |

**主要処理フロー**:
1. **行123-126**: fetchCurrentTeam、fetchCurrentUserでチーム・ユーザー情報取得
2. **行127-128**: getCurrentUserRoleでロール取得
3. **行128**: getTeamMembersでメンバー一覧取得
4. **行129-130**: canManageMembersの判定
5. **行131**: listInvitationsで招待一覧取得
6. **行161-170**: getTeamMemberQuotaでシート制限取得
7. **行176-220**: UIコンポーネントのレンダリング

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

各機能を提供するコンポーネントを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | invite-member-dialog.tsx | `apps/studio.giselles.ai/app/(main)/settings/team/members/invite-member-dialog.tsx` | 招待ダイアログUI |
| 3-2 | team-members-list.tsx | `apps/studio.giselles.ai/app/(main)/settings/team/members/team-members-list.tsx` | メンバー一覧表示 |

**主要処理フロー**:
- **page.tsx行43-109**: TeamMemberPlanNoticeコンポーネント - プラン制限表示
- **page.tsx行188-199**: InviteMemberDialogの呼び出し

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

```
page.tsx (Server Component)
    │
    ├─ fetchCurrentTeam() - チーム情報取得
    │
    ├─ fetchCurrentUser() - ユーザー情報取得
    │
    ├─ getCurrentUserRole() - ロール取得
    │
    ├─ getTeamMembers() - メンバー一覧取得
    │
    ├─ canManageTeamMembers() - 機能利用可否判定
    │
    ├─ listInvitations() - 招待一覧取得
    │
    ├─ getTeamMemberQuota() - シート制限取得
    │
    ├─ TeamMemberPlanNotice - プラン制限表示
    │
    ├─ InviteMemberDialog - 招待ダイアログ
    │      └─ sendInvitation (Server Action)
    │
    └─ TeamMembersList - メンバー一覧
           ├─ removeMember (Server Action)
           └─ cancelInvitation (Server Action)
```

### データフロー図

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

teamMemberships テーブル ────────▶ getTeamMembers() ────────────▶ members[]
         │                              │
invitations テーブル ────────────▶ listInvitations() ───────────▶ invitations[]
         │                              │
teams テーブル ──────────────────▶ fetchCurrentTeam() ──────────▶ team
         │                              │
users テーブル ──────────────────▶ fetchCurrentUser() ──────────▶ currentUser
         │                              │
         └──────────────────────────────▶────────────────────────▶ TeamMemberPlanNotice
                                        │                              │
getTeamMemberQuota() ───────────────────▶────────────────────────▶ memberQuota
                                                                        │
                                                                        ▼
                                                              InviteMemberDialog
                                                              TeamMembersList
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| page.tsx | `apps/studio.giselles.ai/app/(main)/settings/team/members/page.tsx` | ソース | サーバーコンポーネント、メインページ |
| invite-member-dialog.tsx | `apps/studio.giselles.ai/app/(main)/settings/team/members/invite-member-dialog.tsx` | ソース | 招待ダイアログUI |
| team-members-list.tsx | `apps/studio.giselles.ai/app/(main)/settings/team/members/team-members-list.tsx` | ソース | メンバー一覧表示 |
| actions.ts | `apps/studio.giselles.ai/app/(main)/settings/team/members/actions.ts` | ソース | Server Actions |
| invitation.ts | `apps/studio.giselles.ai/app/(main)/settings/team/members/invitation.ts` | ソース | 招待関連関数 |
| plan-features/team-members.ts | `apps/studio.giselles.ai/services/teams/plan-features/team-members.ts` | ソース | プラン別機能制限 |
