# 画面設計書 166-アチーブメント

## 概要

本ドキュメントはGitLabのグループアチーブメント管理画面の設計仕様を記載したものである。

### 本画面の処理概要

グループ内でユーザーに授与できるアチーブメント（実績バッジ）を管理する画面である。グループ管理者がアチーブメントを作成し、メンバーに授与することで、貢献や達成を可視化・称賛できる。

**業務上の目的・背景**：チームメンバーのモチベーション向上と貢献の可視化を目的として、アチーブメント（バッジ）システムを提供する。プロジェクトへの貢献、スキル習得、マイルストーン達成などを認め、チーム内での評価・称賛を促進する。本画面により、グループ単位でアチーブメントを管理し、メンバーのプロフィールに表示される実績を管理できる。

**画面へのアクセス方法**：グループメニュー > Achievements からアクセスする。または直接URL `/{group_path}/-/achievements` にアクセスする。

**主要な操作・処理内容**：
1. アチーブメント一覧の表示
2. 新規アチーブメントの作成（名前、説明、アイコン画像）
3. アチーブメントのユーザーへの授与
4. アチーブメント取得ユーザー一覧の表示
5. ページネーションによる一覧の閲覧

**画面遷移**：
- 遷移元：グループ設定画面、グループメニュー
- 遷移先：アチーブメント新規作成画面（同一ページ内）

**権限による表示制御**：
- `read_achievement`権限：アチーブメント一覧の閲覧が可能
- `admin_achievement`権限：アチーブメントの作成が可能
- `award_achievement`権限：アチーブメントの授与が可能

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 136 | アクティビティフィード | 主機能 | アチーブメントの管理 |

## 画面種別

一覧

## URL/ルーティング

- パス: `/{group_path}/-/achievements`
- コントローラー: `Groups::AchievementsController#index`
- HTTPメソッド: GET

## 入出力項目

| 項目名 | 項目ID | 入出力 | データ型 | 必須 | 説明 |
|--------|--------|--------|----------|------|------|
| グループパス | group_path | 入力 | String | 必須 | 対象グループのパス |
| 検索クエリ | search | 入力 | String | 任意 | アチーブメント検索文字列 |
| アチーブメント名 | name | 入力 | String | 必須 | アチーブメント名（作成時） |
| 説明 | description | 入力 | String | 任意 | アチーブメント説明（作成時） |
| アバター | avatar | 入力 | File | 任意 | アイコン画像（作成時） |

## 表示項目

| 項目名 | データソース | 説明 |
|--------|-------------|------|
| アイコン画像 | achievements.avatar_url | アチーブメントアイコン（未設定時はGitLabロゴ） |
| アチーブメント名 | achievements.name | アチーブメント名 |
| 説明 | achievements.description | アチーブメント説明文 |
| 取得ユーザー数 | achievements.unique_users.count | アチーブメントを取得したユーザー数 |
| 取得ユーザー一覧 | achievements.unique_users | アバターリスト形式で表示 |

## イベント仕様

### 1-新規作成ボタン押下

アチーブメント作成フォームを表示する。

- トリガー: 「New achievement」ボタンクリック
- 処理: Vue Routerで新規作成画面へ遷移
- 条件: `admin_achievement`権限を持つ場合のみ表示

### 2-アチーブメント作成フォーム送信

新規アチーブメントを作成する。

- トリガー: フォームの「Create」ボタンクリック
- 処理: GraphQL mutation `createAchievement`を実行
- 成功時: 一覧に追加、成功メッセージ表示
- 失敗時: エラーメッセージ表示

### 3-授与ボタン押下

アチーブメントをユーザーに授与するダイアログを表示。

- トリガー: 「Award」ボタンクリック
- 処理: ユーザー選択ダイアログ表示後、GraphQL mutationを実行
- 条件: `award_achievement`権限を持つ場合のみ表示

### 4-ページ遷移

次/前のページへ移動。

- トリガー: ページネーションコントロール操作
- 処理: GraphQL APIでページング処理

### 5-ユーザー一覧の追加読み込み

取得ユーザーの追加読み込み。

- トリガー: 「Load more」ボタンクリック
- 処理: `getMoreUniqueUsers`クエリで追加取得

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 一覧表示 | achievements | SELECT | アチーブメント一覧の取得 |
| 一覧表示 | user_achievements | SELECT | 授与情報の取得 |
| アチーブメント作成 | achievements | INSERT | 新規アチーブメント作成 |
| アチーブメント授与 | user_achievements | INSERT | ユーザーへの授与 |

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

