# 機能設計書：79-Stats Dashboard

## 1. 概要

### 1.1 機能の目的
Stats Dashboard（@tryghost/stats）は、Ghostサイト全体の分析ダッシュボードを提供するReactベースのマイクロフロントエンドアプリケーションです。サイト全体のWebトラフィック、会員成長、ニュースレターパフォーマンスなどの包括的な分析機能を提供します。

### 1.2 機能の範囲
- **Overview**: サイト全体のサマリー（訪問者数、会員数、MRR、最新記事、人気記事）
- **Web分析**: サイト全体のKPI、トップコンテンツ、流入元、地域別統計
- **Growth分析**: 会員成長推移、無料/有料会員、MRR推移、獲得元
- **Newsletters分析**: ニュースレター別パフォーマンス、開封率、クリック率

### 1.3 関連画面
| 画面No | 画面名 | 関連度 |
|--------|--------|--------|
| 111-120 | サイト分析画面 | 高 |

## 2. 機能種別
- 種別：管理画面（マイクロフロントエンド）
- 分類：管理画面
- カテゴリ：分析・レポーティング

## 3. 入力仕様

### 3.1 日付範囲オプション
| キー | 値 | 説明 |
|------|-----|------|
| today | 1 | 今日 |
| last7days | 7 | 過去7日間 |
| last30days | 30 | 過去30日間（デフォルト） |
| last3months | 90 | 過去3ヶ月 |
| last12months | 365 | 過去12ヶ月 |
| allTime | -1 | 全期間 |

### 3.2 ニュースレター選択
| パラメータ | 型 | 説明 |
|-----------|-----|------|
| selectedNewsletterId | string | 選択されたニュースレターID |

## 4. 出力仕様

### 4.1 レンダリング出力
- コンテナクラス: `.shade-stats`, `.app-container`
- ShadeApp（新デザインシステム）使用

### 4.2 KPI指標
| 指標 | 説明 | タブ |
|------|------|------|
| visits | 訪問者数 | Overview, Web |
| pageviews | ページビュー数 | Web |
| bounce_rate | 直帰率 | Web |
| visit_duration | 滞在時間 | Web |
| members | 総会員数 | Overview, Growth |
| free | 無料会員数 | Growth |
| paid | 有料会員数 | Growth |
| mrr | 月間経常収益 | Overview, Growth |
| opened_rate | 開封率 | Newsletters |
| clicked_rate | クリック率 | Newsletters |

## 5. 処理フロー

### 5.1 アプリケーション初期化フロー
```
1. index.tsx
   └─ App as AdminXApp エクスポート

2. App (app.tsx:9-32)
   ├─ FrameworkProvider
   │   ├─ staleTime: 0 (常にstale)
   │   ├─ refetchOnMount: true
   │   └─ refetchOnWindowFocus: false
   ├─ AppProvider (appSettings)
   ├─ RouterProvider (prefix='/')
   ├─ StatsErrorBoundary
   └─ GlobalDataProvider
       └─ ShadeApp → Outlet

3. routes.tsx
   └─ /analytics
       ├─ '' → Overview
       ├─ 'web' → Web
       ├─ 'growth' → Growth
       └─ 'newsletters' → Newsletters
```

### 5.2 GlobalDataProvider データ取得フロー
```
1. GlobalDataProvider (global-data-provider.tsx)
   ├─ useBrowseSettings(): 設定取得
   ├─ useBrowseSite(): サイト情報取得
   ├─ useBrowseConfig(): Ghost設定取得
   ├─ useTinybirdToken(): 分析トークン取得（statsConfig存在時）
   └─ Context.Provider value:
       ├─ data, site, statsConfig, tinybirdToken
       ├─ isLoading, range, setRange
       ├─ settings
       └─ selectedNewsletterId, setSelectedNewsletterId
```

