# 画面設計書 37-SEOメタデータ設定

## 概要

本ドキュメントは、Ghost管理画面の「SEOメタデータ」設定（Meta data）の設計内容を記載した画面設計書です。この画面は、検索エンジンとソーシャルメディア向けのメタデータを設定するための機能を提供します。

### 本画面の処理概要

この画面では、検索エンジン用のメタタイトル・説明、Facebook/Xカード用の画像・タイトル・説明を設定することができます。

**業務上の目的・背景**：SEO（検索エンジン最適化）とソーシャルメディアでの共有最適化は、サイトの露出と流入を増やすために重要です。この画面で適切なメタデータを設定することで、検索結果での表示、Facebook・X（旧Twitter）での共有時の表示を制御でき、クリック率向上に寄与します。

**画面へのアクセス方法**：
- 設定画面（/ghost/settings）のGeneral settingsセクション内
- 「Meta data」項目でメタデータを設定

**主要な操作・処理内容**：
1. 検索エンジン用メタデータ（Search）：メタタイトル、メタ説明の設定とプレビュー
2. Xカード設定：X用のタイトル、説明、画像の設定
3. Facebookカード設定：Facebook用のタイトル、説明、画像の設定
4. 画像アップロード：OG画像、X画像のアップロード・編集

**画面遷移**：
- 遷移元：設定画面のGeneral settingsセクション
- 遷移先：なし（タブ切り替え形式のインライン編集）

**権限による表示制御**：Admin権限以上のスタッフユーザーのみ編集可能。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 38 | SEO設定 | 主機能 | サイト全体のSEOメタデータ設定 |

## 画面種別

編集（タブ切り替え形式）

## URL/ルーティング

| URL パターン | 説明 |
|-------------|------|
| `/ghost/settings` (General settings内) | 設定画面内のMeta dataセクション |

## 入出力項目

### 入力項目

| 項目名 | 型 | 最大長 | 推奨長 | 設定キー | 説明 |
|--------|-----|--------|-------|---------|------|
| Meta title | String | 300 | 70文字 | meta_title | 検索エンジン用タイトル |
| Meta description | String | 500 | 156文字 | meta_description | 検索エンジン用説明 |
| Facebook title | String | 300 | - | og_title | Facebook共有用タイトル |
| Facebook description | String | 300 | - | og_description | Facebook共有用説明 |
| Facebook image | URL | - | - | og_image | Facebook共有用画像 |
| X title | String | 300 | - | twitter_title | X共有用タイトル |
| X description | String | 300 | - | twitter_description | X共有用説明 |
| X image | URL | - | - | twitter_image | X共有用画像 |

## 表示項目

### タブ構成

| タブID | タブ名 | 内容 |
|--------|-------|------|
| metadata | Search | 検索エンジン用メタデータ + プレビュー |
| twitter | X card | X用メタデータ + 画像アップロード |
| facebook | Facebook card | Facebook用メタデータ + 画像アップロード |

### Searchタブ（検索エンジンプレビュー）

| 項目名 | コンポーネント | 説明 |
|--------|--------------|------|
| Google検索プレビュー | SearchEnginePreview | Googleライクな検索結果プレビュー |
| Meta title入力 | TextField | プレースホルダー: サイトタイトル |
| Meta description入力 | TextField | プレースホルダー: サイト説明 |

### Xカードタブ

| 項目名 | コンポーネント | 説明 |
|--------|--------------|------|
| Xポストモック | div | Xのポストレイアウトを模したUI |
| X image | ImageUpload | カード画像アップロード |
| X title | TextField | カードタイトル |
| X description | TextField | カード説明 |

### Facebookカードタブ

| 項目名 | コンポーネント | 説明 |
|--------|--------------|------|
| Facebookポストモック | div | Facebookのポストレイアウトを模したUI |
| Facebook image | ImageUpload | カード画像アップロード |
| Facebook title | TextField | カードタイトル |
| Facebook description | TextField | カード説明 |

## イベント仕様

### 1-タブ切り替え

**トリガー**: TabViewでタブを選択

