# 画面設計書 45-ティア設定

## 概要

本ドキュメントは、Ghost管理画面の「ティア設定」画面の設計仕様を定義する。この画面は有料プラン（Tier）の作成・編集・価格設定、Stripe連携を行う機能を提供する。

### 本画面の処理概要

この画面では、サイトのメンバーシッププラン（ティア）を管理する。無料ティアと有料ティアの設定、価格設定、特典の定義、Stripeとの連携などが可能である。

**業務上の目的・背景**：Ghostはサブスクリプション収益モデルをサポートするプラットフォームであり、ティア（価格プラン）はその中核機能である。サイト運営者は複数の価格帯を設定し、それぞれに異なる特典を付与することで、多様な購読者ニーズに対応できる。Stripeとの連携により、安全な決済処理が実現される。

**画面へのアクセス方法**：管理画面のサイドバーから「Settings」→「Membership」セクション→「Tiers」カードにアクセスする。URLは`/ghost/#/settings`でnavid: `tiers`となる。

**主要な操作・処理内容**：
1. Stripe接続/切断（決済機能の有効化）
2. アクティブティアとアーカイブ済みティアの一覧表示
3. 無料ティアの編集
4. 有料ティアの作成・編集・アーカイブ
5. 月額/年額価格の設定
6. 無料トライアル期間の設定
7. ティア特典（Benefits）の追加・編集・並び替え
8. ウェルカムページURLの設定

**画面遷移**：設定画面のMembershipセクションに直接表示される。ティアカードをクリックするとTierDetailModalが開く。Stripe接続ボタンでStripe連携モーダルが開く。

**権限による表示制御**：Administrator以上のロールを持つユーザーのみがティア設定にアクセスできる。Ghost(Pro)プランではStripe接続やティア数に制限がある場合がある。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 15 | Tier（プラン）管理 | 主機能 | 有料プランの作成・価格設定 |
| 14 | Stripe連携 | 補助機能 | 決済プラットフォームとの連携設定 |

## 画面種別

設定画面（一覧・モーダル遷移型）

## URL/ルーティング

- メイン画面: `/ghost/#/settings` (navid: `tiers`)
- ティア詳細モーダル: `/ghost/#/settings/tiers/{tierId}`
- 新規ティア作成: `/ghost/#/settings/tiers/new`
- Stripe接続: `/ghost/#/settings/stripe-connect`

## 入出力項目

| 項目名 | 種別 | データ型 | 必須 | 説明 |
|--------|------|---------|------|------|
| Name | 入力 | string | 必須 | ティアの名前（最大191文字） |
| Description | 入力 | string | - | ティアの説明（最大191文字） |
| Monthly price | 入力 | number | 有料のみ | 月額価格（セント単位） |
| Yearly price | 入力 | number | 有料のみ | 年額価格（セント単位） |
| Currency | 入力 | enum | 有料のみ | 通貨コード |
| Trial days | 入力 | number | - | 無料トライアル日数 |
| Welcome page URL | 入力 | string | - | 登録完了後のリダイレクトURL |
| Benefits | 入力 | string[] | - | ティアの特典リスト |
| Visibility | 入力 | enum | - | 表示設定（public/none） |

## 表示項目

| 項目名 | データ型 | 説明 |
|--------|---------|------|
| Active Tiers | Tier[] | アクティブなティア一覧 |
| Archived Tiers | Tier[] | アーカイブ済みティア一覧 |
| Stripe connection status | boolean | Stripe接続状態 |

## イベント仕様

### 1-Stripe接続ボタン押下

「Connect with Stripe」または「Connected to Stripe」ボタンをクリックすると：

1. Stripe接続制限のチェック（`limiter.errorIfWouldGoOverLimit`）
2. 制限エラーがある場合、`LimitModal`を表示
3. 制限がない場合、`stripe-connect`ルートへ遷移

### 2-ティアカードクリック

ティアカードをクリックすると：

1. `TierDetailModal`が開く
2. 既存ティアの場合は編集モード、「Add tier」の場合は新規作成モード

### 3-ティア保存

「Save」ボタンをクリックすると：

1. バリデーション実行（名前必須、価格検証）
2. `useAddTier`または`useEditTier`でAPIリクエスト
3. 無料ティアの可視性変更時は`portal_plans`設定も更新