### 5.3 Overviewタブ データ取得フロー
```
1. Overview (overview.tsx)
   ├─ useGlobalData(): グローバルデータ
   ├─ useGrowthStats(): 会員成長統計
   ├─ useLatestPostStats(): 最新記事統計
   ├─ useTopPostsViews(): 人気記事統計
   └─ useTinybirdQuery('api_kpis'): 訪問者統計

2. データ処理
   ├─ sanitizeChartData(): チャートデータ正規化
   ├─ visitorsChartData: 訪問者チャート
   ├─ membersChartData: 会員チャート
   └─ mrrChartData: MRRチャート

3. UIレンダリング
   ├─ OverviewKPIs: KPI表示
   ├─ LatestPost: 最新記事カード
   ├─ TopPosts: 人気記事リスト
   └─ HelpCard: ヘルプカード
```

## 6. ビジネスルール

### 6.1 表示条件ルール
| ルールID | ルール内容 | 実装箇所 |
|----------|-----------|----------|
| BR-01 | paidMembersEnabled時のみMRRチャート表示 | overview.tsx:156 |
| BR-02 | statsConfig存在時のみTinybirdトークン取得 | global-data-provider.tsx:42 |
| BR-03 | 日付範囲'today'はOverviewで除外 | overview.tsx:200 |

### 6.2 データ集計ルール
```
visits: 期間内の訪問数合計
members: 期間末時点の会員数
mrr: 期間末時点のMRR（セント→ドル変換）
```

## 7. データベース操作仕様

本機能はクライアントサイドアプリのため、Ghost Admin APIおよびTinybird APIを使用します。

### 7.1 Ghost Admin API
| エンドポイント | 用途 |
|---------------|------|
| GET /config/ | 設定取得（stats config含む） |
| GET /site/ | サイト情報取得 |
| GET /settings/ | 設定取得 |
| GET /stats/top-posts-views/ | 人気記事統計 |

### 7.2 Tinybird API
| エンドポイント | 用途 |
|---------------|------|
| api_kpis | サイト全体のKPI時系列データ |
| api_top_sources | トップ流入元 |
| api_top_locations | 地域別統計 |
| api_top_content | トップコンテンツ |

## 8. エラー処理

### 8.1 エラーバウンダリ
```typescript
<StatsErrorBoundary>
    <GlobalDataProvider>
        <ShadeApp>
            <Outlet />
        </ShadeApp>
    </GlobalDataProvider>
</StatsErrorBoundary>
```

### 8.2 APIエラー処理
```typescript
// global-data-provider.tsx:46-59
const ghostError = ghostRequests.map(request => request.error).find(Boolean);
const tinybirdError = hasStatsConfig ? tinybirdTokenQuery.error : null;
const error = ghostError || tinybirdError;

if (error) {
    throw error;
}
```

## 9. トランザクション仕様

本機能は読み取り専用のため、トランザクション管理は不要です。

## 10. パフォーマンス要件

### 10.1 データキャッシュ戦略
```typescript
queryClientOptions: {
    staleTime: 0,           // 常にstale扱い
    refetchOnMount: true,   // マウント時に再取得
    refetchOnWindowFocus: false
}
```

### 10.2 コード分割
- 各タブコンポーネント（Overview, Web, Growth, Newsletters）は`lazyComponent`で遅延ロード

### 10.3 チャートデータ正規化
- `sanitizeChartData`関数で欠落日付を補完
- Y軸範囲を動的計算

## 11. セキュリティ考慮事項

### 11.1 認証
- Ghost Admin APIセッションに依存
- FrameworkProviderで認証状態管理

### 11.2 Tinybird認証
- `useTinybirdToken()`でトークン取得
- statsConfig存在時のみトークン取得

### 11.3 データアクセス制御
- site_uuidでデータ分離
- 自サイトの統計のみアクセス可能

## 12. 備考

