# 画面設計書 190-グループ新規作成

## 概要

本ドキュメントは、GitLabの組織配下グループ新規作成画面の設計を記述したものである。

### 本画面の処理概要

**業務上の目的・背景**：組織に所属する新しいグループを作成するための画面である。組織は複数のグループを持つことができ、各グループは複数のプロジェクトを管理できる。組織コンテキスト内でグループを作成することで、組織全体のリソース管理を一元化し、メンバー間のコラボレーションを促進できる。

**画面へのアクセス方法**：組織グループ・プロジェクト画面（186）の「New group」ボタンをクリック、または直接URL `/-/organizations/:organization_path/groups/new` にアクセスする。

**主要な操作・処理内容**：
1. グループ名の入力
2. グループURLの入力（自動生成対応）
3. 可視性レベルの選択
4. グループの作成

**画面遷移**：
- 遷移元：組織グループ・プロジェクト画面（186-組織グループ・プロジェクト）
- 遷移先：作成成功時は新しいグループの詳細画面へ、キャンセル時はグループ一覧（display=groups）へ

**権限による表示制御**：
- `create_group` 権限を持つユーザーのみがアクセス可能
- 権限がない場合はアクセス拒否となる

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 133 | グループ管理 | 主機能 | 組織配下でのグループ作成 |

## 画面種別

新規作成

## URL/ルーティング

- パス: `/-/organizations/:organization_path/groups/new`
- HTTPメソッド: GET（画面表示）、POST（作成処理）
- コントローラー: `Organizations::GroupsController#new`（表示）、`Organizations::GroupsController#create`（作成）

## 入出力項目

| 項目名 | 項目ID | データ型 | 必須 | 入力形式 | 説明 |
|--------|--------|----------|------|----------|------|
| グループ名 | name | String | Yes | テキスト | グループの表示名。文字、数字、絵文字、アンダースコアで開始。ピリオド、ダッシュ、スペース、括弧も使用可能 |
| グループURL | path | String | Yes | テキスト | URLパス。最大255文字。英数字とアンダースコアが使用可能。ダッシュで開始、ピリオドで終了不可 |
| 可視性レベル | visibility_level | Integer | Yes | ラジオボタン | Private/Internal/Public |

## 表示項目

| 項目名 | 表示形式 | 説明 |
|--------|----------|------|
| ページタイトル | 見出し | "New group" |
| グループ説明文1 | テキスト | グループについての説明（リンク付き） |
| グループ説明文2 | テキスト | サブグループについての説明（リンク付き） |
| グループ名入力 | テキスト入力 | プレースホルダー: "My group" |
| グループURL入力 | テキスト入力 | ベースパス付き |
| SCIM警告 | アラート | グループ名にピリオドを含む場合の警告 |
| 可視性レベル選択 | ラジオボタン | 利用可能なレベルから選択 |
| 作成ボタン | ボタン | "Create group" |
| キャンセルボタン | リンク | グループ一覧へ戻る |

## イベント仕様

### 1-グループ名入力

1. グループ名を入力
2. パスが手動入力されていない場合、グループ名からURLパスを自動生成（slugify）
3. バリデーション実行

### 2-グループURL入力

1. URLパスを入力
2. 手動入力フラグを設定
3. パス形式のバリデーション（最大長、パターン）
4. URL利用可能性チェック（非同期）

### 3-作成ボタン押下

1. クライアントサイドバリデーションを実行
2. REST API `POST /groups` を呼び出し
3. 成功時、成功メッセージと共に新しいグループの詳細画面へリダイレクト
4. 失敗時、エラーアラートを表示

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

1. 組織グループ・プロジェクト画面（display=groups）へ遷移

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 作成ボタン押下 | namespaces | INSERT | グループ（名前空間）の作成 |
| 作成ボタン押下 | routes | INSERT | ルート情報の作成 |
| 作成ボタン押下 | members | INSERT | 作成者をオーナーとして登録 |

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

#### namespaces

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | name | フォーム入力値 | グループ名 |
| INSERT | path | フォーム入力値 | URLパス |
| INSERT | visibility_level | 選択値 | 0:Private/10:Internal/20:Public |
| INSERT | type | 'Group' | グループ種別 |
| INSERT | organization_id | 組織ID | 所属組織 |
| INSERT | created_at | 現在時刻 | 作成日時 |
| INSERT | updated_at | 現在時刻 | 更新日時 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| MSG-001 | 成功 | Group {group_name} was successfully created. | 作成成功時 |
| MSG-002 | エラー | An error occurred creating a group in this organization. Please try again. | 作成失敗時 |
| MSG-003 | 警告 | Your group name must not contain a period if you intend to use SCIM integration, as it can lead to errors. | 常時表示 |
| MSG-004 | バリデーション | Enter a descriptive name for your group. | グループ名未入力時 |
| MSG-005 | バリデーション | Enter a path for your group. | URLパス未入力時 |
| MSG-006 | バリデーション | Group path cannot be longer than {length} characters. | パス長超過時 |
| MSG-007 | バリデーション | Choose a group path that does not start with a dash or end with a period. | パス形式不正時 |

## 例外処理

