# 機能設計書：78-Posts Analytics

## 1. 概要

### 1.1 機能の目的
Posts Analytics（@tryghost/posts）は、Ghostの記事分析機能を提供するReactベースのマイクロフロントエンドアプリケーションです。個別記事のWebトラフィック、ニュースレター開封率、会員獲得状況などの詳細な分析ダッシュボードを提供します。

### 1.2 機能の範囲
- **Overview**: 記事の全体サマリー（Web + Newsletter + Growth）
- **Web分析**: 訪問者数、トップソース、地域別統計
- **Growth分析**: 無料/有料会員獲得数、MRRインパクト
- **Newsletter分析**: 開封率、クリック率、フィードバック
- **タグ管理**: タグ一覧表示
- **コメント管理**: コメント一覧表示

### 1.3 関連画面
| 画面No | 画面名 | 関連度 |
|--------|--------|--------|
| 104-110 | 記事分析画面 | 高 |

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

## 3. 入力仕様

### 3.1 ルートパラメータ
| パラメータ | 型 | 必須 | 説明 |
|-----------|-----|------|------|
| postId | string | Yes | 分析対象の記事ID |

### 3.2 フィルタパラメータ
| フィールド | 型 | 説明 |
|-----------|-----|------|
| audience | string[] | オーディエンスフィルタ（all/free/paid） |
| source | string | 流入元フィルタ |
| location | string | 地域フィルタ |

### 3.3 日付範囲
| 定数 | 値 | 説明 |
|------|-----|------|
| LAST_7_DAYS | 7 | 過去7日間 |
| LAST_30_DAYS | 30 | 過去30日間 |
| ALL_TIME | -1 | 全期間 |

## 4. 出力仕様

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

### 4.2 データ表示
| 指標 | 説明 |
|------|------|
| visits | 訪問者数 |
| pageviews | ページビュー数 |
| bounce_rate | 直帰率 |
| visit_duration | 滞在時間 |
| free_members | 無料会員獲得数 |
| paid_members | 有料会員獲得数 |
| mrr | MRRインパクト |
| opened_count | メール開封数 |
| email_count | メール送信数 |

## 5. 処理フロー

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

2. App (app.tsx:12-41)
   ├─ FrameworkProvider
   │   ├─ staleTime: 0 (常にstale)
   │   ├─ refetchOnMount: true
   │   └─ refetchOnWindowFocus: false
   ├─ PostsAppContextProvider
   ├─ RouterProvider (prefix='/')
   ├─ PostsErrorBoundary
   └─ ShadeApp → Outlet

3. routes.tsx
   └─ posts/analytics/:postId → PostAnalyticsProvider + PostAnalytics
```

### 5.2 Post Analytics データ取得フロー
```
1. PostAnalyticsProvider (post-analytics-context.tsx)
   ├─ useBrowseConfig(): Ghost設定取得
   ├─ useBrowseSite(): サイト情報取得
   ├─ useBrowseSettings(): 設定取得
   ├─ useTinybirdToken(): 分析トークン取得
   └─ useBrowsePosts(): 記事データ取得
       └─ include: email, authors, tags, tiers, count.*, newsletter

2. 分析データ取得（Overview/Web/Growth/Newsletter）
   └─ useTinybirdQuery()
       ├─ api_kpis: KPI時系列データ
       ├─ api_top_sources: トップソース
       └─ api_top_locations: 地域別データ
```

### 5.3 Webタブ フィルタフロー
```
1. URLパラメータからフィルタ取得
   └─ useFilterParams()

2. フィルタ変更
   └─ setAnalyticsFilters()
       └─ URL更新（ブックマーク/共有可能）

3. Tinybird APIクエリ
   └─ params: site_uuid, date_from, date_to, timezone, member_status, post_uuid, ...filterParams
