# 画面設計書 54-レコメンデーション設定

## 概要

本ドキュメントは、Ghost管理画面における「レコメンデーション設定」画面の設計仕様を記述するものである。

### 本画面の処理概要

レコメンデーション設定画面は、サイトが推薦する他のパブリケーション（おすすめサイト）の管理と、他サイトからの推薦状況を確認するための画面である。相互推薦によるオーディエンス拡大を支援する機能を提供する。

**業務上の目的・背景**：コンテンツパブリッシャーにとって、関連するサイトとの相互推薦は新規読者獲得の有効な手段である。Ghost間での推薦システムにより、サインアップ完了後の画面やポータルで推薦サイトを表示し、読者に価値ある他のパブリケーションを紹介できる。また、他サイトから推薦された場合に「推薦返し」を行うことで、相互に読者を増やすエコシステムを構築できる。

**画面へのアクセス方法**：管理画面のサイドメニューから「Settings」を選択し、「Growth」セクション内の「Recommendations」項目をクリックしてアクセスする。URLパスは`#/settings/recommendations`となる。

**主要な操作・処理内容**：
1. 推薦サイト一覧の表示・管理（Your recommendations）
2. 他サイトからの推薦一覧の確認（Recommending you）
3. 新規推薦サイトの追加
4. 既存推薦サイトの編集・削除
5. 推薦返しの実行

**画面遷移**：設定画面のGrowthセクションからアクセスする。「Add recommendation」ボタンからは新規推薦追加モーダル（recommendations/add）へ遷移する。各推薦項目クリックで編集モーダルへ遷移する。

**権限による表示制御**：Administrator（管理者）およびOwner（オーナー）ロールのみがこの設定にアクセス・編集可能である。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 60 | サイトレコメンデーション | 主機能 | おすすめサイトの追加・編集・削除 |
| 61 | 受信レコメンデーション | 補助機能 | 他サイトからの推薦の確認 |

## 画面種別

一覧（タブ切り替え式）

## URL/ルーティング

- 管理画面URL: `/ghost/#/settings/recommendations`
- 内部ルーティング: `navid='recommendations'`
- ReactコンポーネントID: `TopLevelGroup` with `testId='recommendations'`

## 入出力項目

| 項目名 | 項目ID | データ型 | 入力/出力 | 必須 | 最大長 | 説明 |
|--------|--------|----------|-----------|------|--------|------|
| 推薦サイトURL | url | string | 入力（モーダル） | Yes | 2000 | 推薦するサイトのURL |
| 推薦タイトル | title | string | 入力（モーダル） | - | 255 | 表示用タイトル（自動取得も可能） |
| 推薦説明文 | description | string | 入力（モーダル） | - | 2000 | 表示用説明文 |
| ワンクリック購読 | one_click_subscribe | boolean | 入力（モーダル） | - | - | Ghostサイトの場合に有効化可能 |

## 表示項目

| 項目名 | 表示条件 | 説明 |
|--------|----------|------|
| Your recommendationsタブ | 常時 | 自サイトが推薦しているサイトの一覧（件数カウンター付き） |
| Recommending youタブ | 常時 | 他サイトから推薦されているサイトの一覧（件数カウンター付き） |
| Add recommendationボタン | 常時（デスクトップ） | 新規推薦サイトを追加するボタン |
| 推薦サイトリスト | recommendationsあり | 推薦サイトのアイコン、タイトル、クリック/サインアップ数を表示 |
| 受信推薦リスト | incomingRecommendationsあり | 推薦元サイトのアイコン、タイトル、流入メンバー数を表示 |
| Recommend backボタン | 推薦返し未実行の受信推薦 | 推薦返しを行うボタン |
| 共有リンク | Your recommendationsタブ | 推薦ページへの直接リンクとコピーボタン |
| Show moreボタン | ページネーションあり | 追加データを読み込むボタン |

## イベント仕様

### 1-タブ切り替え

TabViewコンポーネントでタブを選択すると、selectedTab状態が更新され、対応するリストコンポーネントが表示される。初期値は'your-recommendations'。

### 2-Add recommendation押下

