# 画面設計書 57-投げ銭・寄付設定

## 概要

本ドキュメントは、Ghost管理画面における「投げ銭・寄付設定」画面の設計仕様を記述するものである。

### 本画面の処理概要

投げ銭・寄付設定画面は、読者からの一回限りの支援（Tips & Donations）機能を設定する画面である。サブスクリプションとは異なり、読者が好きなタイミングで任意の金額を支払える仕組みを提供する。

**業務上の目的・背景**：月額/年額のサブスクリプションに加えて、一回限りの支援を受け付けることで、収益の多様化が可能になる。特に個人クリエイターやジャーナリストにとって、読者からの直接的なサポートは重要な収入源となる。この機能により、「コーヒー一杯分」のような気軽な支援を受け付けることができる。

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

**主要な操作・処理内容**：
1. 推奨寄付金額の設定
2. 通貨の選択
3. 共有可能リンクの確認・コピー
4. 寄付ページのプレビュー

**画面遷移**：設定画面のGrowthセクションからアクセスする。「Preview」ボタンからはPortalの寄付ページ（/#/portal/support）が新規タブで開く。

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

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 17 | 寄付機能 | 主機能 | 投げ銭・寄付機能の設定 |
| 14 | Stripe連携 | 補助機能 | Stripe経由での決済設定 |

## 画面種別

編集

## URL/ルーティング

- 管理画面URL: `/ghost/#/settings/tips-and-donations`
- 内部ルーティング: `navid='tips-and-donations'`
- ReactコンポーネントID: `TopLevelGroup` with `testId='tips-and-donations'`
- 寄付ページURL: `{siteUrl}/#/portal/support`

## 入出力項目

| 項目名 | 項目ID | データ型 | 入力/出力 | 必須 | 最大長 | 説明 |
|--------|--------|----------|-----------|------|--------|------|
| 推奨寄付金額 | donations_suggested_amount | string | 入力 | - | - | セント単位で保存（例：500 = 5ドル） |
| 通貨 | donations_currency | string | 入力 | - | - | 通貨コード（例：USD, EUR, JPY） |

## 表示項目

| 項目名 | 表示条件 | 説明 |
|--------|----------|------|
| Suggested amount入力 | 常時 | 推奨寄付金額を入力するCurrencyFieldコンポーネント |
| Currency選択 | 常時 | 通貨を選択するSelectコンポーネント |
| Shareable link表示 | 常時 | 寄付ページへの直接リンク |
| Previewボタン | 常時 | 寄付ページを新規タブで開くボタン |
| Copy linkボタン | 常時 | リンクをクリップボードにコピーするボタン |
| Stripeポリシーリンク | 常時 | Stripeの投げ銭ポリシーへのリンク |

## イベント仕様

### 1-推奨金額変更

CurrencyFieldコンポーネントのonChangeイベントでhandleSettingChange関数が呼び出される。金額はセント単位の文字列として'donations_suggested_amount'設定に保存される。

### 2-通貨変更

SelectコンポーネントのonSelectイベントでhandleSettingChange関数が呼び出される。'donations_currency'設定に通貨コードが保存される。通貨変更時はバリデーションが再実行される。

### 3-金額入力バリデーション

onBlurイベントでvalidate関数が呼び出される。validateCurrencyAmount関数により、金額が0より大きく、10,000以下であることを検証する。エラーがある場合はhintとしてエラーメッセージが表示される。

### 4-Previewボタン押下

openPreview関数が呼び出される。未保存の変更がある場合はconfirmIfDirtyダイアログを表示し、確認後に寄付ページを新規タブで開く。

### 5-Copy linkボタン押下

copyDonateUrl関数が呼び出される。navigator.clipboard.writeTextでリンクがコピーされ、2秒間「Copied」表示になる。

### 6-設定保存

値変更時に自動的にisEditing状態がtrueになり、保存ボタンが有効化される。handleSaveClick関数でhandleSaveが呼び出され、設定が保存される。

### 7-設定キャンセル

handleCancelClick関数でhandleCancelが呼び出され、変更が破棄されisEditing状態がfalseになる。

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 設定保存 | settings | UPDATE | donations_suggested_amount, donations_currencyを更新 |

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

#### settings

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | key='donations_suggested_amount' の value | セント単位の金額文字列 | デフォルト: '500' |
| UPDATE | key='donations_currency' の value | 通貨コード | デフォルト: 'USD' |

## メッセージ仕様

| メッセージID | 種類 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| MSG-001 | 説明 | Give your audience a simple way to support your work with one-time payments. | 説明文 |
| MSG-002 | 情報 | All tips and donations are subject to Stripe's tipping policy. | フッター注記 |
| MSG-003 | ラベル | Copied | リンクコピー後2秒間 |
| MSG-004 | ラベル | Copy link | リンクコピー前 |
| MSG-005 | エラー | Amount must be greater than 0 | 金額が0以下の場合 |
| MSG-006 | エラー | Amount cannot exceed 10,000 | 金額が10,000を超える場合 |

## 例外処理

| 例外条件 | 処理内容 |
|----------|----------|
| 金額バリデーションエラー | errorsオブジェクトにエラーメッセージを設定、保存を阻止 |
| 未保存変更時のプレビュー | confirmIfDirtyダイアログで確認を求める |
| API通信エラー | エラートーストを表示 |
| セッション切れ | 認証画面へリダイレクト |

## 備考

