# 画面設計書 19-プロジェクト探索

## 概要

本ドキュメントは、GitLabのプロジェクト探索画面（Explore Projects Index）の画面設計を定義したものである。

### 本画面の処理概要

公開されているプロジェクトを探索・検索するための画面である。未ログインユーザーでもアクセス可能。

**業務上の目的・背景**：GitLabはオープンソースプロジェクトのホスティングプラットフォームとして、公開プロジェクトの発見と参加を促進する機能を提供している。本画面は、PublicまたはInternalなプロジェクトを検索・閲覧し、興味のあるプロジェクトを見つけてスターを付けたり、フォークしたりするための入口となる。

**画面へのアクセス方法**：サイドバーまたはナビゲーションから「Explore」>「Projects」メニューを選択。直接URLアクセスの場合は `/explore/projects` へアクセスする。

**主要な操作・処理内容**：
1. 公開プロジェクト一覧の表示（ページネーション対応）
2. 名前・説明による検索
3. トピックによるフィルタリング
4. ソート順の変更（Latest Activity、Name、Stars等）
5. スター付きプロジェクトの表示切り替え
6. 各プロジェクトの詳細画面への遷移

**画面遷移**：
- 遷移元：トップページ、サイドバー、ナビゲーション
- 遷移先：プロジェクト詳細画面、トピック別プロジェクト一覧、プロジェクト新規作成画面

**権限による表示制御**：未ログインユーザーはPublicプロジェクトのみ閲覧可能。ログインユーザーはPublic + Internalプロジェクトを閲覧可能。匿名ユーザーの検索は機能フラグで無効化可能。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | プロジェクト管理 | 主機能 | 公開プロジェクトの探索 |

## 画面種別

一覧

## URL/ルーティング

| URL | HTTPメソッド | 説明 |
|-----|-------------|------|
| `/explore/projects` | GET | プロジェクト探索（HTML） |
| `/explore/projects.json` | GET | プロジェクト探索（JSON API） |
| `/explore/projects/trending` | GET | トレンドプロジェクト |
| `/explore/projects/starred` | GET | スター数順プロジェクト |
| `/explore/projects/topics` | GET | トピック一覧 |
| `/explore/projects/topics/:topic_name` | GET | トピック別プロジェクト |

## 入出力項目

### 入力項目（検索・フィルタ）

| 項目名 | 型 | 必須 | 説明 |
|--------|-----|------|------|
| name | string | No | プロジェクト名検索 |
| search | string | No | 全文検索 |
| sort | string | No | ソート順 |
| topic | string | No | トピックフィルタ |
| page | integer | No | ページ番号 |

### 出力項目

| 項目名 | 型 | 説明 |
|--------|-----|------|
| @projects | Array | プロジェクト一覧 |
| @all_user_projects | Array | ユーザーのプロジェクト（カウント用） |
| @all_starred_projects | Array | スター付きプロジェクト（カウント用） |

## 表示項目

### プロジェクト一覧

| 項目名 | 説明 | 備考 |
|--------|------|------|
| プロジェクト名 | プロジェクトのフルパス | リンク |
| アバター | プロジェクトのアバター画像 | |
| 説明 | プロジェクトの説明 | 部分表示 |
| トピック | 関連するトピック | タグ表示 |
| スター数 | スター数 | |
| フォーク数 | フォーク数 | |
| 最終更新日時 | 最終更新日時 | 相対時間表示 |
| 可視性 | Public/Internal | アイコン表示 |

## イベント仕様

### 1-プロジェクトリストの読み込み

ページ読み込み時にProjectsFinderを使用してプロジェクト一覧を取得する。

**処理フロー**：
1. set_non_archived_param（アーカイブ済み除外）
2. set_sorting（ソート順設定）
3. limit_pages（ページ数制限）
4. ProjectsFinderによるプロジェクト検索
5. preload_associations（関連データ事前読み込み）
6. テンプレートレンダリング

### 2-検索実行

検索フォームに入力して検索すると、マッチするプロジェクトが表示される。

**検索制限**：
- 最小検索文字数: 3文字
- 匿名ユーザーの検索: disable_anonymous_project_searchフラグで制御可能

### 3-トレンドプロジェクト

「Trending」タブを選択すると、トレンドのプロジェクトが表示される（retire_trending_projectsフラグで廃止予定）。

### 4-スター数順プロジェクト

「Most stars」タブを選択すると、スター数の多い順でプロジェクトが表示される。

### 5-トピック別表示

トピックをクリックすると、該当トピックのプロジェクト一覧が表示される。

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| ページ表示 | projects | SELECT | フィルタ条件に合致するプロジェクトを取得 |
| ページ表示 | topics | SELECT | プロジェクトに関連するトピック取得 |
| ページ表示 | namespaces | SELECT | プロジェクトの名前空間情報取得 |
| ページ表示 | routes | SELECT | プロジェクトのルート情報取得 |

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

