# 画面設計書 28-投稿Webトラフィック画面

## 概要

投稿分析画面のWebタブにおける詳細トラフィック分析画面の設計書。特定の投稿に対するWebトラフィック（訪問者数、ページビュー、直帰率、滞在時間）を詳細に分析し、ソース別・地域別のアクセス状況を可視化する。

### 本画面の処理概要

投稿Webトラフィック画面は、個別投稿へのWebアクセスを多角的に分析するための専門画面である。

**業務上の目的・背景**：コンテンツ制作者が特定の投稿がどのようにWeb上で閲覧されているかを詳細に把握するために使用する。訪問者がどこから来ているか（ソース）、どの地域からアクセスが多いか（ロケーション）を分析することで、コンテンツの配信戦略やターゲティングの改善に役立てる。

**画面へのアクセス方法**：投稿分析画面内の「Web」タブをクリック、または直接 `/posts/analytics/:postId/web` にアクセスする。email_only投稿の場合は概要画面にリダイレクトされる。

**主要な操作・処理内容**：
1. 日付範囲の選択による分析期間の変更
2. KPIチャート（訪問者数/ページビュー/直帰率/滞在時間）の切り替え表示
3. フィルタリング機能によるデータ絞り込み（オーディエンス、ソース、ロケーション）
4. ソースクリックによるフィルタリング
5. ロケーションクリックによるフィルタリング

**画面遷移**：
- 遷移元：投稿分析概要画面（/posts/analytics/:postId）
- 遷移先：フィルタ適用後の同画面

**権限による表示制御**：email_only投稿の場合、本画面は表示されず概要画面にリダイレクトされる。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 52 | 記事分析 | 主機能 | 投稿へのWebトラフィック詳細分析 |
| 54 | リンクトラッキング | 補助機能 | リンククリックの追跡 |
| 55 | Tinybird連携 | 補助機能 | Tinybirdからのアナリティクスデータ取得 |

## 画面種別

ダッシュボード / 分析

## URL/ルーティング

| パス | コンポーネント | 説明 |
|------|---------------|------|
| `/posts/analytics/:postId/web` | Web | 投稿Webトラフィック画面 |

## 入出力項目

### 入力項目

| 項目名 | 項目ID | データ型 | 必須 | 説明 |
|--------|--------|----------|------|------|
| 投稿ID | postId | string | Yes | URLパラメータ |
| 日付範囲 | range | number | No | 分析期間 |
| オーディエンス | audience | string | No | フィルタ：all/free/paid |
| ソース | source | string | No | フィルタ：参照元ドメイン |
| ロケーション | location | string | No | フィルタ：国/地域名 |

### 出力項目

| 項目名 | データ型 | 説明 |
|--------|----------|------|
| 訪問者数 | number | 期間内の訪問者数 |
| ページビュー数 | number | 期間内のページビュー数 |
| 直帰率 | number | 1ページのみ閲覧の割合 |
| 平均滞在時間 | number | 訪問あたりの平均秒数 |
| KPIチャートデータ | array | 日別KPIの時系列データ |
| ソース一覧 | array | 参照元上位一覧 |
| ロケーション一覧 | array | 地域別上位一覧 |

## 表示項目

### KPIセクション

| メトリクス | dataKey | ラベル | フォーマッター |
|-----------|---------|--------|--------------|
| visits | visits | Visitors | formatNumber |
| views | pageviews | Pageviews | formatNumber |
| bounce-rate | bounce_rate | Bounce rate | formatPercentage |
| visit-duration | avg_session_sec | Visit duration | formatDuration |

### ソースセクション

| 項目名 | データ型 | 説明 |
|--------|----------|------|
| source | string | 参照元ドメイン |
| visits | number | 訪問者数 |
| percentage | number | 全体に対する割合 |

### ロケーションセクション

| 項目名 | データ型 | 説明 |
|--------|----------|------|
| location | string | 国/地域名 |
| visits | number | 訪問者数 |
| percentage | number | 全体に対する割合 |

## イベント仕様

### 1-日付範囲変更

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

**処理フロー**:
1. PostAnalyticsContextのsetRange関数が呼び出される
2. rangeが更新され、chartRangeが再計算される
3. useTinybirdQueryが新しいparamsで再実行
4. チャート・テーブルが再描画