#### achievements

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | 全項目 | namespace_id = グループID | グループのアチーブメント |
| INSERT | name | ユーザー入力値 | アチーブメント名（最大255文字） |
| INSERT | description | ユーザー入力値 | 説明（最大1024文字） |
| INSERT | namespace_id | グループID | 所属グループ |

#### user_achievements

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | user_id, achievement_id | achievement_id = 対象ID | 授与されたユーザー |
| INSERT | user_id | 選択されたユーザーID | 授与対象ユーザー |
| INSERT | achievement_id | アチーブメントID | 授与アチーブメント |
| INSERT | awarded_by_user_id | current_user.id | 授与者 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|---------|
| MSG001 | 情報 | There are currently no achievements. | アチーブメント未作成時 |
| MSG002 | 情報 | Not yet awarded. | 未授与のアチーブメント |
| MSG003 | 情報 | %{userCount} awarded users | 授与ユーザー数表示 |
| MSG004 | 成功 | Achievement was created | 作成成功時 |
| MSG005 | 成功 | Achievement was awarded | 授与成功時 |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| 権限不足（read_achievement） | 404ページを表示 |
| グループ未存在 | 404ページを表示 |
| 名前重複 | エラーメッセージを表示 |
| バリデーションエラー | エラーメッセージを表示 |

## 備考

- 本画面はVue.jsコンポーネントとVue Routerによるspa構成
- データ取得・更新はGraphQL APIを使用
- アイコン未設定時はGitLabロゴ（gitlab_logo.png）を表示
- 取得ユーザー一覧はUserAvatarListコンポーネントで表示
- ページネーションは20件/ページ

---

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

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

### 推奨読解順序

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

アチーブメントのデータモデルとバリデーションを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | achievement.rb | `app/models/achievements/achievement.rb` | モデル定義、バリデーション、リレーション |

**読解のコツ**:
- `validates :name`（行15-18）で名前バリデーション確認
- `validates :description`（行19）で説明バリデーション確認
- `has_many :user_achievements`（行10）で授与関連確認

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

HAMLテンプレートとコントローラーの処理を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.html.haml | `app/views/groups/achievements/index.html.haml` | Vueマウントポイント、viewModel設定 |
| 2-2 | achievements_controller.rb | `app/controllers/groups/achievements_controller.rb` | コントローラー、権限チェック |

**主要処理フロー**:
1. **行8-15**: `#js-achievements-app`要素にviewModelをJSON形式で設定
2. viewModelに`canAdminAchievement`、`canAwardAchievement`、`groupFullPath`等を含む
3. **行16-17**: `authorize_read_achievement!`で権限チェック

#### Step 3: フロントエンド実装を理解する

Vueコンポーネントの実装を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | achievements_app.vue | `app/assets/javascripts/achievements/components/achievements_app.vue` | メインコンポーネント |

**主要処理フロー**:
- `ENTRIES_PER_PAGE = 20`（行18）でページサイズ
- `getGroupAchievements`クエリでアチーブメント取得
- `loadMoreUsers`メソッドで追加ユーザー読み込み

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

```
Groups::AchievementsController#index
    │
    ├─ authorize_read_achievement! (認可チェック)
    │
    └─ View: index.html.haml
           └─ Vue: AchievementsApp
                  │
                  ├─ GraphQL: getGroupAchievements
                  │
                  ├─ GraphQL: getMoreUniqueUsers
                  │
                  └─ Components
                         ├─ AwardButton
                         ├─ UserAvatarList
                         └─ GlKeysetPagination
```

### データフロー図

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

URLリクエスト ───▶ AchievementsController ───▶ HAMLテンプレート
                         │
                         ▼
                  認可チェック
                         │
                         ▼
                  Vue初期化 ───▶ GraphQL API ───▶ アチーブメント一覧

授与ボタン ───▶ awardAchievement mutation ───▶ user_achievements INSERT
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.html.haml | `app/views/groups/achievements/index.html.haml` | テンプレート | Vueマウントポイント定義 |
| achievements_controller.rb | `app/controllers/groups/achievements_controller.rb` | コントローラー | リクエスト処理・認可 |
| achievement.rb | `app/models/achievements/achievement.rb` | モデル | アチーブメントデータ定義 |
| achievements_app.vue | `app/assets/javascripts/achievements/components/achievements_app.vue` | Vue | メインコンポーネント |
| achievements_form.vue | `app/assets/javascripts/achievements/components/achievements_form.vue` | Vue | 作成フォーム |
| award_button.vue | `app/assets/javascripts/achievements/components/award_button.vue` | Vue | 授与ボタン |
