# 画面設計書 34-ユーザー管理一覧画面

## 概要

本ドキュメントは、Fat Free CRMにおけるユーザー管理一覧画面（管理者用）の設計仕様を記載する。本画面は管理者がシステム内の全ユーザーを一覧表示・管理するための機能を提供する。

### 本画面の処理概要

本画面は、管理者がシステム内の全ユーザーを一覧表示し、ユーザーの新規作成、編集、停止、再有効化、削除などの管理操作を行うための画面である。各ユーザーのステータス（アクティブ、停止中、承認待ち、管理者）を視覚的に識別でき、効率的なユーザー管理を支援する。

**業務上の目的・背景**：CRMシステムの管理者が組織内のユーザーアカウントを効率的に管理するための機能を提供する。ユーザーの登録・編集・削除、アカウントの停止・再開、アクセス権限の管理など、セキュリティとユーザー管理の両面で重要な役割を果たす。また、新規登録ユーザーの承認プロセス（needs_approval設定時）を管理する際の中心的な画面となる。

**画面へのアクセス方法**：以下の方法でアクセス可能：
1. ナビゲーションメニューから「Admin」>「Users」を選択
2. URL `/admin/users` に直接アクセス
3. URL `/admin` から自動的にリダイレクト

**主要な操作・処理内容**：
1. システム内の全ユーザー一覧を表示（ページネーション対応）
2. 新規ユーザーの作成
3. ユーザー情報の編集
4. ユーザーアカウントの停止
5. 停止中アカウントの再有効化
6. ユーザーの削除（確認ダイアログ付き）
7. CSV/XLS形式でのエクスポート

**画面遷移**：
- 遷移元：管理画面トップ、ナビゲーションメニュー
- 遷移先：ユーザー管理詳細画面、ユーザー管理新規作成フォーム、ユーザー管理編集フォーム

**権限による表示制御**：管理者（admin）ユーザーのみがアクセス可能。Admin::ApplicationControllerの認証・認可により制御される。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 59 | ユーザー管理一覧 | 主機能 | 全ユーザー一覧表示（管理者用） |
| 62 | ユーザー削除 | 補助機能 | 管理者によるユーザー削除 |

## 画面種別

一覧（管理）

## URL/ルーティング

| HTTPメソッド | URL | コントローラ#アクション | 説明 |
|-------------|-----|----------------------|------|
| GET | /admin/users | admin/users#index | ユーザー一覧表示 |
| GET | /admin/users/new | admin/users#new | 新規作成フォーム |
| POST | /admin/users | admin/users#create | ユーザー作成 |
| GET | /admin/users/:id | admin/users#show | ユーザー詳細 |
| GET | /admin/users/:id/edit | admin/users#edit | 編集フォーム |
| PUT/PATCH | /admin/users/:id | admin/users#update | ユーザー更新 |
| DELETE | /admin/users/:id | admin/users#destroy | ユーザー削除 |
| GET | /admin/users/:id/confirm | admin/users#confirm | 削除確認 |
| PUT | /admin/users/:id/suspend | admin/users#suspend | アカウント停止 |
| PUT | /admin/users/:id/reactivate | admin/users#reactivate | アカウント再有効化 |

## 入出力項目

本画面は一覧表示が主機能のため、入力項目は検索クエリのみ。

| 項目名 | 項目ID | 入力タイプ | 必須 | 備考 |
|--------|--------|-----------|------|------|
| 検索クエリ | query | text | - | ユーザー名、メール等で検索 |
| ページ番号 | page | hidden | - | ページネーション用 |

## 表示項目

### ユーザー一覧（各行）

| 項目名 | データソース | 表示形式 | 備考 |
|--------|------------|---------|------|
| ステータスバッジ | User#status | バッジ | signed_up/suspended/admin/active |
| アバター | User#avatar | 画像 | 30x30サイズ |
| フルネーム | User#full_name | リンク | プロフィールへ遷移 |
| ユーザー名 | User#username | テキスト | 括弧内表示 |
| 役職 | User#title | テキスト | |
| 会社 | User#company | テキスト | |
| ログイン回数 | User#sign_in_count | テキスト | |
| 最終ログイン | User#current_sign_in_at | 相対時間 | timeagoヘルパー使用 |
| メール | User#email | リンク | mailto:リンク |
| 電話番号 | User#phone | リンク | tel:リンク |
| 携帯電話 | User#mobile | リンク | tel:リンク |
| 登録日 | User#created_at | 日付 | |
| グループ | User#groups | リスト | グループ名をカンマ区切り |

### ステータスバッジの種類