ボタン押下時にupdateRoute('recommendations/add')が呼び出され、新規推薦追加モーダルが開く。

### 3-推薦項目クリック

RecommendationItem押下時にNiceModal.show(EditRecommendationModal)が呼び出され、編集モーダルが表示される。推薦データがpropsとして渡される。

### 4-Recommend back押下

IncomingRecommendationItemのボタン押下時にupdateRoute('recommendations/add?url=...')が呼び出され、推薦元サイトのURLがプリセットされた追加モーダルが開く。

### 5-共有リンクコピー

コピーボタン押下時にnavigator.clipboard.writeTextで推薦ページURLがクリップボードにコピーされ、2秒間「Copied」表示になる。

### 6-Show more押下

fetchNextPage関数が呼び出され、次のページのデータがReact Queryにより取得・追加される。

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 推薦追加（モーダル） | recommendations | INSERT | 新規推薦レコードを作成 |
| 推薦編集（モーダル） | recommendations | UPDATE | 推薦レコードを更新 |
| 推薦削除（モーダル） | recommendations | DELETE | 推薦レコードを削除 |

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

#### recommendations

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | title | 入力値またはメタデータから自動取得 | |
| INSERT | url | 入力されたURL | |
| INSERT | description | 入力値またはメタデータから自動取得 | |
| INSERT | one_click_subscribe | Ghostサイトの場合true | |
| INSERT | favicon | メタデータから自動取得 | |
| INSERT | featured_image | メタデータから自動取得 | |

## メッセージ仕様

| メッセージID | 種類 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| MSG-001 | 情報 | Shared with new members after signup, or anytime using this link | Your recommendationsタブのヒント |
| MSG-002 | ラベル | Copied | リンクコピー後2秒間 |
| MSG-003 | ラベル | Copy link | リンクコピー前 |
| MSG-004 | 情報 | No one's recommended you yet. Once they do, you'll find them here along with how many memberships they've driven. | 受信推薦がない場合 |

## 例外処理

| 例外条件 | 処理内容 |
|----------|----------|
| API通信エラー | エラートーストを表示 |
| 推薦サイトなし | 「Add first recommendation」ボタンとExploreリンクを表示 |
| 受信推薦なし | 案内メッセージを表示 |
| セッション切れ | 認証画面へリダイレクト |

## 備考

- 推薦サイトはGhostサイトとそれ以外で表示が異なる
  - Ghostサイト：サインアップ数を表示、ワンクリック購読が可能
  - 非Ghostサイト：クリック数を表示
- 受信推薦からの流入メンバー数はリファラー履歴（referrer history）から集計
- ページネーションは初回5件、以降100件ずつ読み込む設計
- beta=trueフラグにより実験的機能としてマークされている

---

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

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

### 推奨読解順序

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

Recommendation型とIncomingRecommendation型の構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | recommendations.ts | `apps/admin-x-framework/src/api/recommendations.ts` | Recommendation型（4-16行目）、IncomingRecommendation型（87-95行目） |
| 1-2 | referrers.ts | `apps/admin-x-framework/src/api/referrers.ts` | ReferrerHistoryItem型（流入統計用） |

**読解のコツ**: Recommendationにはcount.subscribersとcount.clicksがあり、one_click_subscribeがtrueのGhostサイトではsubscribersを表示する。

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

メインコンポーネントとタブ構成を読み解く。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | recommendations.tsx | `apps/admin-x-settings/src/components/settings/growth/recommendations.tsx` | コンポーネント全体の構造、タブ設定 |

**主要処理フロー**:
1. **11-15行目**: useSettingGroupでsaveState/handleSaveを取得
2. **18-44行目**: useBrowseRecommendationsで推薦リストを取得（無限スクロール対応）
3. **52-77行目**: useBrowseIncomingRecommendationsで受信推薦リストを取得
4. **79行目**: useReferrerHistoryで流入統計を取得
5. **89-102行目**: tabs配列の定義（Your recommendations, Recommending you）

#### Step 3: 推薦リストコンポーネントを理解する

