# 画面設計書 88-フィードバックページ

## 概要

本ドキュメントは、Ghost会員向けポータル（Portal）のフィードバックページ（FeedbackPage）の画面設計書である。記事に対するフィードバック（More like this / Less like this）を送信する画面を定義する。

### 本画面の処理概要

フィードバックページは、Ghost会員サイトのPortalウィジェット内で、メールに含まれるフィードバックリンクから遷移し、記事に対する評価（いいね/よくない）を送信する画面である。

**業務上の目的・背景**：フィードバック機能は、会員が記事に対する評価を送信することで、サイト運営者がコンテンツの質を改善するための指標を収集する仕組みである。メール内のフィードバックリンクを通じて簡単に評価を送信できることで、会員のエンゲージメントを高める。

**画面へのアクセス方法**：ニュースレターメール内のフィードバックリンク（thumbs up/down）をクリックして遷移する。URLには`uuid`、`key`、`postId`、`score`パラメータが含まれる。直接URL（`#/portal/feedback`）でのアクセスも可能。

**主要な操作・処理内容**：
1. ログイン済み会員の場合は自動的にフィードバック送信
2. 未ログイン会員の場合は確認ダイアログを表示
3. フィードバック（score: 1=More like this, 0=Less like this）の選択と送信
4. 送信完了後の確認画面表示
5. エラー発生時のエラー画面表示

**画面遷移**：
- 遷移元：ニュースレターメール内のフィードバックリンク
- 遷移先：閉じる（ポップアップ終了）

**権限による表示制御**：
- ログイン状態/未ログイン状態で初期表示が異なる
- ログイン済み：自動送信後、完了画面表示
- 未ログイン：確認ダイアログ表示、選択後送信

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 71 | Portal | 主機能 | Portalウィジェット内でのUI表示 |
| 15 | コンテンツ公開 | 補助機能 | 記事に対するフィードバック収集 |

## 画面種別

入力・完了

## URL/ルーティング

- ハッシュルート：`#/portal/feedback`
- Pages.jsでの登録キー：`feedback`

## 入出力項目

| 項目名 | 項目ID | 型 | 必須 | 入力/出力 | 説明 |
|--------|--------|-----|------|----------|------|
| UUID | uuid | string | △（未ログイン時） | 入力 | 会員識別子 |
| Key | key | string | △（未ログイン時） | 入力 | 認証キー |
| 記事ID | postId | string | ○ | 入力 | フィードバック対象の記事ID |
| スコア | score | number | ○ | 入力 | 1=More like this, 0=Less like this |

## 表示項目

| 項目名 | データソース | 説明 |
|--------|-------------|------|
| ダイアログタイトル | - | "Give feedback on this post" 固定 |
| いいねボタン | - | "More like this" + ThumbUpIcon |
| よくないボタン | - | "Less like this" + ThumbDownIcon |
| 送信ボタン | - | "Submit feedback" |
| 完了タイトル | - | "Thanks for the feedback!" 固定 |
| 完了メッセージ | - | "Your input helps shape what gets published." 固定 |
| エラータイトル | - | "Sorry, that didn't work." 固定 |

## イベント仕様

### 1-フィードバック選択

**トリガー**："More like this" または "Less like this" ボタンクリック

**処理フロー**：
1. setScore(value) で選択状態を更新（1 or 0）
2. 選択中のボタンにハイライトスタイル適用
3. brandColor を使用した選択状態の視覚的フィードバック

**バリデーション**：
- なし（いずれかを選択必須）

### 2-フィードバック送信

**トリガー**："Submit feedback" ボタンクリック

**処理フロー**：
1. loading状態をtrueに設定
2. sendFeedback関数でAPIリクエスト送信
3. api.feedback.add({uuid, postId, key, score})を実行
4. 成功時：score更新、confirmed状態をtrueに設定
5. 失敗時：エラーメッセージ設定、ErrorPage表示

**API呼び出し**：
- POST `/members/api/feedback/`
- クエリパラメータ（未ログイン時）：`?uuid={uuid}&key={key}`
- ボディ：`{feedback: [{post_id: postId, score: score}]}`

### 3-自動フィードバック送信（ログイン済み）

**トリガー**：ページ表示時（isLoggedIn === true）

