# 機能設計書：75-Announcement Bar

## 1. 概要

### 1.1 機能の目的
Announcement Barは、Ghostサイトのページ上部に告知バーを表示するフロントエンドウィジェットです。サイト管理者が設定した告知メッセージをサイト訪問者に対して表示し、重要なお知らせやプロモーション情報を効果的に伝達します。

### 1.2 機能の範囲
- Ghost Content APIからの告知設定取得
- 告知バーのレンダリングと表示
- 閉じるボタンによる非表示制御
- sessionStorageを使用した表示状態の永続化
- コンテンツ変更時の自動再表示
- プレビューモードのサポート
- 3種類の背景カラーテーマ（light/accent/dark）

### 1.3 関連画面
| 画面No | 画面名 | 関連度 |
|--------|--------|--------|
| 103 | 告知バー | 高 |

## 2. 機能種別
- 種別：フロントエンドウィジェット（UMD）
- 分類：フロントエンドアプリ
- カテゴリ：表示・通知

## 3. 入力仕様

### 3.1 scriptタグ属性
| 属性名 | 型 | 必須 | 説明 |
|--------|-----|------|------|
| data-announcement-bar | - | Yes | スクリプト識別子 |
| data-api-url | string | Yes | 告知設定APIのURL |
| data-preview | boolean | No | プレビューモードフラグ |
| data-announcement | string | No | プレビュー用告知コンテンツ（HTML） |
| data-announcement-background | string | No | プレビュー用背景色（light/accent/dark） |

### 3.2 API応答データ
| フィールド | 型 | 説明 |
|------------|-----|------|
| announcement | string | 告知メッセージ（HTML） |
| announcement_background | string | 背景色設定（light/accent/dark） |

## 4. 出力仕様

### 4.1 レンダリング出力
- DOM要素：`#announcement-bar-root`にReactコンポーネントをマウント
- CSSクラス：`.gh-announcement-bar`、`.gh-announcement-bar-content`
- 背景テーマ：`.light`、`.accent`、`.dark`

### 4.2 ストレージ出力
| キー | 保存先 | 説明 |
|------|--------|------|
| isAnnouncementBarVisible | sessionStorage | バー表示状態 |
| announcementBarContent | sessionStorage | 現在のコンテンツ（変更検出用） |

## 5. 処理フロー

### 5.1 初期化フロー
```
1. init()実行
   ├─ getSiteData(): scriptタグからapiUrl/previewDataを取得
   ├─ setup(): addRootDiv()でルートdivをbody先頭に追加
   └─ ReactDOM.render(): Appコンポーネントをマウント

2. App コンポーネント
   ├─ previewData存在: Previewコンポーネント表示
   └─ previewData不在: Mainコンポーネント表示

3. Main コンポーネント
   ├─ setupGhostApi(): APIクライアント初期化
   ├─ api.init(): 告知設定取得
   └─ setSiteSettings(): 状態更新→AnnouncementBar表示
```

### 5.2 表示制御フロー
```
1. AnnouncementBar マウント時
   ├─ shouldShowBar(content)
   │   ├─ isContentChanged(): sessionStorageと比較
   │   │   ├─ 変更あり: setBarVisibility(true), setContent(content), return true
   │   │   └─ 変更なし: getBarVisibility()の値を返す
   │   └─ return visible状態

2. 閉じるボタンクリック時
   ├─ setVisible(false): React状態更新
   └─ setBarVisibility(false): sessionStorage更新
```

## 6. ビジネスルール

### 6.1 表示ルール
| ルールID | ルール内容 | 実装箇所 |
|----------|-----------|----------|
| BR-01 | 告知コンテンツが空の場合は表示しない | announcement-bar.js:28-30 |
| BR-02 | ユーザーが閉じた場合はセッション中非表示 | announcement-bar.js:19-22 |
| BR-03 | コンテンツが変更された場合は再表示 | announcement-bar.js:47-54 |
| BR-04 | プレビューモードではAPIを呼ばずに直接表示 | app.js:6-8 |