### 4-ティアアーカイブ

「Archive tier」ボタンをクリックすると：

1. 確認モーダルを表示
2. 確認後、`useEditTier`でactive=falseに更新
3. アーカイブ済みティアに移動

### 5-ティア再有効化

「Reactivate tier」ボタンをクリックすると：

1. ティア数制限のチェック
2. 確認モーダルを表示
3. 確認後、`useEditTier`でactive=trueに更新

### 6-タブ切り替え

「Active」「Archived」タブを切り替えると：

1. 対応するティア一覧が表示される
2. Stripe未接続時は「Archived」タブは非表示

### 7-Load more

「Load more」ボタンをクリックすると：

1. `fetchNextPage`で次のページを取得
2. 無限スクロール的なページネーション

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| ティア作成 | tiers | INSERT | 新規ティア追加 |
| ティア編集 | tiers | UPDATE | ティア情報更新 |
| ティアアーカイブ | tiers | UPDATE | active=false |
| 無料ティア可視性変更 | settings | UPDATE | portal_plans更新 |

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

#### tiers

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | name | ティア名 | 必須 |
| INSERT/UPDATE | description | 説明 | - |
| INSERT/UPDATE | monthly_price | 月額（セント） | 有料のみ |
| INSERT/UPDATE | yearly_price | 年額（セント） | 有料のみ |
| INSERT/UPDATE | currency | 通貨コード | 有料のみ |
| INSERT/UPDATE | trial_days | トライアル日数 | - |
| INSERT/UPDATE | welcome_page_url | リダイレクトURL | - |
| INSERT/UPDATE | benefits | 特典配列 | JSON |
| INSERT/UPDATE | visibility | public/none | - |
| UPDATE | active | true/false | アーカイブ時 |

## メッセージ仕様

| 種別 | メッセージ | 表示条件 |
|------|-----------|----------|
| エラー | Enter a name for the tier | 名前が空の場合 |
| エラー | 通貨検証エラー | 価格が無効な場合 |
| 成功 | Tier archived | アーカイブ完了時 |
| 成功 | Tier reactivated | 再有効化完了時 |
| 確認 | Members will no longer be able to subscribe... | アーカイブ確認 |
| 確認 | Reactivating will re-enable it as an option... | 再有効化確認 |

## 例外処理

| 状態 | 処理内容 |
|------|----------|
| Stripe未接続 | 有料ティア機能を制限、無料ティアのみ表示 |
| ティア制限エラー | LimitModalを表示 |
| バリデーションエラー | フィールド下にエラーメッセージを表示 |

## 備考

- Stripe接続が必須で有料ティア機能が有効化される
- 無料ティアは削除不可、1つのみ
- ティアの月額価格でソートされて表示
- 特典（Benefits）はドラッグ＆ドロップで並び替え可能
- 無料トライアルは年額プランにのみ適用
- アーカイブ済みティアは新規登録不可だが、既存メンバーは維持

---

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

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

### 推奨読解順序

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

まず、ティアのデータ型定義を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | tiers.ts | `apps/admin-x-framework/src/api/tiers.ts` | Tier型の定義（7-23行目） |

**読解のコツ**: `Tier`型にはid, name, description, monthly_price, yearly_price, benefits, visibility, activeなどの主要フィールドが定義されている。

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

処理の起点となるファイル・関数を特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | tiers.tsx | `apps/admin-x-settings/src/components/settings/membership/tiers.tsx` | メインコンポーネントの構造（26-108行目） |

**主要処理フロー**:
1. **28-31行目**: 設定とティアデータの取得
2. **35-52行目**: Stripe接続モーダルを開く処理
3. **54-56行目**: ティアのソート処理
4. **58-69行目**: タブの定義
5. **71-76行目**: Stripe接続状態による表示切り替え

#### Step 3: ティア詳細モーダルを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | tier-detail-modal.tsx | `apps/admin-x-settings/src/components/settings/membership/tiers/tier-detail-modal.tsx` | モーダル全体の構造 |

