# 画面設計書 25-ニュースレター分析画面

## 概要

Ghost管理画面の統計機能におけるニュースレター分析画面の設計書である。ニュースレターの購読者数推移、開封率、クリック率などのメール配信パフォーマンスを分析し、トップパフォーマンスのニュースレター一覧を表示する。

### 本画面の処理概要

ニュースレター分析画面は、メール配信の効果を詳細に分析するための専門画面である。

**業務上の目的・背景**：サイト運営者がニュースレター配信の効果を把握し、メールマーケティング戦略を最適化するために使用する。購読者数の推移、平均開封率、平均クリック率を追跡し、どのニュースレターがエンゲージメントを獲得しているかを特定する。複数のニュースレターを運営している場合は、個別のニュースレターごとに分析が可能。

**画面へのアクセス方法**：統計画面のナビゲーションから「Newsletters」タブをクリックするか、直接 `/analytics/newsletters` にアクセスする。ニュースレター機能（newslettersEnabled）が無効の場合は、概要画面にリダイレクトされる。

**主要な操作・処理内容**：
1. ニュースレターセレクターで分析対象のニュースレターを選択
2. 日付範囲セレクターで分析期間を変更
3. Total subscribers / Opens / Clicksの各タブでKPIチャートを切り替え
4. トップニュースレター一覧のソート（Date / Sent / Opens / Clicks）
5. 個別ニュースレター（投稿）をクリックして投稿分析画面へ遷移

**画面遷移**：
- 遷移元：統計概要画面（/analytics）、タブナビゲーション
- 遷移先：投稿分析画面（/posts/analytics/:postId）

**権限による表示制御**：ニュースレター機能が無効の場合、本画面は表示されず概要画面にリダイレクトされる。メール開封追跡（emailTrackOpens）、クリック追跡（emailTrackClicks）の設定によりOpens/Clicksカラムの表示が制御される。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 22 | メール分析 | 主機能 | ニュースレターの開封率・クリック率分析 |
| 20 | ニュースレター管理 | 補助機能 | ニュースレター一覧の取得 |

## 画面種別

ダッシュボード / 分析

## URL/ルーティング

| パス | コンポーネント | 説明 |
|------|---------------|------|
| `/analytics/newsletters` | Newsletters | ニュースレター分析画面 |

## 入出力項目

### 入力項目

| 項目名 | 項目ID | データ型 | 必須 | 説明 |
|--------|--------|----------|------|------|
| 日付範囲 | range | number | Yes | 分析期間 |
| ニュースレターID | selectedNewsletterId | string | No | 選択されたニュースレター |
| KPIタブ | tab | string | No | URLパラメータ：total-subscribers/opens/clicks |
| ソート順 | sortBy | string | No | date desc/sent_to desc/open_rate desc/click_rate desc |

### 出力項目

| 項目名 | データ型 | 説明 |
|--------|----------|------|
| 購読者数 | number | ニュースレター購読者の総数 |
| 平均開封率 | number | 期間内の平均開封率 |
| 平均クリック率 | number | 期間内の平均クリック率 |
| 購読者チャートデータ | array | 日別購読者数の時系列データ |
| トップニュースレター | array | パフォーマンス上位のニュースレター一覧 |

## 表示項目

### KPIタブ

| タブ名 | 値 | 説明 | 表示条件 |
|--------|---|------|---------|
| Total subscribers | total-subscribers | 購読者数推移 | 常時表示 |
| Opens | opens | 開封率推移 | emailTrackOpens有効時 |
| Clicks | clicks | クリック率推移 | emailTrackClicks有効時 |

### トップニュースレターテーブル

| 項目名 | データ型 | 説明 | 表示条件 |
|--------|----------|------|---------|
| post_title | string | 投稿タイトル | 常時表示 |
| send_date | date | 送信日時 | 常時表示 |
| sent_to | number | 送信数 | 常時表示 |
| open_rate | number | 開封率 | emailTrackOpens有効時 |
| click_rate | number | クリック率 | emailTrackClicks有効時 |

## イベント仕様

### 1-ニュースレター選択

**トリガー**: NewsletterSelectで対象ニュースレターを選択