```

## 6. ビジネスルール

### 6.1 表示条件ルール
| ルールID | ルール内容 | 実装箇所 |
|----------|-----------|----------|
| BR-01 | email_only記事はWebタブにアクセス不可 | Web/web.tsx:42-46 |
| BR-02 | メール配信記事のみNewsletterセクション表示 | Overview/overview.tsx:95 |
| BR-03 | Web分析無効時はGrowthタブへリダイレクト | Overview/overview.tsx:99-103 |
| BR-04 | hasBeenEmailed + emailTrackOpens/Clicks有効時のみ | Overview/overview.tsx:95 |

### 6.2 チャート範囲計算
```
chartRange = getRangeForStartDate(post.published_at)
└─ 記事公開日から今日までの日数に基づいて最適な範囲を決定
```

### 6.3 地域データ処理
- Unknown地域（'', 'XX', 'null'等）を単一の「Unknown」エントリに集約
- 既知地域を先に表示、Unknown を最後に表示

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

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

### 7.1 Ghost Admin API
| エンドポイント | 用途 |
|---------------|------|
| GET /posts/ | 記事データ取得（email, authors, tags等include） |
| GET /config/ | 設定取得（stats config含む） |
| GET /site/ | サイト情報取得 |
| GET /settings/ | 設定取得 |

### 7.2 Tinybird API
| エンドポイント | 用途 |
|---------------|------|
| api_kpis | KPI時系列データ（visits, pageviews, bounce_rate, visit_duration） |
| api_top_sources | トップ流入元 |
| api_top_locations | 地域別統計 |

## 8. エラー処理

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

### 8.2 必須パラメータ検証
```typescript
if (!postId) {
    throw new Error('Post ID is required for PostAnalyticsProvider');
}
```

### 8.3 APIエラー処理
- Ghost APIエラーとTinybird APIエラーを統合検出
- エラー発生時は例外をスロー

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

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

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

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

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

### 10.3 仮想スクロール
- `@tanstack/react-virtual`による大規模リスト最適化

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

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

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

### 11.3 データアクセス制御
- 記事IDに基づくアクセス（自サイトの記事のみ）
- site_uuidでデータ分離

## 12. 備考

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

### 12.2 ルート構成
| パス | コンポーネント | 説明 |
|------|---------------|------|
| posts/analytics/:postId | Overview | 概要タブ |
| posts/analytics/:postId/web | Web | Web分析タブ |
| posts/analytics/:postId/growth | Growth | Growth分析タブ |
| posts/analytics/:postId/newsletter | Newsletter | Newsletter分析タブ |
| tags | Tags | タグ一覧 |
| comments | Comments | コメント一覧 |

### 12.3 分析データ型
```typescript
interface Post {
    published_at?: string;
    excerpt?: string;
    authors?: { name: string }[];
    email?: {
        opened_count: number;
        email_count: number;
        status?: string;
    };
    newsletter?: { feedback_enabled?: boolean };
    count?: {
        positive_feedback?: number;
        negative_feedback?: number;
        clicks?: number;
        signups?: number;
        paid_conversions?: number;
    };
}
```

---

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

### 推奨読解順序

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

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

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

```
App (12-41行目)
├─ FrameworkProvider
│   └─ queryClientOptions: {staleTime: 0, refetchOnMount: true, ...}
├─ PostsAppContextProvider
│   └─ appSettings, externalNavigate, fromAnalytics
├─ RouterProvider (prefix=APP_ROUTE_PREFIX)
├─ PostsErrorBoundary
└─ ShadeApp (className='shade-posts app-container')
    └─ Outlet
```

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

```
routes (11-80行目)
├─ posts/analytics/:postId
│   ├─ PostAnalyticsProvider + PostAnalytics
│   └─ children:
│       ├─ '' → Overview
│       ├─ 'web' → Web
│       ├─ 'growth' → Growth
│       └─ 'newsletter' → Newsletter
├─ tags → Tags
└─ comments → Comments
```

#### 4. Post Analytics コンテキスト
**ファイル**: `apps/posts/src/providers/post-analytics-context.tsx`

```
PostAnalyticsProvider (64-127行目)
├─ useParams(): postId取得
├─ useBrowseConfig(): 設定取得
├─ useBrowseSite(): サイト情報
├─ useBrowseSettings(): 設定
├─ useTinybirdToken(): 分析トークン
├─ useBrowsePosts(): 記事データ
│   └─ include: email,authors,tags,tiers,count.*,newsletter
└─ Context.Provider value:
    ├─ data, site, statsConfig, tinybirdToken
    ├─ isLoading, range, setRange
    ├─ settings, postId, post, isPostLoading