**主要処理フロー**:
- **16-97行目**: TierDetailModalContentコンポーネント
- **29-33行目**: バリデーション定義
- **35-97行目**: useFormフックによるフォーム管理
- **127-154行目**: アーカイブ/再有効化の確認モーダル

#### Step 4: API連携を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | tiers.ts | `apps/admin-x-framework/src/api/tiers.ts` | ティアAPI定義 |

**主要処理フロー**:
- **34-52行目**: `useBrowseTiers` - ティア一覧取得（無限スクロール対応）
- **54-60行目**: `useAddTier` - 新規ティア作成
- **62-71行目**: `useEditTier` - ティア更新

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

```
Tiers (tiers.tsx)
    │
    ├─ useGlobalData()
    │      ├─ settings
    │      └─ config
    │
    ├─ useBrowseTiers() ──► GET /tiers/
    │      ├─ tiers: Tier[]
    │      ├─ meta: pagination info
    │      └─ isEnd: boolean
    │
    ├─ useLimiter()
    │      └─ errorIfWouldGoOverLimit()
    │
    ├─ checkStripeEnabled()
    │      └─ 設定からStripe状態を判定
    │
    ├─ TabView (Active/Archived)
    │      └─ TiersList
    │             └─ TierCard (クリックでモーダル)
    │
    └─ StripeButton / StripeConnectedButton
           └─ openConnectModal()
                  └─ updateRoute('stripe-connect')

TierDetailModal (tier-detail-modal.tsx)
    │
    ├─ useForm()
    │      ├─ formState: TierFormState
    │      ├─ updateForm()
    │      └─ handleSave()
    │
    ├─ useAddTier() ──► POST /tiers/
    ├─ useEditTier() ──► PUT /tiers/{id}/
    │
    └─ Form UI
           ├─ TextField (name, description)
           ├─ CurrencyField (prices)
           ├─ Toggle (free trial)
           ├─ URLTextField (welcome page)
           └─ SortableList (benefits)
```

### データフロー図

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

                    ┌─────────────────┐
                    │  useBrowseTiers  │───────► Tier一覧表示
                    │                  │
                    └────────┬─────────┘
                             │
              ┌──────────────┼──────────────┐
              │              │              │
    ┌─────────▼────┐  ┌──────▼─────┐  ┌────▼────────┐
    │ Active Tiers │  │Archived    │  │ Free Tier   │
    │ (sorted by   │  │Tiers       │  │ (type=free) │
    │ price)       │  │            │  │             │
    └──────┬───────┘  └────────────┘  └──────┬──────┘
           │                                  │
           └──────────────────────────────────┘
                             │
                    ┌────────▼────────┐
TierCard Click ─────┤ TierDetailModal  │
                    └────────┬────────┘
                             │
                    ┌────────▼────────┐
フォーム入力 ───────┤    useForm       │
                    │   formState      │
                    └────────┬────────┘
                             │
              ┌──────────────┼──────────────┐
              │              │              │
    ┌─────────▼────┐  ┌──────▼─────┐       │
    │ useAddTier   │  │useEditTier │       │
    │ POST /tiers/ │  │PUT /tiers/ │       │
    └──────────────┘  └────────────┘       │
                             │             │
                    ┌────────▼─────────────▼┐
                    │       tiers DB        │
                    └───────────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| tiers.tsx | `apps/admin-x-settings/src/components/settings/membership/tiers.tsx` | ソース | メイン設定コンポーネント |
| tiers-list.tsx | `apps/admin-x-settings/src/components/settings/membership/tiers/tiers-list.tsx` | ソース | ティア一覧表示 |
| tier-detail-modal.tsx | `apps/admin-x-settings/src/components/settings/membership/tiers/tier-detail-modal.tsx` | ソース | ティア詳細編集モーダル |
| tier-detail-preview.tsx | `apps/admin-x-settings/src/components/settings/membership/tiers/tier-detail-preview.tsx` | ソース | ティアプレビュー |
| tiers.ts | `apps/admin-x-framework/src/api/tiers.ts` | ソース | ティアAPI定義 |
| settings.ts | `apps/admin-x-framework/src/api/settings.ts` | ソース | 設定API定義 |
| currency.ts | `apps/admin-x-settings/src/utils/currency.ts` | ソース | 通貨関連ユーティリティ |
