# 画面設計書 137-スニペット一覧

## 概要

本ドキュメントは、GitLabにおけるプロジェクトスニペット一覧画面の設計を定義するものである。

### 本画面の処理概要

**業務上の目的・背景**：スニペットは、コードの断片や設定ファイル、メモなどを保存・共有するための機能である。プロジェクト固有のスニペットを管理することで、チームメンバー間でよく使うコード片やスクリプトを簡単に共有できる。この画面では、プロジェクトに関連付けられたスニペットの一覧を表示し、検索・フィルタリング・新規作成へのアクセスを提供する。

**画面へのアクセス方法**：プロジェクト画面から「コード」>「スニペット」またはプロジェクトサイドメニューの「スニペット」をクリックすることでアクセスできる。URLパスは `/:namespace/:project/-/snippets` となる。

**主要な操作・処理内容**：
1. プロジェクトスニペットの一覧表示（タイトル、作成者、作成日時など）
2. スコープによるフィルタリング（すべて/自分のもの）
3. 可視性レベルによるフィルタリング（公開/内部/非公開）
4. ソート順の変更
5. 新規スニペット作成画面への遷移
6. スニペット詳細画面への遷移

**画面遷移**：
- 遷移元：プロジェクトメニュー、プロジェクトダッシュボード
- 遷移先：スニペット詳細画面、スニペット新規作成画面

**権限による表示制御**：プロジェクトのスニペット機能が有効で、閲覧権限を持つユーザーがアクセスできる。プロジェクトメンバーは非公開スニペットも閲覧可能。管理者は全スニペットにアクセス可能。`create_snippet` 権限を持つユーザーには「New snippet」ボタンが表示される。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 71 | プロジェクトスニペット | 主機能 | プロジェクトスニペットの一覧表示 |

## 画面種別

一覧

## URL/ルーティング

| メソッド | パス | コントローラ#アクション |
|---------|------|------------------------|
| GET | `/:namespace_id/:project_id/-/snippets` | `projects/snippets#index` |

## 入出力項目

| 項目名 | 項目ID | 型 | 必須 | 入出力 | バリデーション | 説明 |
|--------|--------|-----|------|--------|---------------|------|
| プロジェクトID | project_id | String | Yes | 入力 | プロジェクト存在チェック | 対象プロジェクト |
| スコープ | scope | String | No | 入力 | all/are_private/are_internal/are_public | フィルタ条件 |
| ソート順 | sort | String | No | 入力 | - | ソート条件 |
| ページ番号 | page | Integer | No | 入力 | 正の整数 | ページネーション |

## 表示項目

| 項目名 | 項目ID | 型 | 説明 |
|--------|--------|-----|------|
| ページタイトル | page_title | String | 「Snippets」 |
| スコープメニュー | scope_menu | Component | All/Private/Internal/Publicの切り替え |
| 新規スニペットボタン | new_button | Link | 新規作成画面へのリンク |
| スニペット一覧 | snippets_list | List | スニペットカード一覧 |
| 空状態表示 | empty_state | Component | スニペットが存在しない場合 |

### スニペット一覧の各項目

| 項目名 | 説明 |
|--------|------|
| タイトル | スニペットのタイトル（リンク） |
| 可視性アイコン | 公開/内部/非公開を示すアイコン |
| 作成者 | スニペット作成者のアバターと名前 |
| 作成日時 | 作成日時（相対表示） |
| 参照番号 | スニペット参照番号（$123形式） |
| ファイル名 | スニペットのファイル名 |
| コメント数 | ノート（コメント）の数 |

## イベント仕様

### 1-スコープフィルタ切り替え

**トリガー**：スコープメニューのタブクリック

**処理フロー**：
1. 選択されたスコープでURLパラメータを更新
2. SnippetsFinderでフィルタリングされたスニペットを取得
3. 一覧を再表示

### 2-新規スニペット作成

**トリガー**：「New snippet」ボタンクリック

**処理フロー**：
1. スニペット新規作成画面へ遷移
2. URLは `/:namespace/:project/-/snippets/new`

### 3-スニペット詳細表示

**トリガー**：スニペットタイトルクリック

**処理フロー**：
1. スニペット詳細画面へ遷移
2. URLは `/:namespace/:project/-/snippets/:id`

### 4-ページネーション

**トリガー**：ページ番号クリック

**処理フロー**：
1. 指定ページのスニペットを取得
2. 一覧を更新

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 一覧表示 | snippets | SELECT | スニペット一覧取得 |
| 一覧表示 | users | SELECT | 作成者情報取得 |
| 一覧表示 | snippet_statistics | SELECT | 統計情報取得 |

この画面はデータの読み取りのみを行い、データベースの更新は行わない。

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| MSG001 | 情報 | Snippets are small pieces of code or notes... | 空状態時（説明文） |
| MSG002 | 情報 | New snippet | 新規作成ボタン（権限あり） |