### 2-フィルタ適用

**トリガー**: StatsFilterコンポーネントでフィルタを設定

**処理フロー**:
1. setAnalyticsFilters関数でフィルタ配列を更新
2. filterParamsがuseMemoで再計算
3. Tinybird APIにフィルタパラメータが追加
4. データが絞り込まれて再表示
5. scrollToTop関数でページ先頭にスクロール

**関連コード**: `apps/posts/src/views/PostAnalytics/Web/web.tsx` 72-96行目

### 3-ソースクリック

**トリガー**: ソース一覧のソース名をクリック

**処理フロー**:
1. handleSourceClick関数が発火
2. handleFilterClick('source', value)を呼び出し
3. createFilterでsourceフィルタを作成
4. setAnalyticsFiltersでフィルタ適用

**関連コード**: `apps/posts/src/views/PostAnalytics/Web/web.tsx` 115行目

### 4-ロケーションクリック

**トリガー**: ロケーション一覧の国/地域名をクリック

**処理フロー**:
1. handleLocationClick関数が発火
2. handleFilterClick('location', value)を呼び出し
3. createFilterでlocationフィルタを作成
4. setAnalyticsFiltersでフィルタ適用

**関連コード**: `apps/posts/src/views/PostAnalytics/Web/web.tsx` 114行目

### 5-email_only投稿時のリダイレクト

**トリガー**: 投稿ロード完了後、email_only投稿の場合

**処理フロー**:
1. useEffectで条件判定
2. post?.email_onlyがtrue
3. navigate関数で `/posts/analytics/:postId` にリダイレクト

**関連コード**: `apps/posts/src/views/PostAnalytics/Web/web.tsx` 42-46行目

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面表示 | - | SELECT | Tinybird APIからKPI/ソース/ロケーションデータ取得 |

### Tinybird API呼び出し詳細

| エンドポイント | パラメータ | 用途 |
|---------------|-----------|------|
| api_kpis | site_uuid, post_uuid, date_from, date_to, timezone, member_status, [filters] | KPIデータ |
| api_top_sources | site_uuid, post_uuid, date_from, date_to, timezone, member_status, [filters] | ソースデータ |
| api_top_locations | site_uuid, post_uuid, date_from, date_to, timezone, member_status, [filters] | ロケーションデータ |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|---------|
| - | 情報 | No visitors {period} | データがない場合 |
| - | 情報 | Try adjusting filters to see more data | フィルタでデータなし |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| email_only投稿 | Overviewにリダイレクト |
| データなし | EmptyIndicator表示 |
| kpiValues.visits === '0' | EmptyIndicator表示 |
| ローディング中 | BarChartLoadingIndicator表示 |

## 備考

- chartRangeは投稿公開日から計算され、選択したrangeとの最小値を使用
- UNKNOWN_LOCATION_VALUESに含まれる値は「Unknown」として集約
- フィルタはURLに同期されるため、ブックマーク・共有が可能
- hasFiltersによりUIレイアウトが変更される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | web.tsx | `apps/posts/src/views/PostAnalytics/Web/web.tsx` | ProcessedLocationData型定義（18-22行目） |
| 1-2 | kpi-helpers.ts | `apps/posts/src/utils/kpi-helpers.ts` | getWebKpiValues関数 |
| 1-3 | use-filter-params.ts | `apps/posts/src/hooks/use-filter-params.ts` | フィルタURL同期 |

**読解のコツ**: サイト統計のWeb画面と類似の構造だが、post_uuidパラメータが追加されている点に注意。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | web.tsx | `apps/posts/src/views/PostAnalytics/Web/web.tsx` | Webコンポーネント全体（27-270行目） |
| 2-2 | routes.tsx | `apps/posts/src/routes.tsx` | /webルート定義（41-44行目） |

**主要処理フロー**:
1. **28-30行目**: useNavigate, useParams, useGlobalDataでデータ取得
2. **33行目**: useFilterParamsでフィルタ状態取得
3. **36-39行目**: audienceをフィルタから導出
4. **42-46行目**: email_only投稿時のリダイレクト
5. **49-58行目**: chartRangeを公開日とrangeから計算
6. **72-96行目**: filterParamsを構築
7. **99-112行目**: handleFilterClick/handleLocationClick/handleSourceClick定義
8. **118-137行目**: params構築（post.uuidを含む）
9. **140-158行目**: useTinybirdQueryでkpi/locations/sources取得
10. **178-199行目**: processedLocationsData計算（Unknown集約）