### 6.2 セッション管理
- sessionStorageを使用（ブラウザタブ/ウィンドウごと）
- タブを閉じると状態はリセット
- 新しいタブでは告知バーが再表示される

## 7. データベース操作仕様

本機能はクライアントサイドウィジェットのため、直接のデータベース操作は行いません。

### 7.1 参照データ（API経由）
- 告知設定はGhost CoreのAPIを通じて取得
- APIエンドポイント：`data-api-url`属性で指定されたURL

## 8. エラー処理

### 8.1 エラー種別と対応
| エラー種別 | 検出方法 | 対応 |
|------------|----------|------|
| API通信失敗 | res.ok === false | Error throw、バー非表示 |
| scriptタグ不在 | querySelector結果null | 空オブジェクト返却、バー非表示 |
| 告知コンテンツ空 | !settings.announcement | null返却（バー非表示） |

### 8.2 エラーハンドリング実装
```javascript
// api.js:22-26
.then(function (res) {
    if (res.ok) {
        return res.json();
    } else {
        throw new Error('Failed to fetch site data');
    }
});
```

## 9. トランザクション仕様

本機能はクライアントサイドウィジェットのため、トランザクション管理は適用されません。
sessionStorageへの書き込みは同期的に実行されます。

## 10. パフォーマンス要件

### 10.1 レンダリング性能
- 初期レンダリング：100ms以内
- DOM挿入位置：body先頭（prepend）
- z-index: 90（他要素より前面）

### 10.2 バンドルサイズ
- UMD形式でビルド
- React 17.0.2を依存として含む
- 最小限のコンポーネント構成

## 11. セキュリティ考慮事項

### 11.1 XSS対策
- **注意**: dangerouslySetInnerHTMLを使用
- 告知コンテンツはGhost管理画面で入力されたHTMLをそのまま表示
- サーバーサイドでのサニタイズに依存

### 11.2 認証・認可
- 公開APIを使用（認証不要）
- credentialsオプションは未指定（デフォルト動作）

## 12. 備考

### 12.1 技術スタック
- React 17.0.2
- Vite（ビルドツール）
- CSSファイルによるスタイリング（Tailwind不使用）
- SVGアイコン（vite-plugin-svgr）

### 12.2 背景カラーテーマ
| テーマ | 背景色 | 文字色 | リンク色 |
|--------|--------|--------|----------|
| light | #f0f0f0 | #15171a | accent-color |
| accent | --ghost-accent-color | #fff | #fff |
| dark | #15171a | #fff | #fff |

### 12.3 国際化対応
- 本ウィジェットは多言語対応機能を持たない
- 告知コンテンツは管理画面で任意の言語を入力可能

---

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

### 推奨読解順序

#### 1. エントリーポイントの理解
**ファイル**: `apps/announcement-bar/src/index.js`

```
init() (45-57行目)
├── getSiteData() (18-28行目): scriptタグからdata属性を取得
├── getPreviewData() (30-38行目): プレビューモード判定
├── setup() → addRootDiv() (8-16行目): ルートdiv作成
└── ReactDOM.render(): Appコンポーネントをマウント
```

#### 2. コンポーネント構造の把握
**ファイル**: `apps/announcement-bar/src/app.js`

```
App (5-12行目)
├── previewData存在 → Preview コンポーネント
└── previewData不在 → Main コンポーネント
```

#### 3. API通信の理解
**ファイル**: `apps/announcement-bar/src/utils/api.js`

```
setupGhostApi() (1-38行目)
├── makeRequest() (2-10行目): fetch wrapper
├── api.announcementSettings.browse() (14-29行目): 告知設定取得
└── api.init() (32-35行目): 初期化処理
```

#### 4. Mainコンポーネントの理解
**ファイル**: `apps/announcement-bar/src/components/main.js`

