# 画面設計書 11-キャンペーン一覧画面

## 概要

Fat Free CRM におけるキャンペーン（マーケティングキャンペーン）を一覧表示・管理するための画面設計書である。

### 本画面の処理概要

本画面は、マーケティング活動の計画・実行・追跡を行うためのキャンペーン情報を一覧形式で表示し、効率的な管理を可能にする画面である。

**業務上の目的・背景**：
マーケティング担当者が実施するキャンペーン（メール配信、広告、イベントなど）を一元管理し、各キャンペーンの進捗状況やリード獲得数を把握する必要がある。本画面を通じて、どのキャンペーンが効果的か、目標に対してどの程度達成しているかを素早く確認でき、マーケティング戦略の意思決定を支援する。

**画面へのアクセス方法**：
1. ログイン後、サイドバーまたはナビゲーションメニューから「Campaigns」をクリック
2. ダッシュボードのキャンペーン関連リンクからアクセス
3. URL直接入力: `/campaigns`

**主要な操作・処理内容**：
1. キャンペーン一覧の表示（ページネーション対応）
2. ステータスによるフィルタリング（計画中、開始済み、完了など）
3. テキスト検索による絞り込み
4. 新規キャンペーン作成フォームの表示（Ajaxモーダル）
5. キャンペーン詳細画面への遷移
6. キャンペーンの編集・削除
7. CSV/XLSエクスポート
8. RSS/ATOMフィードのエクスポート
9. 高度な検索（Ransack）

**画面遷移**：
- 遷移元: ダッシュボード、取引先詳細画面、リード詳細画面
- 遷移先: キャンペーン詳細画面、キャンペーン新規作成フォーム、キャンペーン編集フォーム

**権限による表示制御**：
- 認証済みユーザーのみアクセス可能（CanCanCan による認可）
- ユーザーは自身が作成したキャンペーン、または共有されたキャンペーンのみ表示・操作可能
- アクセス権限（Public/Private/Shared）によって他ユーザーへの公開範囲が決定される

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 10 | キャンペーン一覧表示 | 主機能 | キャンペーン一覧表示、ステータスフィルタリング、CSV/XLS/RSS/ATOMエクスポート |
| 81 | 高度な検索 | 補助機能 | Ransackによる詳細検索 |
| 14 | キャンペーン削除 | 補助機能 | キャンペーンの削除処理 |

## 画面種別

一覧

## URL/ルーティング

| HTTPメソッド | URL | アクション | 説明 |
|-------------|-----|----------|------|
| GET | /campaigns | index | 一覧表示 |
| GET | /campaigns.csv | index | CSV形式でエクスポート |
| GET | /campaigns.xls | index | XLS形式でエクスポート |
| POST | /campaigns/filter | filter | ステータスフィルタリング |
| GET | /campaigns/redraw | redraw | 表示設定変更後の再描画 |
| GET | /campaigns/advanced_search | advanced_search | 高度な検索 |

## 入出力項目

### 検索・フィルタ入力

| 項目名 | 入力形式 | 必須 | 説明 |
|--------|---------|------|------|
| 検索キーワード | テキスト | - | キャンペーン名での検索 |
| ステータスフィルタ | チェックボックス | - | planned, started, on_hold, completed, called_off |
| 表示件数 | セレクト | - | 1ページあたりの表示件数 |
| ソート順 | セレクト | - | 名前、作成日、更新日等 |

## 表示項目

### キャンペーン一覧

| 項目名 | データ型 | 説明 |
|--------|---------|------|
| キャンペーン名 | string | キャンペーンの名称（リンク） |
| ステータス | string | planned/started/on_hold/completed/called_off |
| 開始日 | date | キャンペーン開始日 |
| 終了日 | date | キャンペーン終了日 |
| リード数 | integer | 獲得したリード数 |
| 商談数 | integer | 関連する商談数 |
| 収益 | decimal | 実績収益 |
| タグ | string | 付与されたタグ |

### サイドバー（ステータス別件数）

| 項目名 | データ型 | 説明 |
|--------|---------|------|
| 各ステータスの件数 | integer | ステータス別のキャンペーン数 |
| 合計件数 | integer | 全キャンペーン数 |

## イベント仕様

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

「Create Campaign」ボタンをクリックすると、Ajax通信でキャンペーン新規作成フォーム（_new.html.haml）がモーダルとして画面上部に展開される。

### 02-キャンペーン名リンク押下

キャンペーン名のリンクをクリックすると、該当キャンペーンの詳細画面（/campaigns/:id）に遷移する。

### 03-ステータスフィルタ変更

サイドバーのステータスチェックボックスをクリックすると、Ajax通信でフィルタリング処理が実行され、一覧が再描画される。セッションにフィルタ条件が保存される。

### 04-検索実行

検索フォームにキーワードを入力してEnterまたは検索ボタンを押下すると、キャンペーン名でのテキスト検索が実行される。

### 05-CSV/XLSエクスポート

エクスポートリンクをクリックすると、現在の検索・フィルタ条件に基づいたデータがCSVまたはXLS形式でダウンロードされる。

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

ページリンクをクリックすると、指定ページのデータが読み込まれ一覧が更新される。

### 07-表示件数変更

表示件数セレクトボックスを変更すると、ユーザー設定（pref）に保存され、1ページ目から再描画される。

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 一覧表示 | campaigns | SELECT | 条件に応じたキャンペーン取得 |
| フィルタリング | - | - | セッションへの保存のみ |
| 削除 | campaigns | UPDATE | deleted_at への論理削除 |
| 削除 | versions | INSERT | 削除履歴の記録 |

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