| ステータス | 条件 | 表示クラス |
|----------|------|-----------|
| signed_up | awaits_approval? == true | .strip.signed_up |
| suspended | suspended? == true | .strip.suspended |
| admin | admin? == true | .strip.admin |
| active | 上記以外 | .strip.active |

## イベント仕様

### 1-画面表示（一覧取得）

**トリガー**: 画面アクセス（GET /admin/users）

**処理フロー**:
1. Admin::UsersController#index アクションが呼び出される
2. get_users メソッドで User.by_id.ransack(params[:q]).text_search(query).paginate(page:) を実行
3. index.html.haml が描画される

### 2-ユーザー新規作成リンク押下

**トリガー**: 「Create User」リンクのクリック

**処理フロー**:
1. Ajax で GET /admin/users/new リクエストが送信される
2. _new.html.haml パーシャルが応答
3. #create_user 領域に新規作成フォームを展開

### 3-ユーザー編集リンク押下

**トリガー**: 編集リンクのクリック

**処理フロー**:
1. Ajax で GET /admin/users/:id/edit リクエストが送信される
2. _edit.html.haml パーシャルが応答

### 4-アカウント停止

**トリガー**: 「Suspend!」リンクのクリック

**処理フロー**:
1. Ajax で PUT /admin/users/:id/suspend リクエストが送信される
2. @user.update_attribute(:suspended_at, Time.now) を実行
3. suspend.js.haml が応答し、一覧を更新

### 5-アカウント再有効化

**トリガー**: 「Reactivate」リンクのクリック

**処理フロー**:
1. Ajax で PUT /admin/users/:id/reactivate リクエストが送信される
2. @user.update_attribute(:suspended_at, nil) を実行
3. reactivate.js.haml が応答し、一覧を更新

### 6-ユーザー削除

**トリガー**: 「Delete?」リンクのクリック（確認後）

**処理フロー**:
1. confirm アクションで削除確認ダイアログを表示
2. 確認後、DELETE /admin/users/:id リクエストが送信される
3. destroyable? チェックの後、@user.destroy を実行
4. destroy.js.haml が応答し、一覧から削除

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面表示 | users, groups, groups_users | SELECT | ユーザー一覧を取得 |
| アカウント停止 | users | UPDATE | suspended_at を現在日時に設定 |
| アカウント再有効化 | users | UPDATE | suspended_at を NULL に設定 |
| ユーザー削除 | users, 関連テーブル | DELETE | ユーザーと関連データを削除 |

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

#### users

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE (suspend) | suspended_at | Time.now | アカウント停止時 |
| UPDATE (reactivate) | suspended_at | nil | 再有効化時 |
| DELETE | - | WHERE id = :id | ユーザー削除時 |

## メッセージ仕様

| 種別 | メッセージキー | 表示内容 | 表示条件 |
|------|--------------|---------|---------|
| ラベル | users | ユーザー | 画面タイトル |
| ラベル | create_user | ユーザー作成 | 作成リンク |
| ラベル | user_signed_up | 登録済み | awaits_approval状態 |
| ラベル | user_suspended | 停止中 | suspended状態 |
| ラベル | user_admin | 管理者 | admin状態 |
| ラベル | user_active | アクティブ | 通常状態 |
| ラベル | user_awaits_approval | 承認待ち | 承認待ち状態 |
| ラベル | user_never_logged_in | 未ログイン | ログイン実績なし |
| ラベル | last_logged_in | 最終ログイン | ログイン実績あり |
| ラベル | user_since | ユーザー登録日 | 登録日表示 |
| ラベル | user_signed_up_on | 登録日時 | 承認待ちユーザー |
| ラベル | user_suspended_on | 停止日 | 停止中ユーザー |
| 警告 | msg_cant_delete_user | ユーザーを削除できません | 削除不可時 |

## 例外処理

| 例外 | 対応 |
|------|------|
| 未認証アクセス | ログイン画面へリダイレクト |
| 管理者以外のアクセス | 403 Forbidden を返却 |
| 自分自身の停止 | 停止処理をスキップ（user != current_user チェック） |
| 関連アセットが存在するユーザーの削除 | 削除不可、警告メッセージ表示 |

## 備考

- 自分自身のアカウントは停止・削除できない（表示がグレーアウト）
- ユーザー削除時は destroyable? メソッドでチェック（関連アセットの有無）
- Ransack による高度な検索が利用可能
- ページネーションは will_paginate を使用
- エクスポート機能（shared/export パーシャル）が利用可能

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | user.rb | `app/models/users/user.rb` | suspended?, awaits_approval?, destroyable? メソッド（111-178行目） |
| 1-2 | schema.rb | `db/schema.rb` | usersテーブル構造（433-475行目） |

