# 画面設計書 89-メール停止通知ページ

## 概要

本ドキュメントは、Ghost会員向けポータル（Portal）のメール停止通知ページ（EmailSuppressedPage）の画面設計書である。メール配信が停止された会員に対して再有効化を促す画面を定義する。

### 本画面の処理概要

メール停止通知ページは、Ghost会員サイトのPortalウィジェット内で、メール配信が停止されている会員に対して、その理由を説明し、メール配信の再有効化を促す画面である。

**業務上の目的・背景**：メール配信は、スパム報告やハードバウンス（配信不能）が発生した場合に自動的に停止される。この画面は、配信停止の理由を会員に説明し、問題が解決した場合にメール配信を再有効化する手段を提供する。

**画面へのアクセス方法**：アカウントホームページやメール設定ページから、email_suppressedフラグがtrueの会員に対して表示される。直接URL（`#/portal/emailSuppressed`）でのアクセスも可能。

**主要な操作・処理内容**：
1. メール配信停止の理由説明表示
2. 「Re-enable emails」ボタンによる配信再有効化
3. 再有効化成功後のメール設定ページへの遷移（または前画面に戻る）
4. 再有効化失敗時のエラーハンドリング

**画面遷移**：
- 遷移元：アカウントホームページ（accountHome）、メール設定ページ（accountEmail）
- 遷移先：メール設定ページ（accountEmail）、前画面（back）

**権限による表示制御**：
- ログイン済み会員のみアクセス可能
- email_suppressed状態の会員に対して表示

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 20 | ニュースレター管理 | 主機能 | メール配信の再有効化 |
| 71 | Portal | 補助機能 | Portalウィジェット内でのUI表示 |

## 画面種別

確認・操作

## URL/ルーティング

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

## 入出力項目

| 項目名 | 項目ID | 型 | 必須 | 入力/出力 | 説明 |
|--------|--------|-----|------|----------|------|
| なし | - | - | - | - | 本画面に入力項目なし |

## 表示項目

| 項目名 | データソース | 説明 |
|--------|-------------|------|
| ページタイトル | - | "Emails disabled" 固定 |
| 説明文 | - | メール停止の理由説明 |
| アイコン | - | EmailDeliveryFailedIcon |
| 再有効化ボタン | - | "Re-enable emails" |

## イベント仕様

### 1-メール配信再有効化

**トリガー**："Re-enable emails" ボタンクリック

**処理フロー**：
1. `doAction('removeEmailFromSuppressionList')` 実行
2. actionが`removeEmailFromSuppressionList:success`に変化
3. useEffectで`doAction('refreshMemberData')`を実行
4. actionが`refreshMemberData:success`に変化
5. useEffectでページ遷移処理
   - hasMultipleNewsletters または hasCommentsEnabled の場合：accountEmailページへ遷移
   - それ以外：前画面に戻る

**API呼び出し**：
- DELETE `/members/api/member/suppression/`

### 2-再有効化成功後の遷移

**トリガー**：`refreshMemberData:success` アクション検知

**処理フロー**：
1. hasMultipleNewsletters(site) または hasCommentsEnabled(site) を判定
2. true の場合：
   - `doAction('switchPage', {page: 'accountEmail', lastPage: 'accountHome'})` 実行
   - `doAction('showPopupNotification', {message: 'You have been successfully resubscribed'})` 実行
3. false の場合：
   - `doAction('back')` 実行

### 3-再有効化失敗時の処理

**トリガー**：`removeEmailFromSuppressionList:failed` または `refreshMemberData:failed` アクション検知

**処理フロー**：
1. `doAction('back')` 実行
2. 前画面に戻る

### 4-戻るボタン

**トリガー**：BackButton クリック

**処理フロー**：
1. `doAction('back')` 実行
2. 前画面（lastPage）に戻る

### 5-閉じるボタン

**トリガー**：CloseButton クリック

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

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| メール再有効化 | members_email_suppression | DELETE | 抑制リストからの削除 |

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

#### members_email_suppression

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| DELETE | - | member_id = 現在の会員ID | 抑制リストから削除 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|--------------|---------|
| MSG-001 | タイトル | "Emails disabled" | 常時 |
| MSG-002 | 説明 | "You're not receiving emails because you either marked a recent message as spam, or because messages could not be delivered to your provided email address." | 常時 |
| MSG-003 | ボタン | "Re-enable emails" | 常時 |
| MSG-004 | 通知 | "You have been successfully resubscribed" | 再有効化成功時 |

## 例外処理

| 例外条件 | 処理内容 | 表示メッセージ |
|---------|---------|--------------|
| 抑制リスト削除失敗 | 前画面に戻る | なし（サイレント） |
| 会員データ更新失敗 | 前画面に戻る | なし（サイレント） |

## 備考