#### campaigns

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | user_id または permissions で認可 | my(current_user)スコープ適用 |
| UPDATE (削除時) | deleted_at | 現在日時 | 論理削除 |

## メッセージ仕様

| メッセージ種別 | メッセージコード | 表示内容 | 表示条件 |
|--------------|----------------|---------|---------|
| 成功 | msg_asset_deleted | "#{name} has been deleted." | 削除成功時 |
| 情報 | - | "No campaigns found." | 検索結果0件時 |

## 例外処理

| 例外ケース | 処理内容 | 表示 |
|-----------|---------|------|
| 認証エラー | ログイン画面にリダイレクト | Deviseのデフォルトメッセージ |
| 権限エラー | 403エラー | アクセス権限がありません |
| データなし | 空メッセージ表示 | shared/_empty パーシャル |

## 備考

- キャンペーンステータスは `Setting.campaign_status` で設定される
- ソート順は `Setting.unroll(:campaign_status)` で定義
- paper_trail によりバージョン履歴が記録される
- acts_as_taggable_on によりタグ付けが可能

---

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

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

### 推奨読解順序

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

まず、キャンペーンのデータモデルを理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | campaign.rb | `app/models/entities/campaign.rb` | Campaignモデルの定義、バリデーション、スコープ、関連を確認 |

**読解のコツ**: Railsのモデル定義では、`belongs_to`, `has_many` で関連を、`scope` で検索条件を、`validates` でバリデーションを確認する。`uses_user_permissions` は権限管理用のミックスインである。

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | campaigns_controller.rb | `app/controllers/entities/campaigns_controller.rb` | indexアクションの処理フロー |
| 2-2 | entities_controller.rb | `app/controllers/entities_controller.rb` | 親クラスの共通処理 |

**主要処理フロー**:
1. **13-20行目**: `index`アクションでキャンペーン一覧を取得
2. **138-178行目（entities_controller.rb）**: `get_list_of_records`で検索・フィルタ・ページネーション処理
3. **154-160行目（campaigns_controller.rb）**: `filter`アクションでステータスフィルタリング

#### Step 3: ルーティングを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | routes.rb | `config/routes.rb` | campaignsリソースのルート定義（64-82行目） |

**主要処理フロー**:
- **64行目**: `resources :campaigns` でRESTfulルート生成
- **65-72行目**: collectionルート（advanced_search, filter, options等）
- **74-81行目**: memberルート（attach, discard, subscribe等）

#### Step 4: ビューを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | index.html.haml | `app/views/campaigns/index.html.haml` | 一覧画面のメインテンプレート |
| 4-2 | _campaign.html.haml | `app/views/campaigns/_campaign.html.haml` | 各キャンペーン行のパーシャル |
| 4-3 | _sidebar_index.html.haml | `app/views/campaigns/_sidebar_index.html.haml` | サイドバー（フィルタ）のパーシャル |
| 4-4 | _title_bar.html.haml | `app/views/entities/_title_bar.html.haml` | タイトルバーと新規作成ボタン |

**主要処理フロー**:
- **1行目（index.html.haml）**: スタイル読み込み
- **3行目**: タイトルバーのレンダリング
- **11-15行目**: キャンペーン一覧のイテレーション
- **17-18行目**: ページネーションとエクスポート

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

```
[HTTP Request: GET /campaigns]
    │
    ├─ routes.rb (64行目)
    │      └─ CampaignsController#index
    │
    ├─ CampaignsController#index (13-20行目)
    │      ├─ get_campaigns (= get_list_of_records)
    │      │      ├─ ransack_search.result
    │      │      ├─ scope.state(filter) [フィルタ適用]
    │      │      ├─ scope.text_search(query) [テキスト検索]
    │      │      └─ scope.paginate [ページネーション]
    │      │
    │      └─ respond_with @campaigns
    │             ├─ format.html [campaigns/index.html.haml]
    │             ├─ format.xls [header layout + XLS]
    │             └─ format.csv [CSV出力]
    │
    └─ campaigns/index.html.haml
           ├─ entities/_title_bar.html.haml
           ├─ campaigns/_campaign.html.haml (collection)
           ├─ campaigns/_sidebar_index.html.haml
           ├─ shared/_paginate_with_per_page.html.haml
           └─ shared/_export.html.haml
```

### データフロー図

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

HTTPリクエスト ───▶ ルーティング ───▶ campaigns/index.html.haml
   │                    │
   │                    ▼
   │             CampaignsController
   │                    │
   ├─ query     ───▶ text_search (Ransack)
   ├─ status    ───▶ state scope (フィルタ)
   └─ page      ───▶ paginate (will_paginate)
                       │
                       ▼
                campaigns テーブル ───▶ @campaigns
                       │
                       ▼
                ビューレンダリング ───▶ HTML/CSV/XLS
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| campaign.rb | `app/models/entities/campaign.rb` | モデル | キャンペーンのデータモデル定義 |
| campaigns_controller.rb | `app/controllers/entities/campaigns_controller.rb` | コントローラー | キャンペーン関連のアクション処理 |
| entities_controller.rb | `app/controllers/entities_controller.rb` | コントローラー | エンティティ共通の基底クラス |
| index.html.haml | `app/views/campaigns/index.html.haml` | ビュー | 一覧画面のメインテンプレート |
| _campaign.html.haml | `app/views/campaigns/_campaign.html.haml` | ビュー | 一覧の各行パーシャル |
| _sidebar_index.html.haml | `app/views/campaigns/_sidebar_index.html.haml` | ビュー | サイドバーフィルタ |
| _title_bar.html.haml | `app/views/entities/_title_bar.html.haml` | ビュー | タイトルバー共通パーシャル |
| routes.rb | `config/routes.rb` | 設定 | URLルーティング定義 |