**読解のコツ**: `suspended?`（111-113行目）は `suspended_at != nil` で判定。`awaits_approval?`（116-118行目）は `suspended? && sign_in_count == 0 && Setting.user_signup == :needs_approval` で判定。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | routes.rb | `config/routes.rb` | admin/users リソース（168-177行目） |
| 2-2 | admin/users_controller.rb | `app/controllers/admin/users_controller.rb` | index, suspend, reactivate, destroy アクション |

**主要処理フロー**:
1. **16-19行目**: index アクションで get_users を呼び出し
2. **87-90行目**: suspend アクションで suspended_at を更新
3. **96-100行目**: reactivate アクションで suspended_at を nil に
4. **74-78行目**: destroy アクションで destroyable? チェック後に削除

#### Step 3: ビュー層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.html.haml | `app/views/admin/users/index.html.haml` | 全体構造（1-14行目） |
| 3-2 | _user.html.haml | `app/views/admin/users/_user.html.haml` | ユーザー行表示（1-69行目） |

**主要処理フロー**:
- **index.html.haml 4行目**: `link_to_inline(:create_user, new_admin_user_path)` で作成リンク
- **index.html.haml 11行目**: `render partial: "admin/users/user", collection: @users` でユーザー一覧
- **_user.html.haml 3-10行目**: ステータスバッジの条件分岐
- **_user.html.haml 16-29行目**: 操作リンク（編集、停止/再有効化、削除）

#### Step 4: get_users メソッドを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | admin/users_controller.rb | `app/controllers/admin/users_controller.rb` | get_users メソッド（134-147行目） |

**主要処理フロー**:
- **138行目**: `@search = klass.ransack(params[:q])` でRansack検索オブジェクト作成
- **142行目**: `scope = User.by_id` で ID降順スコープ
- **144行目**: `scope = scope.text_search(current_query)` でテキスト検索
- **145行目**: `scope = scope.paginate(page: current_page)` でページネーション

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

```
リクエスト: GET /admin/users
    │
    ├─ routes.rb
    │      └─ namespace :admin { resources :users }
    │
    ├─ Admin::UsersController
    │      ├─ before_action :setup_current_tab
    │      ├─ load_resource (CanCanCan)
    │      │
    │      └─ index (16-19行目)
    │             └─ get_users (134-147行目)
    │                    ├─ User.ransack(params[:q])
    │                    ├─ User.by_id
    │                    ├─ text_search(query)
    │                    └─ paginate(page:)
    │
    └─ index.html.haml
           ├─ link_to_inline(:create_user)
           │
           └─ render partial: "_user", collection: @users
                  └─ _user.html.haml
                         ├─ ステータスバッジ表示
                         ├─ アバター表示
                         ├─ ユーザー情報表示
                         └─ 操作リンク（編集/停止/削除）
```

### データフロー図

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

HTTPリクエスト   ───▶ Admin::UsersController    ───▶ index.html.haml
GET /admin/users      │                            │
                      ├─ get_users                 ├─ ユーザー一覧
                      │      ├─ ransack            │      └─ _user.html.haml
                      │      ├─ text_search        │
                      │      └─ paginate           ├─ ページネーション
                      │                            │      └─ shared/paginate
                      └─ @users                    │
                                                   └─ エクスポートリンク
                                                          └─ shared/export
```

```
[アカウント停止フロー]

リンクclick      ───▶ Admin::UsersController#suspend ───▶ users テーブル
PUT /admin/users/     │                                    │
:id/suspend           └─ update_attribute(:suspended_at, Time.now)
                                                           └─ UPDATE suspended_at
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.html.haml | `app/views/admin/users/index.html.haml` | ビュー | ユーザー管理一覧画面 |
| _user.html.haml | `app/views/admin/users/_user.html.haml` | パーシャル | ユーザー行表示 |
| _new.html.haml | `app/views/admin/users/_new.html.haml` | パーシャル | 新規作成フォーム |
| _edit.html.haml | `app/views/admin/users/_edit.html.haml` | パーシャル | 編集フォーム |
| users_controller.rb | `app/controllers/admin/users_controller.rb` | コントローラ | 管理者用ユーザー操作 |
| user.rb | `app/models/users/user.rb` | モデル | Userモデル定義 |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義 |
| _paginate.html.haml | `app/views/shared/_paginate.html.haml` | パーシャル | ページネーション |
| _export.html.haml | `app/views/shared/_export.html.haml` | パーシャル | エクスポートリンク |