Your recommendationsタブの一覧表示ロジックを読み解く。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | recommendation-list.tsx | `apps/admin-x-settings/src/components/settings/growth/recommendations/recommendation-list.tsx` | 推薦項目の表示ロジック |

**主要処理フロー**:
- **18-66行目**: RecommendationItemコンポーネント（アイコン、タイトル、統計表示）
- **31-35行目**: isGhostSite判定によるsubscribers/clicks表示の分岐
- **68-104行目**: RecommendationListコンポーネント（リスト表示、共有リンク、空状態）

#### Step 4: 受信推薦リストコンポーネントを理解する

Recommending youタブの一覧表示ロジックを読み解く。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | incoming-recommendation-list.tsx | `apps/admin-x-settings/src/components/settings/growth/recommendations/incoming-recommendation-list.tsx` | 受信推薦の表示ロジック |

**主要処理フロー**:
- **17-85行目**: IncomingRecommendationItemコンポーネント
- **20-35行目**: signups計算（referrer historyからドメインマッチングで集計）
- **37-39行目**: recommendBack関数（推薦返し）

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

```
Recommendations (recommendations.tsx)
    │
    ├─ useSettingGroup() ... saveState取得
    │
    ├─ useBrowseRecommendations()
    │      ├─ GET /recommendations/ (無限スクロール)
    │      └─ hasNextPage, fetchNextPage
    │
    ├─ useBrowseIncomingRecommendations()
    │      ├─ GET /incoming_recommendations/
    │      └─ hasNextPage, fetchNextPage
    │
    ├─ useReferrerHistory()
    │      └─ 流入統計データ取得
    │
    ├─ TabView
    │      ├─ Tab: Your recommendations
    │      │      └─ RecommendationList
    │      │             ├─ Table
    │      │             ├─ RecommendationItem (multiple)
    │      │             │      └─ onClick → EditRecommendationModal
    │      │             └─ 共有リンク/コピーボタン
    │      │
    │      └─ Tab: Recommending you
    │             └─ IncomingRecommendationList
    │                    ├─ Table
    │                    └─ IncomingRecommendationItem (multiple)
    │                           └─ Recommend back → recommendations/add?url=...
    │
    └─ Add recommendation ボタン
           └─ updateRoute('recommendations/add')
```

### データフロー図

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

API取得
/recommendations/ ───▶ useBrowseRecommendations ───▶ recommendations[]
    │
/incoming_recommendations/ ───▶ useBrowseIncomingRecommendations ───▶ incomingRecommendations[]
    │
/referrers/history ───▶ useReferrerHistory ───▶ stats[]

統計計算
recommendations ───▶ count.subscribers / count.clicks ───▶ 表示
    │
incomingRecommendations + stats ───▶ ドメインマッチング ───▶ signups表示

ユーザー操作
Add click ───▶ updateRoute('recommendations/add') ───▶ AddRecommendationModal
    │
Item click ───▶ NiceModal.show(EditRecommendationModal) ───▶ 編集モーダル
    │
Recommend back ───▶ updateRoute('recommendations/add?url=...') ───▶ AddRecommendationModal
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| recommendations.tsx | `apps/admin-x-settings/src/components/settings/growth/recommendations.tsx` | ソース | レコメンデーション設定画面のメインコンポーネント |
| recommendation-list.tsx | `apps/admin-x-settings/src/components/settings/growth/recommendations/recommendation-list.tsx` | ソース | 推薦リストコンポーネント |
| incoming-recommendation-list.tsx | `apps/admin-x-settings/src/components/settings/growth/recommendations/incoming-recommendation-list.tsx` | ソース | 受信推薦リストコンポーネント |
| recommendation-icon.tsx | `apps/admin-x-settings/src/components/settings/growth/recommendations/recommendation-icon.tsx` | ソース | 推薦アイコンコンポーネント |
| edit-recommendation-modal.tsx | `apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx` | ソース | 推薦編集モーダル |
| recommendations.ts | `apps/admin-x-framework/src/api/recommendations.ts` | ソース | Recommendations API定義 |
| referrers.ts | `apps/admin-x-framework/src/api/referrers.ts` | ソース | Referrer History API定義 |