- Stripeでは事前設定金額の上限が10,000に制限されている（MAX_AMOUNT定数）
- 金額はセント単位で保存されるため、表示時には100で割って通貨単位に変換する
- 通貨リストはcurrencySelectGroups関数から取得される国際的な通貨一覧
- 寄付ページはPortalウィジェットの一部として表示される
- hideEditButton=trueにより、通常の編集ボタンは非表示（変更は即座に検知される）

---

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

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

### 推奨読解順序

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

投げ銭設定で使用される設定値と通貨ユーティリティを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | settings.ts | `apps/admin-x-framework/src/api/settings.ts` | Setting型の定義、getSettingValues関数 |
| 1-2 | currency.ts | `apps/admin-x-settings/src/utils/currency.ts` | currencySelectGroups関数、validateCurrencyAmount関数 |

**読解のコツ**: donations_suggested_amountはセント単位の文字列。通貨によって小数点以下の桁数が異なる場合がある。

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

投げ銭設定画面のメインファイルを読み解く。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | tips-and-donations.tsx | `apps/admin-x-settings/src/components/settings/growth/tips-and-donations.tsx` | コンポーネント全体の構造、バリデーションロジック |

**主要処理フロー**:
1. **11-30行目**: useSettingGroupフックの初期化（onValidateオプション付き）
2. **32-35行目**: getSettingValuesで現在の設定値を取得（donations_currency, donations_suggested_amount）
3. **37-38行目**: 金額のセント変換と寄付URLの生成
4. **40-42行目**: 通貨変更時のバリデーション再実行
5. **48-54行目**: localSettingsの変更を監視してisEditing状態を更新

#### Step 3: バリデーションロジックを理解する

金額入力のバリデーション処理を読み解く。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | tips-and-donations.tsx | `apps/admin-x-settings/src/components/settings/growth/tips-and-donations.tsx` | 24-30行目のonValidate |
| 3-2 | currency.ts | `apps/admin-x-settings/src/utils/currency.ts` | validateCurrencyAmount関数 |

**主要処理フロー**:
- **9行目**: MAX_AMOUNT = 10,000（Stripe制限）
- **25-28行目**: validateCurrencyAmountを呼び出し、maxAmountオプションを渡す
- エラーがある場合はerrorsオブジェクトにdonationsSuggestedAmountキーで格納

#### Step 4: 共有リンク機能を理解する

リンクのコピーとプレビュー機能を読み解く。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | tips-and-donations.tsx | `apps/admin-x-settings/src/components/settings/growth/tips-and-donations.tsx` | 56-64行目 |

**主要処理フロー**:
- **38行目**: donateUrl = siteUrl/#/portal/support
- **56-59行目**: copyDonateUrl - clipboard.writeText + 2秒タイマー
- **62-64行目**: openPreview - confirmIfDirty + window.open

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

```
TipsAndDonations (tips-and-donations.tsx)
    │
    ├─ useSettingGroup({ onValidate })
    │      ├─ useGlobalData() ... settings取得
    │      ├─ useEditSettings() ... 設定更新API
    │      ├─ useForm() ... フォーム状態管理
    │      └─ onValidate → validateCurrencyAmount()
    │
    ├─ getSettingValues()
    │      ├─ donations_currency (default: 'USD')
    │      └─ donations_suggested_amount (default: '500')
    │
    ├─ donateUrl生成
    │      └─ siteData.url + '/#/portal/support'
    │
    └─ TopLevelGroup
           ├─ hideEditButton: true
           ├─ CurrencyField
           │      ├─ valueInCents: donations_suggested_amount
           │      ├─ onChange → handleSettingChange()
           │      ├─ onBlur → validate()
           │      └─ rightPlaceholder: Select (currency)
           │
           ├─ Shareable link
           │      ├─ Preview button → openPreview()
           │      │      └─ confirmIfDirty → window.open(donateUrl)
           │      └─ Copy link button → copyDonateUrl()
           │
           └─ Stripe policy link
```

### データフロー図

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

初期表示
settings ───▶ getSettingValues ───▶ donations_currency, donations_suggested_amount
    │
    └─ siteData.url ───▶ donateUrl生成 ───▶ Shareable link表示

金額変更
ユーザー入力 ───▶ CurrencyField onChange
    │
    ▼
handleSettingChange('donations_suggested_amount', cents.toString())
    │
    ▼
updateSetting() ───▶ localSettings更新 (dirty: true)
    │
    ▼
useEffect検知 ───▶ isEditing: true

保存
Save click ───▶ handleSaveClick()
    │
    ▼
validate() ───▶ validateCurrencyAmount()
    │              │
    │              ├─ OK → handleSave() → PUT /settings/
    │              └─ NG → errors.donationsSuggestedAmount
    │
    ▼
成功 ───▶ isEditing: false
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| tips-and-donations.tsx | `apps/admin-x-settings/src/components/settings/growth/tips-and-donations.tsx` | ソース | 投げ銭設定画面のメインコンポーネント |
| use-setting-group.tsx | `apps/admin-x-settings/src/hooks/use-setting-group.tsx` | ソース | 設定グループ共通フック |
| currency.ts | `apps/admin-x-settings/src/utils/currency.ts` | ソース | 通貨関連ユーティリティ |
| settings.ts | `apps/admin-x-framework/src/api/settings.ts` | ソース | Settings API定義 |
| support-page.js | `apps/portal/src/components/pages/support-page.js` | ソース | Portal側の寄付ページ |