### 12.1 技術スタック
- React 18.3.1
- TypeScript
- Vite（ビルドツール）
- @tryghost/admin-x-framework（APIフック）
- @tryghost/shade（デザインシステム）
- @tanstack/react-query（データフェッチ）
- moment / moment-timezone（日付処理）
- i18n-iso-countries（国名表示）
- react-svg-map / @svg-maps/world（世界地図表示）

### 12.2 ルート構成
| パス | コンポーネント | 説明 |
|------|---------------|------|
| /analytics | Overview | 概要タブ |
| /analytics/web | Web | Web分析タブ |
| /analytics/growth | Growth | Growth分析タブ |
| /analytics/newsletters | Newsletters | Newsletters分析タブ |

### 12.3 ヘルプカードリンク
| タイトル | URL |
|---------|-----|
| Understanding analytics in Ghost | https://ghost.org/help/native-analytics |
| How to get your content seen online | https://ghost.org/resources/content-distribution/ |

---

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

### 推奨読解順序

#### 1. エントリーポイント
**ファイル**: `apps/stats/src/index.tsx`

```
index.tsx (1-6行目)
├─ './styles/index.css': スタイル読み込み
└─ App as AdminXApp: エクスポート
```

#### 2. アプリケーション構造
**ファイル**: `apps/stats/src/app.tsx`

```
App (9-32行目)
├─ FrameworkProvider
│   └─ queryClientOptions: {staleTime: 0, refetchOnMount: true, ...}
├─ AppProvider (appSettings)
├─ RouterProvider (prefix=APP_ROUTE_PREFIX)
├─ StatsErrorBoundary
└─ GlobalDataProvider
    └─ ShadeApp (className='shade-stats app-container')
        └─ Outlet
```

#### 3. ルーティング設定
**ファイル**: `apps/stats/src/routes.tsx`

```
routes (10-32行目)
└─ path: 'analytics'
    └─ children:
        ├─ index → Overview
        ├─ 'web' → Web
        ├─ 'growth' → Growth
        └─ 'newsletters' → Newsletters
```

#### 4. グローバルデータプロバイダー
**ファイル**: `apps/stats/src/providers/global-data-provider.tsx`

```
GlobalDataProvider (35-84行目)
├─ useBrowseSettings(): 設定
├─ useBrowseSite(): サイト情報
├─ useBrowseConfig(): 設定（statsConfig）
├─ useTinybirdToken(): 分析トークン
├─ useState(range): 日付範囲
├─ useState(selectedNewsletterId): ニュースレター選択
└─ Context.Provider value:
    ├─ data, site, statsConfig, tinybirdToken
    ├─ isLoading, range, setRange
    ├─ settings
    └─ selectedNewsletterId, setSelectedNewsletterId
```

#### 5. Overviewタブ
**ファイル**: `apps/stats/src/views/Stats/Overview/overview.tsx`

```
Overview (66-246行目)
├─ useAppContext(): アプリ設定
├─ useGlobalData(): グローバルデータ
├─ useGrowthStats(range): 会員成長統計
├─ useLatestPostStats(): 最新記事
├─ useTopPostsViews(): 人気記事
├─ useTinybirdQuery('api_kpis'): 訪問者KPI
├─ visitorsChartData: 訪問者チャートデータ処理
├─ membersChartData: 会員チャートデータ処理
├─ mrrChartData: MRRチャートデータ処理
├─ kpiValues: KPI値計算
└─ JSX:
    ├─ StatsLayout
    ├─ StatsHeader + DateRangeSelect
    ├─ OverviewKPIs
    ├─ LatestPost
    ├─ TopPosts
    └─ HelpCard x 2
```

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

