# 画面設計書 2-サインアップ画面

## 概要

本ドキュメントは、Fizzyアプリケーションのサインアップ画面（`app/views/signups/new.html.erb`）の設計仕様を記述したものです。

### 本画面の処理概要

サインアップ画面は、新規ユーザーがFizzyアカウントを作成するためのエントリーポイントとなる画面です。メールアドレスを入力することで、マジックリンクが送信され、そのリンクを経由してアカウント作成フローに進むことができます。

**業務上の目的・背景**：新規ユーザーの獲得を目的とした画面です。マルチテナントモードで運用する場合、または初回セットアップ時にのみ利用可能となります。シングルテナント環境（企業内利用など）では、この画面へのアクセスが制限され、ログイン画面にリダイレクトされます。これにより、不正なアカウント作成を防ぎつつ、適切な環境でのみ新規登録を許可する制御が実現されています。

**画面へのアクセス方法**：ログイン画面（`/session/new`）に表示される「Sign up」リンクをクリックする、または URL 直接アクセス（`/signup/new`）で表示可能です。ただし、`Account.accepting_signups?`がfalseの場合はログイン画面にリダイレクトされます。

**主要な操作・処理内容**：
1. メールアドレスを入力フィールドに入力する
2. 「Let's go」ボタンをクリックしてフォームを送信する
3. 入力されたメールアドレスのバリデーションが行われる
4. 新規Identityが作成され、マジックリンクメールが送信される
5. マジックリンク送信完了画面にリダイレクトされる

**画面遷移**：
- 遷移元：ログイン画面（`/session/new`）のサインアップリンク
- 遷移先：マジックリンク送信完了画面（`/session/magic_link`）、サインアップ完了画面（認証済みの場合）

**権限による表示制御**：
- 未認証ユーザーのみアクセス可能（認証済みの場合はサインアップ完了画面にリダイレクト）
- マルチテナントモードが有効または初回セットアップ時のみアクセス可能（それ以外はログイン画面にリダイレクト）

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | サインアップ | 主機能 | 新規アカウント作成のためのメールアドレス入力とマジックリンク送信 |

## 画面種別

登録（メールアドレス入力）

## URL/ルーティング

| 項目 | 値 |
|------|-----|
| URL | `/signup/new` または `/signup`（リダイレクト元） |
| HTTPメソッド | GET（表示）、POST（送信先: `/signup`） |
| コントローラー | `SignupsController` |
| アクション | `new`（表示）、`create`（送信処理） |
| レイアウト | `public` |

## 入出力項目

### 入力項目

| No | 項目名 | 物理名 | データ型 | 必須 | 最大長 | 入力形式 | バリデーション | 備考 |
|----|--------|--------|---------|------|--------|----------|---------------|------|
| 1 | メールアドレス | signup[email_address] | string | 必須 | - | email | RFC準拠のメール形式 | autocomplete="username" |

### 出力項目

なし（フォーム画面のため）

## 表示項目

| No | 項目名 | 表示条件 | データソース | 備考 |
|----|--------|----------|-------------|------|
| 1 | タイトル「Sign up」 | 常時 | 固定文言 | - |
| 2 | メールアドレス入力フィールド | 常時 | - | placeholder="Enter your email address..." |
| 3 | 案内文「Enter your email to create an account.」 | 常時 | 固定文言 | - |
| 4 | 「Let's go」ボタン | 常時 | 固定文言 | 矢印アイコン付き |
| 5 | フッター | 常時 | _footer部分テンプレート | サポートメール連絡先 |

## イベント仕様

### 1-「Let's go」ボタン押下

**トリガー**: フォーム送信（submit）

**処理フロー**:
1. `SignupsController#create`が呼び出される
2. レート制限チェック（3分間に10回まで）
3. `Signup`モデルのインスタンスを作成し、`:identity_creation`コンテキストでバリデーション
4. バリデーション成功時：
   - `signup.create_identity`で新規Identityを作成（または既存を取得）
   - マジックリンクを送信
   - マジックリンク送信完了画面にリダイレクト
5. バリデーション失敗時：422 Unprocessable Entityを返却

**レスポンス**:
- 成功時: マジックリンク送信完了画面（`/session/magic_link`）にリダイレクト
- 失敗時: HTTP 422 Unprocessable Entity

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| メールアドレス送信 | identities | INSERT/SELECT | 新規Identityレコード作成（または既存取得） |
| メールアドレス送信 | magic_links | INSERT | マジックリンクレコード作成 |

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

#### identities

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/SELECT | email_address | 入力されたメールアドレス（正規化済み） | `find_or_create_by!`使用 |
| INSERT | created_at | 現在日時 | 自動設定（新規作成時のみ） |
| INSERT | updated_at | 現在日時 | 自動設定（新規作成時のみ） |

#### magic_links

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | identity_id | 対象IdentityのID | 外部キー |
| INSERT | code | ランダム生成6文字コード | ユニーク制約あり |
| INSERT | purpose | sign_up | enum値（サインアップ用途） |
| INSERT | expires_at | 現在時刻 + 15分 | 有効期限 |
| INSERT | created_at | 現在日時 | 自動設定 |
| INSERT | updated_at | 現在日時 | 自動設定 |

## メッセージ仕様

| No | メッセージ種別 | メッセージ内容 | 表示条件 |
|----|--------------|---------------|---------|
| 1 | エラー | Try again later. | レート制限超過時（3分間に10回以上のリクエスト） |

## 例外処理