## 例外処理

| 例外条件 | 処理内容 | 遷移先 |
|---------|---------|--------|
| プロジェクト未検出 | 404 Not Found | Not Found画面 |
| スニペット機能無効 | 404 Not Found | Not Found画面 |
| ページ番号範囲外 | リダイレクト | 最終ページへ |

## 備考

- スニペット機能の有効/無効はプロジェクト設定で制御される（check_snippets_available!）
- プロジェクトメンバーまたは管理者は非公開スニペットも閲覧可能
- SnippetsFinder でスコープ、可視性、ソートを適用
- inc_author、inc_statistics で関連データを一括取得（N+1対策）
- noteable_meta_data でコメント数を効率的に取得
- ページネーションは redirect_out_of_range で範囲外アクセスを処理

---

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

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

### 推奨読解順序

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

Snippetモデルと関連データを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | snippet.rb | `app/models/snippet.rb` | Snippetモデルの定義、スコープ、可視性 |
| 1-2 | snippets_finder.rb | `app/finders/snippets_finder.rb` | スニペット検索ロジック |

**読解のコツ**: `visibility_level` で公開/内部/非公開を管理。`scope` でフィルタリング条件を設定。Finderパターンで検索ロジックを分離。

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

ビューテンプレートとコントローラの処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.html.haml | `app/views/projects/snippets/index.html.haml` | スニペット一覧画面のビュー |
| 2-2 | snippets_controller.rb | `app/controllers/projects/snippets_controller.rb` | indexアクション |

**主要処理フロー**:
1. **行9**: `check_snippets_available!` でスニペット機能有効チェック
2. **行19-33**: `index` アクションでスニペット一覧取得
3. **行20-22**: SnippetsCountService で件数取得
4. **行24-29**: SnippetsFinder でスニペット取得（スコープ、ソート適用）
5. **行31**: redirect_out_of_range でページ範囲外処理
6. **行33**: noteable_meta_data でコメント数取得

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

スニペット一覧表示の共有コンポーネントを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | _snippets_scope_menu.html.haml | `app/views/snippets/_snippets_scope_menu.html.haml` | スコープメニュー |
| 3-2 | _list.html.haml | `app/views/shared/snippets/_list.html.haml` | スニペット一覧表示 |
| 3-3 | _empty_states/snippets.html.haml | `app/views/shared/empty_states/_snippets.html.haml` | 空状態表示 |

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

```
projects/snippets/index.html.haml
    │
    ├─ スニペットが存在する場合
    │      │
    │      ├─ snippets/_snippets_scope_menu.html.haml
    │      │      └─ All/Private/Internal/Public タブ
    │      │
    │      ├─ New snippet ボタン（権限あり時）
    │      │
    │      └─ shared/snippets/_list.html.haml
    │             └─ @snippets.each でスニペット表示
    │
    └─ スニペットが存在しない場合
           │
           └─ shared/empty_states/_snippets.html.haml
                  └─ 空状態説明 + New snippet ボタン

SnippetsController#index
    │
    ├─ Snippets::CountService
    │      └─ スコープ別件数取得
    │
    ├─ SnippetsFinder
    │      ├─ organization_id フィルタ
    │      ├─ project フィルタ
    │      ├─ scope フィルタ
    │      └─ sort 適用
    │
    └─ noteable_meta_data
           └─ コメント数取得
```

### データフロー図

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

project_id ──────▶ SnippetsController#index ──────▶ check_snippets_available!
scope                      │
sort                       │
page                       ▼
                    Snippets::CountService
                           │
                           ▼
                    SnippetsFinder
                           │
                           ├─ scope適用（all/private/internal/public）
                           ├─ sort適用
                           └─ page適用
                                  │
                                  ▼
                           @snippets（Snippetコレクション）
                           @snippet_counts（スコープ別件数）
                                  │
                                  ▼
                           ビューレンダリング
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.html.haml | `app/views/projects/snippets/index.html.haml` | テンプレート | スニペット一覧画面 |
| snippets_controller.rb | `app/controllers/projects/snippets_controller.rb` | コントローラ | HTTPリクエスト処理 |
| snippet.rb | `app/models/snippet.rb` | モデル | Snippetエンティティ |
| snippets_finder.rb | `app/finders/snippets_finder.rb` | Finder | スニペット検索ロジック |
| count_service.rb | `app/services/snippets/count_service.rb` | サービス | 件数カウント |
| _snippets_scope_menu.html.haml | `app/views/snippets/_snippets_scope_menu.html.haml` | パーシャル | スコープメニュー |
| _list.html.haml | `app/views/shared/snippets/_list.html.haml` | パーシャル | 一覧表示 |
| project.rb | `config/routes/project.rb` | ルーティング | URLルーティング定義（行433-437） |