**処理内容**:
- setSelectedTab(tabId)で選択状態を更新
- 対応するタブコンテンツが表示される

### 2-テキストフィールド変更

**トリガー**: 各TextFieldへの入力

**処理内容**:
- createSettingHandler(settingKey)で生成されたハンドラーが呼ばれる
- updateSetting(settingKey, e.target.value)でローカル設定を更新
- isEditingがfalseならhandleEditingChange(true)で編集状態に遷移

### 3-画像アップロード

**トリガー**: ImageUploadでファイルを選択

**処理内容**:
- createImageUploadHandler(settingKey)で生成されたハンドラーが呼ばれる
- uploadImage({file})でAPIに画像アップロード
- getImageUrl()でアップロードされた画像のURLを取得
- updateSetting(settingKey, imageUrl)でローカル設定を更新
- エラー時（415 Unsupported）: 'Unsupported file type'メッセージ

**API呼び出し**:
```
POST /ghost/api/admin/images/upload/
Content-Type: multipart/form-data
```

### 4-画像削除

**トリガー**: ImageUploadの削除ボタンをクリック

**処理内容**:
- createImageDeleteHandler(settingKey)で生成されたハンドラーが呼ばれる
- updateSetting(settingKey, '')で画像URLを空に

### 5-Pintura画像編集

**トリガー**: Pintura有効時、画像の編集ボタンをクリック

**処理内容**:
- editor.openEditor({image, handleSave})でPinturaエディタを開く
- 編集完了時: handleSaveで新しい画像をアップロード
- updateSetting()で設定を更新

### 6-保存ボタンクリック

**トリガー**: Saveボタンをクリック

**処理内容**:
- handleSave()でAPI呼び出し
- 成功時: saveStateを更新、編集モードを終了

**API呼び出し**:
```
PUT /ghost/api/admin/settings/
Content-Type: application/json

{
  "settings": [
    { "key": "meta_title", "value": "..." },
    { "key": "meta_description", "value": "..." },
    { "key": "og_title", "value": "..." },
    { "key": "og_description", "value": "..." },
    { "key": "og_image", "value": "..." },
    { "key": "twitter_title", "value": "..." },
    { "key": "twitter_description", "value": "..." },
    { "key": "twitter_image", "value": "..." }
  ]
}
```

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面表示時 | settings | SELECT | 現在のメタデータ取得 |
| 画像アップロード | images | INSERT | 画像ファイル保存 |
| 保存ボタンクリック | settings | UPDATE | メタデータ更新 |

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

#### settings

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | meta_title | ユーザー入力値 | |
| UPDATE | meta_description | ユーザー入力値 | |
| UPDATE | og_title | ユーザー入力値 | |
| UPDATE | og_description | ユーザー入力値 | |
| UPDATE | og_image | アップロード画像URL | |
| UPDATE | twitter_title | ユーザー入力値 | |
| UPDATE | twitter_description | ユーザー入力値 | |
| UPDATE | twitter_image | アップロード画像URL | |

## メッセージ仕様

| メッセージ種別 | メッセージ内容 | 表示条件 |
|--------------|---------------|---------|
| ヒント | Recommended: 70 characters | Meta title |
| ヒント | Recommended: 156 characters | Meta description |
| ラベル | Upload Facebook image | Facebook画像未設定時 |
| ラベル | Upload X image | X画像未設定時 |
| エラー | Unsupported file type | 非対応ファイル形式アップロード時（HTTP 415） |

## 例外処理

| 例外状態 | 処理内容 |
|---------|---------|
| 画像アップロードエラー（415） | 'Unsupported file type'エラー表示 |
| その他APIエラー | handleError()でエラートースト表示 |

## 備考