**処理フロー**:
1. GlobalDataContext経由でselectedNewsletterIdが更新
2. useSubscriberCountWithRangeが新しいnewsletter_idで再実行
3. useNewsletterStatsWithRangeSplitが再実行
4. KPI・テーブルが選択されたニュースレターのデータで再描画

### 2-日付範囲変更

**トリガー**: DateRangeSelectで期間を選択

**処理フロー**:
1. GlobalDataContext経由でrangeが更新
2. 各データ取得フックが再実行
3. チャート・テーブルが再描画

### 3-KPIタブ切り替え

**トリガー**: Total subscribers/Opens/Clicksタブをクリック

**処理フロー**:
1. NewsletterKPIsコンポーネント内でタブ状態が変更
2. チャートに表示するデータ系列が切り替わる
3. URLのtabパラメータが更新される

### 4-ソート変更

**トリガー**: Date/Sent/Opens/Clicksのヘッダーをクリック

**処理フロー**:
1. setSortBy関数でソート順を更新
2. useNewsletterStatsWithRangeSplitのsortByパラメータが変更
3. APIから新しいソート順でデータ取得
4. テーブルが再描画

**関連コード**: `apps/stats/src/views/Stats/Newsletters/newsletters.tsx` 166-191行目

### 5-投稿クリック

**トリガー**: テーブル内の投稿タイトルをクリック

**処理フロー**:
1. navigate関数で `/posts/analytics/:postId` に遷移（crossApp: true）

**関連コード**: `apps/stats/src/views/Stats/Newsletters/newsletters.tsx` 79-81行目

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面表示 | newsletters | SELECT | ニュースレター一覧取得 |
| 画面表示 | emails | SELECT | ニュースレター統計取得 |
| 画面表示 | members_subscription_stats | SELECT | 購読者数推移取得 |

### API呼び出し詳細

#### Ghost API

| エンドポイント | パラメータ | 用途 |
|---------------|-----------|------|
| /ghost/api/admin/newsletters/ | limit:50 | ニュースレター一覧取得 |
| /ghost/api/admin/stats/subscriptions/ | newsletter_id, range | 購読者数推移取得 |
| /ghost/api/admin/stats/emails/ | newsletter_id, range, order_by | ニュースレター統計取得 |
| /ghost/api/admin/links/ | filter:post_id | クリックデータ取得 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|---------|
| - | 情報 | No newsletters {period} | データがない場合 |
| - | 情報 | Your best performing newsletters {period} | テーブル説明文 |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| newslettersEnabled無効 | /analyticsにリダイレクト |
| ニュースレターなし | 空の選択状態を維持 |
| データなし | EmptyIndicator表示 |
| API取得失敗 | StatsErrorBoundaryでエラー表示 |

## 備考

- 複数のニュースレターがある場合、最初のニュースレターが自動選択される
- 購読者数はnewsletter.count.active_membersから取得可能
- クリックデータは分割ロード（isClicksLoading）で遅延取得される
- ホバー時に開封数/クリック数の実数が表示される（rate -> count切り替え）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | newsletters.tsx | `apps/stats/src/views/Stats/Newsletters/newsletters.tsx` | AvgsDataItem型定義（17-27行目） |
| 1-2 | use-newsletter-stats-with-range.ts | `apps/stats/src/hooks/use-newsletter-stats-with-range.ts` | TopNewslettersOrder型、戻り値型 |

**読解のコツ**: AvgsDataItemがニュースレター統計の1レコードを表す。open_rate/click_rateは0-1の小数。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | newsletters.tsx | `apps/stats/src/views/Stats/Newsletters/newsletters.tsx` | Newslettersコンポーネント（225-407行目） |
| 2-2 | routes.tsx | `apps/stats/src/routes.tsx` | /newslettersルート定義（26-29行目） |

**主要処理フロー**:
1. **226-228行目**: useGlobalDataでrange/selectedNewsletterId取得
2. **231行目**: initialTabをURLパラメータから取得
3. **234-238行目**: useBrowseNewslettersでニュースレター一覧取得
4. **244行目**: shouldFetchStatsでデータ取得条件判定
5. **247-251行目**: useSubscriberCountWithRangeで購読者推移取得
6. **255-260行目**: useNewsletterStatsWithRangeSplitで統計取得
7. **271-295行目**: totalsでKPI値計算（購読者数、平均開封率、平均クリック率）
8. **298-340行目**: subscribersDataでチャートデータ作成
9. **343-358行目**: avgsDataでバーチャート用データ作成