**処理フロー**：
1. ページ表示と同時にLoadingFeedbackView表示
2. useEffectでdoSendFeedback(score)を自動実行
3. 完了後ConfirmFeedback画面表示

### 4-ポップアップを閉じる

**トリガー**："Close" ボタンクリック、または CloseButton クリック

**処理フロー**：
1. `doAction('closePopup')` 実行
2. ポップアップを閉じる

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| フィードバック送信 | members_feedback | INSERT/UPDATE | フィードバックレコード作成または更新 |

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

#### members_feedback

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | member_id | 送信者の会員ID | - |
| INSERT/UPDATE | post_id | pageData.postId | フィードバック対象記事 |
| INSERT/UPDATE | score | 0 or 1 | 0=Less, 1=More |
| INSERT/UPDATE | created_at/updated_at | 現在日時 | - |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|--------------|---------|
| MSG-001 | タイトル | "Give feedback on this post" | 確認ダイアログ |
| MSG-002 | ボタン | "More like this" | いいねボタン |
| MSG-003 | ボタン | "Less like this" | よくないボタン |
| MSG-004 | ボタン | "Submit feedback" | 送信ボタン |
| MSG-005 | タイトル | "Thanks for the feedback!" | 完了画面 |
| MSG-006 | 説明 | "Your input helps shape what gets published." | 完了画面 |
| MSG-007 | ボタン | "Close" | 完了/エラー画面 |
| MSG-008 | タイトル | "Sorry, that didn't work." | エラー画面 |
| MSG-009 | エラー | "There was a problem submitting your feedback. Please try again a little later." | API通信エラー時 |

## 例外処理

| 例外条件 | 処理内容 | 表示メッセージ |
|---------|---------|--------------|
| API通信エラー | ErrorPage表示 | サーバーからのエラーメッセージまたはデフォルトメッセージ |
| 無効なuuid/key | APIエラー返却 | サーバーからのエラーメッセージ |

## 備考

- フィードバックは score: 1 (More like this) と score: 0 (Less like this) の2値
- ログイン済み会員は確認ダイアログをスキップして自動送信
- 未ログイン会員はuuid/keyをクエリパラメータとしてAPI送信
- 完了画面のアイコンは選択したスコアに応じて変化（ThumbUpIcon or ThumbDownIcon）
- モバイル表示時は下からスライドするトレイ形式のUI

---

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

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

### 推奨読解順序

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

フィードバックページで扱う主要なデータ構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | app-context.js | `apps/portal/src/app-context.js` | AppContextの構造（pageData, member, api, brandColor） |

**読解のコツ**: pageDataにuuid, key, postId, scoreが含まれる。memberの有無でログイン状態を判定。

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

ページコンポーネントの構造と初期化処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | feedback-page.js | `apps/portal/src/components/pages/feedback-page.js` | FeedbackPage関数コンポーネント |
| 2-2 | pages.js | `apps/portal/src/pages.js` | ページルーティング定義 |

**主要処理フロー**:
1. **308-318行**: FeedbackPage関数定義、state初期化
2. **313-316行**: isLoggedIn判定、confirmed/loading初期値設定
3. **337-348行**: 条件分岐によるコンポーネント返却

#### Step 3: 確認ダイアログを理解する

未ログイン時の確認ダイアログを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | feedback-page.js | `apps/portal/src/components/pages/feedback-page.js` | ConfirmDialogコンポーネント |

**主要処理フロー**:
- **201-264行**: ConfirmDialogコンポーネント定義
- **203行**: useState(initialScore)でスコア状態管理
- **218-225行**: ボタンスタイル計算（選択状態）
- **231-249行**: More like this / Less like this ボタン

#### Step 4: フィードバック送信処理を理解する

API送信処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | feedback-page.js | `apps/portal/src/components/pages/feedback-page.js` | sendFeedback関数、doSendFeedback関数 |

**主要処理フロー**:
- **266-269行**: sendFeedback関数でAPI呼び出し
- **319-329行**: doSendFeedbackでエラーハンドリング含む送信処理
- **331-334行**: onConfirmで確認ダイアログからの送信

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

バックエンドAPIとの連携処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | api.js | `apps/portal/src/utils/api.js` | feedback.add関数 |

**主要処理フロー**:
- **139-168行**: api.feedback.addでPOSTリクエスト送信
- **142-144行**: uuid/keyをクエリパラメータに追加（未ログイン時）