```
[Ghost Admin (Ember)]
    │
    ▼
AdminXComponent.ReactComponent()
    │
    └─ <AdminXApp framework={...} designSystem={...} />
           │
           └─ index.tsx::App
                  │
                  └─ app.tsx::App
                         │
                         ├─ FrameworkProvider (queryClientOptions)
                         │
                         ├─ AppProvider (appSettings)
                         │
                         ├─ RouterProvider
                         │   └─ routes.tsx
                         │       │
                         │       └─ /analytics
                         │           ├─ Overview
                         │           ├─ Web
                         │           ├─ Growth
                         │           └─ Newsletters
                         │
                         └─ StatsErrorBoundary
                             └─ GlobalDataProvider
                                 └─ ShadeApp

[GlobalDataProvider]
    │
    ├─ useBrowseSettings() → Ghost Admin API
    ├─ useBrowseSite() → Ghost Admin API
    ├─ useBrowseConfig() → Ghost Admin API
    └─ useTinybirdToken() → Ghost Admin API

[Overview/Web/Growth/Newsletters]
    │
    └─ useTinybirdQuery() → Tinybird API
        ├─ api_kpis
        ├─ api_top_sources
        ├─ api_top_locations
        └─ api_top_content
```

### データフロー図

```
[Ghost Admin (Ember)]
    │
    ├── framework props
    ├── designSystem props
    ├── appSettings
    │
    ▼
[FrameworkProvider]
    │
    ├── React Query Client
    │
    ▼
[AppProvider]
    │
    ├── appSettings (paidMembersEnabled, etc.)
    │
    ▼
[GlobalDataProvider]
    │
    ├── Ghost Admin API
    │   ├── /config/ → statsConfig
    │   ├── /site/ → siteData
    │   └── /settings/ → settings
    │
    ├── Tinybird Token
    │   └── statsConfig存在時のみ
    │
    ├── range (日付範囲状態)
    ├── selectedNewsletterId (ニュースレター選択状態)
    │
    ▼
[Analysis Views]
    │
    ├── Tinybird API
    │   ├── api_kpis → visits, pageviews, bounce_rate
    │   ├── api_top_sources → source data
    │   └── api_top_locations → location data
    │
    ├── Ghost Admin API
    │   └── /stats/top-posts-views/ → top posts
    │
    ├── useGrowthStats()
    │   └── 会員成長データ
    │
    └── UI Components
        ├── OverviewKPIs (チャート + 数値)
        ├── LatestPost (最新記事)
        ├── TopPosts (人気記事)
        └── HelpCard (ヘルプリンク)
```

### 関連ファイル一覧

| ファイルパス | 役割 | 重要度 |
|-------------|------|--------|
| apps/stats/src/index.tsx | エントリーポイント | 高 |
| apps/stats/src/app.tsx | アプリルート | 高 |
| apps/stats/src/routes.tsx | ルーティング定義 | 高 |
| apps/stats/src/providers/global-data-provider.tsx | グローバルデータプロバイダー | 高 |
| apps/stats/src/views/Stats/Overview/overview.tsx | 概要タブ | 高 |
| apps/stats/src/views/Stats/Web/web.tsx | Web分析タブ | 高 |
| apps/stats/src/views/Stats/Growth/growth.tsx | Growth分析タブ | 中 |
| apps/stats/src/views/Stats/Newsletters/newsletters.tsx | Newsletters分析タブ | 中 |
| apps/stats/src/views/Stats/layout/stats-layout.tsx | 統計レイアウト | 中 |
| apps/stats/src/views/Stats/layout/stats-header.tsx | 統計ヘッダー | 中 |
| apps/stats/src/views/Stats/components/date-range-select.tsx | 日付範囲選択 | 中 |
| apps/stats/src/views/Stats/Overview/components/overview-kpis.tsx | 概要KPI | 中 |
| apps/stats/src/views/Stats/Overview/components/latest-post.tsx | 最新記事カード | 中 |
| apps/stats/src/views/Stats/Overview/components/top-posts.tsx | 人気記事リスト | 中 |
| apps/stats/src/components/errors/stats-error-boundary.tsx | エラーバウンダリ | 中 |
| apps/stats/src/utils/constants.ts | 定数定義 | 低 |
| apps/stats/package.json | 依存関係定義 | 中 |