| 例外パターン | 処理内容 |
|-------------|----------|
| 権限不足 | アクセス拒否 |
| バリデーションエラー | フィールドにエラーメッセージを表示 |
| パス重複 | バリデーションエラーとして処理 |
| ネットワークエラー | アラートでエラーメッセージを表示 |

## 備考

- グループ名入力時、URLパスは自動生成される（slugify処理）
- URLパスを手動入力した後は自動生成が無効になる
- URLパスの最大長は `Namespace::URL_MAX_LENGTH`（255文字）
- URLパスの形式は `Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS` に従う
- 可視性レベルは組織の設定と管理者の設定により制限される場合がある
- SCIM統合を使用する場合、グループ名にピリオドを含めないことが推奨される
- 作成処理は `Groups::CreateService` を通じて実行される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | namespace.rb | `app/models/namespace.rb` | URL_MAX_LENGTH、パスバリデーション |
| 1-2 | group.rb | `app/models/group.rb` | グループモデルの構造 |

**読解のコツ**: namespaceがグループの親クラスであり、パス関連の制約が定義されている点を確認すること。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | groups_controller.rb | `app/controllers/organizations/groups_controller.rb` | new/createアクション |

**主要処理フロー**:
1. **行10**: `before_action :authorize_create_group!` - 権限チェック
2. **行15**: `def new; end` - 空のアクション
3. **行19-28**: `def create` - グループ作成処理
4. **行62-64**: `create_group` - Groups::CreateServiceの呼び出し

#### Step 3: ヘルパーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | organization_helper.rb | `app/helpers/organizations/organization_helper.rb` | organization_groups_new_app_data |

**主要処理フロー**:
- **行54-58**: `organization_groups_new_app_data` - Vue用データ準備
- **行117-128**: `shared_organization_groups_app_data` - 共通データ（パス制約等）

#### Step 4: フロントエンドを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | new.html.haml | `app/views/organizations/groups/new.html.haml` | マウントポイント |
| 4-2 | app.vue | `app/assets/javascripts/organizations/groups/new/components/app.vue` | メインコンポーネント |
| 4-3 | new_edit_form.vue | `app/assets/javascripts/groups/components/new_edit_form.vue` | 共通フォームコンポーネント |

**主要処理フロー**:
- **行34-43**: inject - 初期データの注入（basePath, availableVisibilityLevels等）
- **行59-86**: `onSubmit` - REST API呼び出しとリダイレクト処理
- **行66-72**: axios.post - グループ作成リクエスト

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

```
new.html.haml
    │
    └─ #js-organizations-groups-new (Vue mount point)
           │
           └─ OrganizationGroupsNewApp (app.vue)
                  │
                  ├─ inject: basePath
                  ├─ inject: groupsAndProjectsOrganizationPath
                  ├─ inject: groupsOrganizationPath
                  ├─ inject: availableVisibilityLevels
                  ├─ inject: restrictedVisibilityLevels
                  ├─ inject: defaultVisibilityLevel
                  ├─ inject: pathMaxlength
                  ├─ inject: pathPattern
                  │
                  └─ NewEditForm (new_edit_form.vue)
                         │
                         ├─ グループ名入力
                         │      └─ watch: name → slugify → path
                         ├─ GroupPathField
                         │      └─ URL利用可能性チェック
                         ├─ VisibilityLevelRadioButtons
                         │
                         └─ onSubmit
                                │
                                └─ axios.post(groupsOrganizationPath)
                                       │
                                       └─ GroupsController#create
                                              │
                                              └─ Groups::CreateService#execute
```

### データフロー図

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

URLアクセス ───▶ GroupsController#new
                      │
                      ├─ authorize_create_group!
                      │
                      └─ organization_groups_new_app_data
                              │
                              └─▶ Vue.js App
                                      │
                                      └─▶ フォーム表示

グループ名入力 ─────▶ watch
                      │
                      └─▶ slugify(name) → path自動設定

フォーム送信 ─────▶ onSubmit
                      │
                      └─ axios.post
                              │
                              └─ GroupsController#create
                                      │
                                      └─ Groups::CreateService#execute
                                              │
                                              ├─ バリデーション
                                              │
                                              └─▶ 成功: グループ詳細へリダイレクト
                                                  失敗: エラーアラート表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| groups_controller.rb | `app/controllers/organizations/groups_controller.rb` | コントローラー | リクエスト処理 |
| new.html.haml | `app/views/organizations/groups/new.html.haml` | ビュー | マウントポイント |
| app.vue | `app/assets/javascripts/organizations/groups/new/components/app.vue` | Vue | メインコンポーネント |
| new_edit_form.vue | `app/assets/javascripts/groups/components/new_edit_form.vue` | Vue | フォームコンポーネント |
| group_path_field.vue | `app/assets/javascripts/groups/components/group_path_field.vue` | Vue | パス入力コンポーネント |
| visibility_level_radio_buttons.vue | `app/assets/javascripts/visibility_level/components/visibility_level_radio_buttons.vue` | Vue | 可視性選択 |
| organization_helper.rb | `app/helpers/organizations/organization_helper.rb` | ヘルパー | app_data生成 |
| create_service.rb | `app/services/groups/create_service.rb` | サービス | グループ作成ロジック |
| organizations.rb | `config/routes/organizations.rb` | ルーティング | URL定義（行20） |