- 編集ボタン（Edit）は非表示（hideEditButton）で、フィールドを直接編集して編集開始
- SearchEnginePreviewはGoogle検索結果をシミュレートしたプレビュー表示
- Pinturaエディタ統合により、アップロード画像のインライン編集が可能
- プレースホルダーにはサイトの基本設定（title, description）が使用される
- 値が空の場合、プレビューにはサイトの基本設定が表示される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | settings API | `@tryghost/admin-x-framework/api/settings` | getSettingValues関数 |
| 1-2 | images API | `@tryghost/admin-x-framework/api/images` | useUploadImage, getImageUrl |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | seo-meta.tsx | `apps/admin-x-settings/src/components/settings/general/seo-meta.tsx` | メインコンポーネント全体の構造 |

**主要処理フロー**:
1. **L18-61**: SearchEnginePreviewコンポーネント - Google検索プレビュー
2. **L63-74**: useSettingGroupでフォーム状態管理
3. **L81-103**: getSettingValuesで10個の設定値を取得
4. **L108-136**: createSettingHandler, createImageUploadHandler, createImageDeleteHandler
5. **L155-183**: metadataTabContent - Searchタブの内容
6. **L185-241**: facebookTabContent - Facebookタブの内容
7. **L243-297**: twitterTabContent - Xタブの内容
8. **L299-315**: tabs配列の定義

#### Step 3: 画像処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | use-pintura-editor.ts | `apps/admin-x-settings/src/hooks/use-pintura-editor.ts` | Pintura統合 |

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

```
[ユーザー操作]
    │
    ├─ SEOMeta コンポーネント
    │      │
    │      ├─ useSettingGroup() フック
    │      │      ├─ localSettings
    │      │      ├─ updateSetting()
    │      │      └─ handleSave()
    │      │
    │      ├─ useUploadImage() フック
    │      │      └─ POST /images/upload/
    │      │
    │      ├─ usePinturaEditor() フック
    │      │      └─ editor.openEditor()
    │      │
    │      └─ TopLevelGroup
    │             └─ TabView
    │                    ├─ [Search] metadataTabContent
    │                    │      ├─ TextField (meta_title)
    │                    │      ├─ TextField (meta_description)
    │                    │      └─ SearchEnginePreview
    │                    │
    │                    ├─ [X card] twitterTabContent
    │                    │      ├─ ImageUpload (twitter_image)
    │                    │      ├─ TextField (twitter_title)
    │                    │      └─ TextField (twitter_description)
    │                    │
    │                    └─ [Facebook card] facebookTabContent
    │                           ├─ ImageUpload (og_image)
    │                           ├─ TextField (og_title)
    │                           └─ TextField (og_description)
    │
    └─ [保存時]
           └─ Ghost Admin API
                  └─ PUT /settings/
```

### データフロー図

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

                     ┌─────────────────────────────┐
settings API ───────▶│ useSettingGroup()           │
(meta_*, og_*,       │   - localSettingsにコピー   │
twitter_*)           └────────────┬────────────────┘
                                  │
                     ┌────────────▼────────────────┐
[TextField入力] ────▶│ createSettingHandler()      │
                     │   - updateSetting()         │
                     └────────────┬────────────────┘
                                  │
                     ┌────────────▼────────────────┐
[ImageUpload] ──────▶│ createImageUploadHandler()  │
                     │   - uploadImage()           │───▶ [POST /images/upload/]
                     │   - getImageUrl()           │
                     │   - updateSetting()         │
                     └────────────┬────────────────┘
                                  │
                     ┌────────────▼────────────────┐
[Saveクリック] ─────▶│ handleSave()                │───▶ [PUT /settings/]
                     └─────────────────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| seo-meta.tsx | `apps/admin-x-settings/src/components/settings/general/seo-meta.tsx` | ソース | メインコンポーネント |
| use-setting-group.ts | `apps/admin-x-settings/src/hooks/use-setting-group.ts` | ソース | 設定グループ管理フック |
| use-pintura-editor.ts | `apps/admin-x-settings/src/hooks/use-pintura-editor.ts` | ソース | Pintura統合フック |
| general-settings.tsx | `apps/admin-x-settings/src/components/settings/general/general-settings.tsx` | ソース | 親コンポーネント |
| images API | `@tryghost/admin-x-framework/api/images` | ソース | 画像アップロードAPI |