#### Step 3: 子コンポーネントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | kpis.tsx | `apps/posts/src/views/PostAnalytics/Web/components/kpis.tsx` | KPIチャート表示 |
| 3-2 | locations.tsx | `apps/posts/src/views/PostAnalytics/Web/components/locations.tsx` | ロケーション一覧 |
| 3-3 | sources.tsx | `apps/posts/src/views/PostAnalytics/Web/components/sources.tsx` | ソース一覧 |

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

```
Web (web.tsx)
    │
    ├─ useGlobalData()
    │      └─ statsConfig, range, data, post, isPostLoading
    │
    ├─ useFilterParams()
    │      └─ analyticsFilters, setAnalyticsFilters
    │
    ├─ useMemo: audience
    │      └─ getAudienceFromFilterValues()
    │
    ├─ useMemo: chartRange
    │      └─ getRangeForStartDate + Math.min(range, calculatedRange)
    │
    ├─ useMemo: filterParams
    │      └─ source, location等のパラメータ構築
    │
    ├─ useMemo: params
    │      └─ site_uuid, post_uuid, date範囲, member_status, filters
    │
    ├─ useTinybirdQuery({endpoint: 'api_kpis'})
    │      └─ kpiData
    │
    ├─ useTinybirdQuery({endpoint: 'api_top_locations'})
    │      └─ locationsData
    │
    ├─ useTinybirdQuery({endpoint: 'api_top_sources'})
    │      └─ sourcesData
    │
    ├─ useMemo: processedLocationsData
    │      └─ Unknown集約、percentage計算
    │
    ├─ Kpis
    │      └─ KPIチャート表示
    │
    ├─ Locations
    │      └─ ロケーション一覧（onLocationClickコールバック）
    │
    └─ Sources
           └─ ソース一覧（onSourceClickコールバック）
```

### データフロー図

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

postId + range + ────────▶ useMemo: params ──────────▶ Tinybird API
post.uuid                          │
analyticsFilters                   ▼
                         useTinybirdQuery x 3
                                   │
                    ┌──────────────┼──────────────┐
                    ▼              ▼              ▼
                kpiData     locationsData   sourcesData
                    │              │              │
                    ▼              ▼              ▼
                  Kpis        Locations      Sources
                    │              │              │
                    ▼              ▼              ▼
              チャート表示    テーブル表示   テーブル表示

クリックイベント ─────────▶ handleFilterClick ─────▶ setAnalyticsFilters
                                   │
                                   ▼
                          filterParams更新
                                   │
                                   ▼
                          データ再取得・再描画
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| web.tsx | `apps/posts/src/views/PostAnalytics/Web/web.tsx` | ソース | Webトラフィック画面メイン |
| kpis.tsx | `apps/posts/src/views/PostAnalytics/Web/components/kpis.tsx` | ソース | KPIチャート |
| locations.tsx | `apps/posts/src/views/PostAnalytics/Web/components/locations.tsx` | ソース | ロケーション一覧 |
| sources.tsx | `apps/posts/src/views/PostAnalytics/Web/components/sources.tsx` | ソース | ソース一覧 |
| stats-filter.tsx | `apps/posts/src/views/PostAnalytics/components/stats-filter.tsx` | ソース | フィルタUI |
| date-range-select.tsx | `apps/posts/src/views/PostAnalytics/components/date-range-select.tsx` | ソース | 日付範囲選択 |
| post-analytics-header.tsx | `apps/posts/src/views/PostAnalytics/components/post-analytics-header.tsx` | ソース | ヘッダー |
| post-analytics-content.tsx | `apps/posts/src/views/PostAnalytics/components/post-analytics-content.tsx` | ソース | コンテンツエリア |
| use-filter-params.ts | `apps/posts/src/hooks/use-filter-params.ts` | ソース | フィルタURL同期 |
| audience.ts | `apps/posts/src/utils/audience.ts` | ソース | オーディエンスヘルパー |
| constants.ts | `apps/posts/src/utils/constants.ts` | ソース | 定数（UNKNOWN_LOCATION_VALUES等） |