```
Main (5-26行目)
├── useRef(setupGhostApi): APIインスタンス保持
├── useState(): siteSettings状態管理
├── useEffect(): API呼び出し（初回のみ）
└── AnnouncementBar: 告知バーレンダリング
```

#### 5. AnnouncementBarコンポーネントの詳細
**ファイル**: `apps/announcement-bar/src/components/announcement-bar.js`

```
AnnouncementBar (6-41行目)
├── useState(shouldShowBar): 初期表示状態
├── useEffect(): content変更時の再表示
├── handleButtonClick() (19-22行目): 閉じる処理
└── JSX: バーのレンダリング

shouldShowBar() (46-58行目)
├── isContentChanged() (64-71行目): コンテンツ変更検出
├── setBarVisibility() (72-78行目): 状態保存
└── getBarVisibility() (80-82行目): 状態取得
```

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

```
[Browser Load]
    │
    ▼
index.js::init()
    │
    ├── getSiteData()
    │   └── getPreviewData()
    │
    ├── setup()
    │   └── addRootDiv()
    │
    └── ReactDOM.render(<App />)
            │
            ├── [previewData] → <Preview />
            │                       └── <AnnouncementBar />
            │
            └── [no previewData] → <Main />
                                       │
                                       ├── setupGhostApi()
                                       │   └── api.init()
                                       │       └── api.announcementSettings.browse()
                                       │           └── fetch(apiUrl)
                                       │
                                       └── <AnnouncementBar />
                                               │
                                               ├── shouldShowBar()
                                               │   ├── isContentChanged()
                                               │   ├── setBarVisibility()
                                               │   └── getBarVisibility()
                                               │
                                               └── handleButtonClick()
                                                   └── setBarVisibility(false)
```

### データフロー図

```
[scriptタグ属性]
    │
    ├── data-api-url ──────────────────┐
    ├── data-preview ──┐               │
    ├── data-announcement ─┤           │
    └── data-announcement-background ─┤           │
                                       │           │
                                       ▼           │
                               [getPreviewData()]  │
                                       │           │
    ┌──────────────────────────────────┼───────────┤
    │                                  │           │
    ▼                                  ▼           ▼
[previewData]                    [Main Component]
    │                                  │
    │                                  ├── setupGhostApi(apiUrl)
    │                                  │
    │                                  ▼
    │                          [Ghost API]
    │                                  │
    │                                  ▼
    │                     {announcement, announcement_background}
    │                                  │
    └──────────────────┬───────────────┘
                       │
                       ▼
              [AnnouncementBar]
                       │
    ┌──────────────────┼──────────────────┐
    │                  │                  │
    ▼                  ▼                  ▼
[shouldShowBar()]  [visible state]  [sessionStorage]
    │                  │                  │
    └──────────────────┴──────────────────┘
                       │
                       ▼
               [DOM Rendering]
              .gh-announcement-bar
```

### 関連ファイル一覧

| ファイルパス | 役割 | 重要度 |
|-------------|------|--------|
| apps/announcement-bar/src/index.js | エントリーポイント、初期化処理 | 高 |
| apps/announcement-bar/src/app.js | ルートコンポーネント、ルーティング | 高 |
| apps/announcement-bar/src/components/main.js | API連携、メインロジック | 高 |
| apps/announcement-bar/src/components/announcement-bar.js | UIコンポーネント、表示制御 | 高 |
| apps/announcement-bar/src/components/preview.js | プレビューモード対応 | 中 |
| apps/announcement-bar/src/utils/api.js | APIクライアント | 高 |
| apps/announcement-bar/src/components/announcement-bar.css | スタイル定義 | 中 |
| apps/announcement-bar/src/icons/clear.svg | 閉じるアイコン | 低 |
| apps/announcement-bar/package.json | 依存関係、ビルド設定 | 中 |