```

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

```
Overview (17-194行目)
├─ useGlobalData(): コンテキストデータ
├─ usePostReferrers(): リファラーデータ
├─ chartRange: 公開日ベースの範囲計算
├─ useTinybirdQuery('api_kpis'): KPIデータ
├─ useTinybirdQuery('api_top_sources'): ソースデータ
├─ showWebSection / showNewsletterSection: 表示制御
├─ WebOverview: Web統計表示
├─ NewsletterOverview: Newsletter統計表示
└─ Growthカード: 会員獲得統計
```

#### 6. Webタブ
**ファイル**: `apps/posts/src/views/PostAnalytics/Web/web.tsx`

```
Web (27-270行目)
├─ useFilterParams(): URLフィルタ同期
├─ audience / filterParams: フィルタ処理
├─ handleFilterClick(): フィルタ適用
├─ useTinybirdQuery('api_kpis'): KPIデータ
├─ useTinybirdQuery('api_top_locations'): 地域データ
├─ useTinybirdQuery('api_top_sources'): ソースデータ
├─ processedLocationsData: Unknown集約処理
├─ Kpis: KPI表示
├─ Locations: 地域表示
└─ Sources: ソース表示
```

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

```
[Ghost Admin (Ember)]
    │
    ▼
AdminXComponent.ReactComponent()
    │
    └─ <AdminXApp framework={...} designSystem={...} />
           │
           └─ index.tsx::App
                  │
                  └─ app.tsx::App
                         │
                         ├─ FrameworkProvider (queryClientOptions)
                         │
                         ├─ PostsAppContextProvider
                         │
                         ├─ RouterProvider
                         │   └─ routes.tsx
                         │       │
                         │       ├─ /posts/analytics/:postId
                         │       │   └─ PostAnalyticsProvider
                         │       │       └─ PostAnalytics
                         │       │           └─ Outlet → Overview/Web/Growth/Newsletter
                         │       │
                         │       ├─ /tags → Tags
                         │       └─ /comments → Comments
                         │
                         └─ PostsErrorBoundary
                             └─ ShadeApp

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

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

### データフロー図

```
[Ghost Admin (Ember)]
    │
    ├── framework props
    ├── designSystem props
    │
    ▼
[FrameworkProvider]
    │
    ├── React Query Client
    │   └── queryClientOptions
    │
    ▼
[PostsAppContextProvider]
    │
    ├── appSettings (analytics設定)
    │
    ▼
[PostAnalyticsProvider]
    │
    ├── Ghost Admin API
    │   ├── /config/ → statsConfig
    │   ├── /site/ → siteData
    │   ├── /settings/ → settings
    │   └── /posts/ → post data
    │
    ├── Tinybird Token
    │   └── statsConfig存在時のみ
    │
    ▼
[Analysis Views]
    │
    ├── Tinybird API
    │   ├── api_kpis → visits, pageviews, bounce_rate, visit_duration
    │   ├── api_top_sources → source data
    │   └── api_top_locations → location data
    │
    ├── URL Filter State
    │   └── useFilterParams() ⟷ URL parameters
    │
    └── UI Components
        ├── Kpis (チャート + 数値)
        ├── Locations (地域別)
        └── Sources (流入元)
```

### 関連ファイル一覧

| ファイルパス | 役割 | 重要度 |
|-------------|------|--------|
| apps/posts/src/index.tsx | エントリーポイント | 高 |
| apps/posts/src/app.tsx | アプリルート | 高 |
| apps/posts/src/routes.tsx | ルーティング定義 | 高 |
| apps/posts/src/providers/post-analytics-context.tsx | 分析コンテキスト | 高 |
| apps/posts/src/providers/posts-app-context.tsx | アプリコンテキスト | 中 |
| apps/posts/src/views/PostAnalytics/post-analytics.tsx | 分析メインコンポーネント | 高 |
| apps/posts/src/views/PostAnalytics/Overview/overview.tsx | 概要タブ | 高 |
| apps/posts/src/views/PostAnalytics/Web/web.tsx | Web分析タブ | 高 |
| apps/posts/src/views/PostAnalytics/Growth/growth.tsx | Growth分析タブ | 中 |
| apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx | Newsletter分析タブ | 中 |
| apps/posts/src/views/PostAnalytics/components/kpi-card.tsx | KPIカードコンポーネント | 中 |
| apps/posts/src/views/PostAnalytics/Web/components/kpis.tsx | KPIチャート | 中 |
| apps/posts/src/views/PostAnalytics/Web/components/sources.tsx | ソース表示 | 中 |
| apps/posts/src/views/PostAnalytics/Web/components/locations.tsx | 地域表示 | 中 |
| apps/posts/src/hooks/use-filter-params.ts | フィルタフック | 中 |
| apps/posts/src/utils/constants.ts | 定数定義 | 低 |
| apps/posts/package.json | 依存関係定義 | 中 |