- isRunningフラグで処理中状態を管理（ボタン無効化）
- actionの値で処理状態を検知（`:running`, `:success`, `:failed`）
- lastPageが存在しない場合はBackButtonを非表示
- メール停止は主に2つの理由で発生：
  1. 会員がメールをスパムとしてマーク
  2. メールが配信不能（ハードバウンス）

---

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

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

### 推奨読解順序

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

メール停止通知ページで扱う主要なデータ構造を把握する。

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

**読解のコツ**: actionの値がページの状態管理に使用される。`:running`, `:success`, `:failed`のサフィックスで処理状態を判定。

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

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

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

**主要処理フロー**:
1. **10-11行**: 関数定義、AppContextからの値取得
2. **13-36行**: useEffectでaction状態に応じた処理
3. **38行**: isRunningフラグの計算
4. **40-42行**: handleSubmit関数

#### Step 3: 状態管理とアクション連携を理解する

useEffectによる状態監視とアクション実行を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | email-suppressed-page.js | `apps/portal/src/components/pages/email-suppressed-page.js` | useEffect内の処理フロー |

**主要処理フロー**:
- **14-16行**: `removeEmailFromSuppressionList:success` → `refreshMemberData`実行
- **18-20行**: 失敗時（`:failed`） → `back`実行
- **22-35行**: `refreshMemberData:success` → ページ遷移処理

#### Step 4: ヘルパー関数を理解する

ニュースレター/コメント機能の判定を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | helpers.js | `apps/portal/src/utils/helpers.js` | hasMultipleNewsletters、hasCommentsEnabled関数 |

**主要処理フロー**:
- `hasMultipleNewsletters`: site.newsletters.length > 1 を判定
- `hasCommentsEnabled`: site.comments_enabled を判定

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

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

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

**主要処理フロー**:
- **236-248行**: deleteSuppressionでDELETEリクエスト送信

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

```
EmailSuppressedPage (email-suppressed-page.js)
    |
    +-- useContext(AppContext)
    |       +-- brandColor, lastPage, doAction, action, site
    |
    +-- useEffect([action, doAction, site])
    |       |
    |       +-- action === 'removeEmailFromSuppressionList:success'
    |       |       +-> doAction('refreshMemberData')
    |       |
    |       +-- action === ':failed' (いずれか)
    |       |       +-> doAction('back')
    |       |
    |       +-- action === 'refreshMemberData:success'
    |               |
    |               +-- hasMultipleNewsletters || hasCommentsEnabled
    |               |       +-> doAction('switchPage', {page: 'accountEmail'})
    |               |       +-> doAction('showPopupNotification', {...})
    |               |
    |               +-- else
    |                       +-> doAction('back')
    |
    +-- handleSubmit()
    |       +-> doAction('removeEmailFromSuppressionList')
    |               +-> api.member.deleteSuppression()
    |
    +-- render()
            +-- BackButton (lastPage存在時)
            +-- CloseButton
            +-- EmailDeliveryFailedIcon
            +-- タイトル + 説明文
            +-- ActionButton (Re-enable emails)
```

### データフロー図

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

AppContext
  +-- action -------------> useEffect監視 ----------------> 状態遷移
  +-- site ---------------> hasMultipleNewsletters() -----> 遷移先判定
  +-- lastPage -----------> BackButton表示判定 -----------> 戻るボタン
  +-- brandColor ---------> ActionButtonスタイル ---------> ボタン色

ユーザー操作
  |
  +-- Re-enable emails ---> handleSubmit()
                                |
                                +-> doAction('removeEmailFromSuppressionList')
                                        |
                                        +-> api.member.deleteSuppression()
                                                |
                                                +-> DELETE /members/api/member/suppression/
                                                        |
                                                        +-> action = 'removeEmailFromSuppressionList:success'
                                                                |
                                                                +-> doAction('refreshMemberData')
                                                                        |
                                                                        +-> action = 'refreshMemberData:success'
                                                                                |
                                                                                +-> 遷移判定
                                                                                        |
                                                                                        +-- newsletters/comments有 -> accountEmail
                                                                                        +-- なし -> back
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| email-suppressed-page.js | `apps/portal/src/components/pages/email-suppressed-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通信処理 |
| helpers.js | `apps/portal/src/utils/helpers.js` | ソース | ヘルパー関数群 |
| action-button.js | `apps/portal/src/components/common/action-button.js` | ソース | アクションボタンコンポーネント |
| close-button.js | `apps/portal/src/components/common/close-button.js` | ソース | 閉じるボタンコンポーネント |
| back-button.js | `apps/portal/src/components/common/back-button.js` | ソース | 戻るボタンコンポーネント |
| email-delivery-failed.svg | `apps/portal/src/images/icons/email-delivery-failed.svg` | アセット | メール配信失敗アイコン |
| i18n.js | `apps/portal/src/utils/i18n.js` | ソース | 国際化ユーティリティ |