#### Step 6: 完了・エラー画面を理解する

完了画面とエラー画面の表示を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | feedback-page.js | `apps/portal/src/components/pages/feedback-page.js` | ConfirmFeedback、ErrorPageコンポーネント |

**主要処理フロー**:
- **279-306行**: ConfirmFeedback完了画面
- **282行**: positiveに応じたアイコン切り替え
- **173-199行**: ErrorPageエラー画面

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

```
FeedbackPage (feedback-page.js)
    |
    +-- useContext(AppContext)
    |       +-- site, pageData, member, api, brandColor
    |
    +-- useState()
    |       +-- score (initialScore)
    |       +-- confirmed (isLoggedIn)
    |       +-- loading (isLoggedIn)
    |       +-- error (null)
    |
    +-- render() 条件分岐
            |
            +-- error ? ErrorPage
            |       +-- ThumbErrorIcon
            |       +-- ActionButton (Close)
            |
            +-- !confirmed ? ConfirmDialog
            |       +-- ThumbUpIcon / ThumbDownIcon
            |       +-- setScore() - 選択状態更新
            |       +-- onConfirm() - 送信実行
            |               +-- doSendFeedback()
            |                       +-- sendFeedback()
            |                               +-- api.feedback.add()
            |
            +-- loading ? LoadingFeedbackView
            |       +-- useEffect -> action(score)
            |       +-- LoadingPage
            |
            +-- ConfirmFeedback
                    +-- ThumbUpIcon / ThumbDownIcon (positiveに応じて)
                    +-- ActionButton (Close)
```

### データフロー図

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

URLパラメータ
  +-- uuid ----------------> pageData.uuid
  +-- key -----------------> pageData.key
  +-- postId --------------> pageData.postId
  +-- score ---------------> pageData.score (initialScore)

AppContext
  +-- member -------------> isLoggedIn判定 ----------------> confirmed/loading初期値
  +-- api ----------------> sendFeedback() ---------------> API呼び出し
  +-- brandColor ---------> ボタンスタイル ---------------> 選択状態ハイライト

ユーザー入力（未ログイン時）
  |
  +-- More like this -----> setScore(1)
  +-- Less like this -----> setScore(0)
  |
  +-- Submit feedback ----> onConfirm()
                                |
                                +-> doSendFeedback()
                                        |
                                        +-> sendFeedback()
                                        |       |
                                        |       +-> api.feedback.add({
                                        |               uuid, postId, key, score
                                        |           })
                                        |
                                        +-> setScore(selectedScore)
                                        +-> setConfirmed(true)
                                                |
                                                +-> ConfirmFeedback表示

自動送信（ログイン済み）
  |
  +-- isLoggedIn=true ----> confirmed=true, loading=true
                                |
                                +-> LoadingFeedbackView
                                        |
                                        +-> useEffect -> doSendFeedback(score)
                                                |
                                                +-> ConfirmFeedback表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| feedback-page.js | `apps/portal/src/components/pages/feedback-page.js` | ソース | メインページコンポーネント |
| pages.js | `apps/portal/src/pages.js` | ソース | ページルーティング定義 |
| app-context.js | `apps/portal/src/app-context.js` | ソース | アプリケーションコンテキスト定義 |
| api.js | `apps/portal/src/utils/api.js` | ソース | API通信処理 |
| errors.js | `apps/portal/src/utils/errors.js` | ソース | エラーメッセージ処理 |
| action-button.js | `apps/portal/src/components/common/action-button.js` | ソース | アクションボタンコンポーネント |
| close-button.js | `apps/portal/src/components/common/close-button.js` | ソース | 閉じるボタンコンポーネント |
| loading-page.js | `apps/portal/src/components/pages/loading-page.js` | ソース | ローディング画面コンポーネント |
| thumbs-up.svg | `apps/portal/src/images/icons/thumbs-up.svg` | アセット | いいねアイコン |
| thumbs-down.svg | `apps/portal/src/images/icons/thumbs-down.svg` | アセット | よくないアイコン |
| thumbs-error.svg | `apps/portal/src/images/icons/thumbs-error.svg` | アセット | エラーアイコン |
| i18n.js | `apps/portal/src/utils/i18n.js` | ソース | 国際化ユーティリティ |