#### projects

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, name, path, description, visibility_level, star_count, forks_count, last_activity_at | visibility_level: public/internal | ProjectsFinder経由 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| MSG-001 | 情報 | "No projects found" | プロジェクトが存在しない場合 |
| MSG-002 | 情報 | "You must sign in to search for specific projects." | 匿名ユーザーが検索しようとした場合 |
| MSG-003 | エラー | "Page out of bounds" | ページ番号が範囲外の場合 |

## 例外処理

| 例外条件 | 処理内容 | 表示メッセージ |
|----------|----------|---------------|
| ページ範囲外 | page_out_of_boundsレスポンス | Page out of bounds |
| 最小検索文字数未満 | 検索スキップ | - |

## 備考

- サーバーサイドレンダリング + Vue.jsのハイブリッド実装（explore_projects_vueフラグで切り替え）
- HTML/JSON両形式のレスポンスをサポート
- PageLimiterによるページ数制限（最大50ページ）
- preload_associationsで関連データを事前読み込み
- retire_trending_projectsフラグでトレンド機能廃止予定
- disable_anonymous_project_searchフラグで匿名検索無効化可能
- current_organizationによる組織スコープのフィルタリング

---

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

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

### 推奨読解順序

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

まず、プロジェクトのデータ構造を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | project.rb | `app/models/project.rb` | Projectモデルの定義、可視性レベル |
| 1-2 | projects_finder.rb | `app/finders/projects_finder.rb` | プロジェクト検索ロジック |

**読解のコツ**: ProjectはNamespaceに所属し、Topics（タグ）を持つ。visibility_levelでPublic/Internal/Privateを制御。

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | projects_controller.rb | `app/controllers/explore/projects_controller.rb` | indexアクション、各種before_action |

**主要処理フロー**:
1. **Line 15-16**: before_actionでset_non_archived_param、set_sorting
2. **Line 18-26**: explore_projects_vue、retire_trending_projectsフラグ対応、limit_pages
3. **Line 35-47**: indexアクションでHTML/JSON形式を分岐
4. **Line 115-130**: load_projectsメソッドでProjectsFinder実行

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.html.haml | `app/views/explore/projects/index.html.haml` | HAMLテンプレート |

**主要処理フロー**:
- **Line 1**: canonical URL設定
- **Line 3**: headパーシャルのレンダリング
- **Line 4-8**: explore_projects_vueフラグによる条件分岐
- **Line 5**: Vue.jsアプリのマウント（`#js-explore-projects`）
- **Line 7-8**: SSR版のnav + projectsパーシャル

#### Step 4: Finderを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | projects_finder.rb | `app/finders/projects_finder.rb` | 検索条件、フィルタリング |

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

```
Explore::ProjectsController#index
    |
    +-- set_non_archived_param (before_action)
    |
    +-- set_sorting (before_action)
    |
    +-- limit_pages (before_action)
    |
    +-- show_alert_if_search_is_disabled
    |
    +-- load_projects
    |       |
    |       +-- load_project_counts
    |       |       |
    |       |       +-- ProjectsFinder.new(params: { non_public: true })
    |       |       +-- ProjectsFinder.new(params: { starred: true })
    |       |
    |       +-- ProjectsFinder.new(current_user, params).execute
    |       |
    |       +-- preload_associations(projects)
    |       |
    |       +-- projects.page(page).without_count
    |       |
    |       +-- prepare_projects_for_rendering(projects)
    |
    +-- respond_to
            |
            +-- format.html (render)
            |       |
            |       +-- [explore_projects_vue enabled]
            |       |       +-- #js-explore-projects (Vue.js)
            |       |
            |       +-- [explore_projects_vue disabled]
            |               +-- 'explore/projects/nav'
            |               +-- 'projects' partial
            |
            +-- format.json (render json)
```

### データフロー図

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

URLパラメータ ───▶ Explore::ProjectsController ───▶ HTML/JSONレスポンス
(name, search,          |
 sort, topic)           |
                        v
                   ProjectsFinder
                        |
                        v
                   Project (Model)
                        |
                        v
                   PostgreSQL
                        |
                        v
                   preload_associations
                        |
                        v
                   prepare_projects_for_rendering
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| projects_controller.rb | `app/controllers/explore/projects_controller.rb` | コントローラ | リクエスト処理 |
| index.html.haml | `app/views/explore/projects/index.html.haml` | ビュー | 画面テンプレート |
| _head.html.haml | `app/views/explore/projects/_head.html.haml` | パーシャル | ヘッダー部分 |
| _nav.html.haml | `app/views/explore/projects/_nav.html.haml` | パーシャル | ナビゲーション |
| _projects.html.haml | `app/views/explore/projects/_projects.html.haml` | パーシャル | プロジェクトリスト |
| projects_finder.rb | `app/finders/projects_finder.rb` | ファインダー | プロジェクト検索 |
| project.rb | `app/models/project.rb` | モデル | プロジェクトデータ定義 |
| explore.rb | `config/routes/explore.rb` | ルート | URL定義 |