| 例外パターン | 対応内容 | 備考 |
|------------|---------|------|
| レート制限超過 | alertメッセージ「Try again later.」を表示してサインアップ画面にリダイレクト | 3分間に10回まで |
| サインアップ不許可 | ログイン画面（`/session/new`）にリダイレクト | `Account.accepting_signups?`がfalseの場合 |
| 認証済みアクセス | サインアップ完了画面（`/signup/completion/new`）にリダイレクト | 既にログイン済みの場合 |
| メールアドレス形式不正 | HTTP 422 Unprocessable Entityを返却 | バリデーションエラー |

## 備考

- 本画面はパスワードレス認証（マジックリンク認証）を採用しているため、パスワード入力フィールドは存在しない
- `data: { turbo: false }`が設定されており、Turboフレームを使用しない通常のフォーム送信が行われる
- フォームには`data: { controller: "form" }`が設定されており、Stimulus.jsのformコントローラーが適用される
- `Identity.find_or_create_by!`を使用しているため、既存のメールアドレスでも新たにマジックリンクが送信される（既存アカウントへのサインインと同等の動作）
- `/signup`へのアクセスは`/signup/new`にリダイレクトされる

---

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

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

### 推奨読解順序

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

まず、サインアップに関わるデータモデルの構造を理解することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | signup.rb | `app/models/signup.rb` | Signupモデルの構造、バリデーション、create_identityメソッド |
| 1-2 | identity.rb | `app/models/identity.rb` | Identityモデルの関連とsend_magic_linkメソッド |
| 1-3 | magic_link.rb | `app/models/magic_link.rb` | マジックリンクのコード生成、有効期限 |

**読解のコツ**: `Signup`はActiveModel::Modelをincludeしており、データベーステーブルを持たないフォームオブジェクトパターンです。`:identity_creation`コンテキストでのバリデーションに注目してください。

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

処理の起点となるコントローラーを特定します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | signups_controller.rb | `app/controllers/signups_controller.rb` | new/createアクションの処理フロー、before_actionによるアクセス制御 |

**主要処理フロー**:
1. **2-6行目**: クラス設定 - アカウントスコープ無効化、未認証許可、レート制限、before_action
2. **10-12行目**: `new`アクション - Signupモデルの新規インスタンス作成
3. **14-21行目**: `create`アクション - バリデーションとIdentity作成処理
4. **24-26行目**: `redirect_authenticated_user` - 認証済みユーザーのリダイレクト
5. **28-30行目**: `enforce_tenant_limit` - サインアップ許可チェック

#### Step 3: ビューとテンプレートを理解する

画面表示のテンプレートを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | new.html.erb | `app/views/signups/new.html.erb` | フォーム構造、Stimulus.jsコントローラー |
| 3-2 | _footer.html.erb | `app/views/sessions/_footer.html.erb` | 共通フッター（sessionsと共用） |

**主要処理フロー**:
- **6-19行目**: フォーム定義（turbo無効、formコントローラー）
- **9行目**: メールアドレス入力フィールド

#### Step 4: アクセス制御を理解する

サインアップ許可の判定ロジックを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | multi_tenantable.rb | `app/models/account/multi_tenantable.rb` | accepting_signups?メソッドの実装 |

**主要処理フロー**:
- **9-11行目**: `accepting_signups?` - マルチテナントまたはアカウント0件でtrue

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

```
SignupsController#create
    │
    ├─ [before_action] enforce_tenant_limit
    │      └─ Account.accepting_signups?
    │             └─ [false] redirect_to new_session_url
    │
    ├─ [before_action] redirect_authenticated_user
    │      └─ authenticated?
    │             └─ [true] redirect_to new_signup_completion_path
    │
    └─ Signup.new(signup_params)
           │
           ├─ signup.valid?(:identity_creation)
           │      └─ [false] head :unprocessable_entity
           │
           └─ [true] signup.create_identity
                  │
                  ├─ Identity.find_or_create_by!(email_address:)
                  │
                  └─ identity.send_magic_link(for: :sign_up)
                         ├─ MagicLink.create!
                         └─ MagicLinkMailer.sign_in_instructions.deliver_later
```

### データフロー図

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

                    ┌─────────────────┐
                    │ before_action   │
                    │ チェック        │
                    └────────┬────────┘
                             │
signup[email_address] ──▶ SignupsController#create
                             │
                             ├──▶ Signup.new
                             │        │
                             │        └─▶ valid?(:identity_creation)
                             │                 │
                             │                 ├─[NG]─▶ 422エラー
                             │                 │
                             │                 └─[OK]─▶ create_identity
                             │                              │
                             │                              ├─▶ Identity作成/取得
                             │                              │
                             │                              └─▶ MagicLink作成
                             │                                       │
                             │                                       └─▶ メール送信
                             │
                             └──▶ リダイレクト ─▶ /session/magic_link
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| signups_controller.rb | `app/controllers/signups_controller.rb` | コントローラー | サインアップ処理の制御 |
| new.html.erb | `app/views/signups/new.html.erb` | ビュー | サインアップ画面テンプレート |
| _footer.html.erb | `app/views/sessions/_footer.html.erb` | パーシャル | フッター表示 |
| signup.rb | `app/models/signup.rb` | モデル | サインアップフォームオブジェクト |
| identity.rb | `app/models/identity.rb` | モデル | ユーザー識別情報の管理 |
| magic_link.rb | `app/models/magic_link.rb` | モデル | マジックリンクの管理 |
| authentication.rb | `app/controllers/concerns/authentication.rb` | Concern | 認証共通処理 |
| via_magic_link.rb | `app/controllers/concerns/authentication/via_magic_link.rb` | Concern | マジックリンク認証処理 |
| magic_link_mailer.rb | `app/mailers/magic_link_mailer.rb` | メーラー | マジックリンクメール送信 |
| multi_tenantable.rb | `app/models/account/multi_tenantable.rb` | Concern | マルチテナント判定ロジック |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義 |