#### Step 3: テーブルコンポーネントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | NewsletterTableRows | `apps/stats/src/views/Stats/Newsletters/newsletters.tsx` | テーブル行コンポーネント（30-139行目） |
| 3-2 | NewsletterTableHeader | `apps/stats/src/views/Stats/Newsletters/newsletters.tsx` | ヘッダーコンポーネント（144-193行目） |
| 3-3 | TopNewslettersTable | `apps/stats/src/views/Stats/Newsletters/newsletters.tsx` | テーブル全体（197-222行目） |

**読解のコツ**: React.memoで最適化されている。isClicksLoadingはクリックデータの遅延ローディング状態。

#### Step 4: KPIコンポーネントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | newsletters-kpis.tsx | `apps/stats/src/views/Stats/Newsletters/components/newsletters-kpis.tsx` | KPIカード・チャート表示 |

#### Step 5: カスタムフックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | use-newsletter-stats-with-range.ts | `apps/stats/src/hooks/use-newsletter-stats-with-range.ts` | ニュースレター統計取得（useNewsletterStatsWithRangeSplit, useSubscriberCountWithRange） |

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

```
Newsletters (newsletters.tsx)
    │
    ├─ useGlobalData()
    │      └─ range, selectedNewsletterId取得
    │
    ├─ useBrowseNewsletters({limit: 50})
    │      └─ ニュースレター一覧取得
    │
    ├─ useSubscriberCountWithRange(range, newsletter_id, enabled)
    │      └─ 購読者数推移取得
    │
    ├─ useNewsletterStatsWithRangeSplit(range, sortBy, newsletter_id, enabled)
    │      └─ ニュースレター統計取得（stats, isClicksLoading）
    │
    ├─ useMemo: totals計算
    │      └─ 購読者数、平均開封率、平均クリック率
    │
    ├─ useMemo: subscribersData作成
    │      └─ チャート用時系列データ
    │
    ├─ useMemo: avgsData作成
    │      └─ バーチャート用データ
    │
    ├─ NewsletterKPIs
    │      └─ Total subscribers/Opens/Clicksチャート表示
    │
    └─ TopNewslettersTable
           ├─ NewsletterTableHeader（ソートボタン）
           └─ NewsletterTableRows（データ行）
```

### データフロー図

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

selectedNewsletterId ────▶ useSubscriberCountWithRange ───▶ subscriberStatsData
range                            │
                                 ▼
                        useMemo: subscribersData
                                 │
                                 ▼
                        NewsletterKPIs -> Area Chart

range + sortBy + ────────▶ useNewsletterStatsWithRangeSplit ──▶ newsletterStatsData
selectedNewsletterId              │
                                  ├─ stats（開封率等）
                                  └─ isClicksLoading（クリック遅延）
                                          │
                                          ▼
                                  TopNewslettersTable -> Table
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| newsletters.tsx | `apps/stats/src/views/Stats/Newsletters/newsletters.tsx` | ソース | ニュースレター分析画面メイン |
| newsletters-kpis.tsx | `apps/stats/src/views/Stats/Newsletters/components/newsletters-kpis.tsx` | ソース | KPIカード・チャート |
| use-newsletter-stats-with-range.ts | `apps/stats/src/hooks/use-newsletter-stats-with-range.ts` | ソース | ニュースレター統計フック |
| newsletter-select.tsx | `apps/stats/src/views/Stats/components/newsletter-select.tsx` | ソース | ニュースレターセレクター |
| sort-button.tsx | `apps/stats/src/views/Stats/components/sort-button.tsx` | ソース | ソートボタン |
| date-range-select.tsx | `apps/stats/src/views/Stats/components/date-range-select.tsx` | ソース | 日付範囲選択 |
| global-data-provider.tsx | `apps/stats/src/providers/global-data-provider.tsx` | ソース | グローバルデータ管理 |
