# 画面設計書 40-ニュースレター受信者

## 概要

本ドキュメントは、Shopware 6 管理画面（Administration）のニュースレター受信者画面の設計仕様を定義します。この画面はニュースレター購読者を一覧表示・管理するための機能を提供し、購読者の検索、フィルタリング、編集、削除を行います。

### 本画面の処理概要

**業務上の目的・背景**：ニュースレター受信者画面は、ECショップのメールマーケティング活動の基盤となる購読者リストを管理するための機能です。購読者のステータス管理（オプトイン/オプトアウト）、購読者情報の確認、販売チャネル別の購読者管理などを行います。効果的なメールマーケティングキャンペーンの実施に不可欠な機能です。

**画面へのアクセス方法**：左側メインナビゲーションの「マーケティング」>「ニュースレター受信者」をクリックしてアクセスします。URLパスは `/admin#/sw/newsletter/recipient/index` です。権限 `newsletter_recipient.viewer` が必要です。

**主要な操作・処理内容**：
1. 受信者一覧の表示：ページネーション付きで購読者を一覧表示
2. 多彩なフィルタリング：ステータス、言語、販売チャネル、タグでフィルタ
3. 並び替え：作成日時（デフォルト降順）でソート
4. インライン編集：一覧上で直接メールアドレス、名前を編集
5. 受信者詳細編集：詳細ページでの編集
6. 受信者削除：購読者データの削除
7. サイドバーフィルタ：開閉可能なフィルタサイドバー

**画面遷移**：メールアドレスをクリックすると受信者詳細編集画面に遷移します。

**権限による表示制御**：
- `newsletter_recipient.viewer`: 受信者一覧の閲覧
- `newsletter_recipient.editor`: インライン編集
- `newsletter_recipient.deleter`: 削除ボタンの表示

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | ニュースレター管理 | 主機能 | ニュースレター購読者の一覧表示・管理 |

## 画面種別

一覧

## URL/ルーティング

- **URLパス**: `/admin#/sw/newsletter/recipient/index`
- **ルート名**: `sw.newsletter.recipient.index`
- **モジュール名**: `sw-newsletter-recipient`
- **エンティティ**: `newsletter_recipient`
- **エンティティ表示プロパティ**: `email`

### サブルート

| ルート名 | パス | コンポーネント | 説明 |
|---------|------|---------------|------|
| sw.newsletter.recipient.detail | `/detail/:id` | sw-newsletter-recipient-detail | 受信者詳細編集 |

## 入出力項目

### フィルタ入力項目

| 項目名 | 項目ID | 入力形式 | 説明 |
|--------|--------|----------|------|
| ステータス | status | マルチセレクト | 購読ステータスでの絞り込み（未設定/直接/オプトイン/オプトアウト） |
| 言語 | languageId | マルチセレクト | 言語での絞り込み |
| 販売チャネル | salesChannelId | マルチセレクト | 販売チャネルでの絞り込み |
| タグ | tags | マルチセレクト | タグでの絞り込み |

### 検索入力項目

| 項目名 | 項目ID | 入力形式 | 説明 |
|--------|--------|----------|------|
| 検索キーワード | term | テキスト | メールアドレス、名前で検索 |

## 表示項目

### ステータス値

| 値 | ラベルキー | 説明 |
|---|-----------|------|
| notSet | sw-newsletter-recipient.list.notSet | 未設定 |
| direct | sw-newsletter-recipient.list.direct | 直接登録 |
| optIn | sw-newsletter-recipient.list.optIn | オプトイン（確認済み） |
| optOut | sw-newsletter-recipient.list.optOut | オプトアウト（購読解除） |

### 一覧カラム

| 項目名 | property | 表示形式 | インライン編集 | デフォルト表示 | 説明 |
|--------|----------|----------|---------------|---------------|------|
| メールアドレス | email | テキスト+リンク | 可（string） | Yes | 詳細へのリンク（IDN対応） |
| 名前 | firstName | テキスト | 可（string） | Yes（primary） | 名前 |
| 販売チャネル | salesChannel.name | テキスト | - | No | 販売チャネル名 |
| ステータス | status | テキスト | - | Yes | 購読ステータス |
| 郵便番号 | zipCode | テキスト | - | Yes | 郵便番号 |
| 市区町村 | city | テキスト | - | Yes | 市区町村 |
| 住所 | street | テキスト | - | No | 住所 |
| 更新日時 | updatedAt | 日時 | - | No | 最終更新日時 |
| 作成日時 | createdAt | 日時 | - | No | 登録日時 |

## イベント仕様

### 1-受信者一覧取得（getList）

画面表示時および検索・フィルタ・ページング変更時に実行：

1. `Criteria` に基づいてクエリを構築
2. 内部フィルタ（`internalFilters`）を適用
3. 販売チャネルのアソシエーションを追加
4. 検索スコアを追加
5. 新しい検索キーワードの場合はソートをリセット
6. ニュースレター受信者リポジトリで検索を実行
7. 結果を `items` に格納、`total` を更新

### 2-初期化処理（createdComponent）

コンポーネント作成時の処理：

1. フィルタ用の言語一覧を取得
2. フィルタ用の販売チャネル一覧を取得
3. フィルタ用のタグ一覧を取得
4. `getList()` を呼び出して一覧を取得

### 3-ステータスフィルタ変更（onStatusSelectionChanged）

ステータスフィルタが変更された際の処理：

1. 選択された値を `statusFilterValue` に保存
2. 選択がある場合：`Criteria.equalsAny` でフィルタを設定
3. 選択がない場合：フィルタを削除
4. `getList()` を呼び出して一覧を再取得

### 4-言語フィルタ変更（onLanguageSelectionChanged）

言語フィルタが変更された際の処理：

1. 選択された値を `languageFilterValue` に保存
2. 選択がある場合：`Criteria.equalsAny` でフィルタを設定
3. 選択がない場合：フィルタを削除
4. `getList()` を呼び出して一覧を再取得

### 5-販売チャネルフィルタ変更（onSalesChannelSelectionChanged）

販売チャネルフィルタが変更された際の処理：

1. 選択された値を `salesChannelFilterValue` に保存
2. 選択がある場合：`Criteria.equalsAny` でフィルタを設定
3. 選択がない場合：フィルタを削除
4. `getList()` を呼び出して一覧を再取得

### 6-タグフィルタ変更（onTagSelectionChanged）

タグフィルタが変更された際の処理：

1. 選択された値を `tagFilterValue` に保存
2. 選択がある場合：`Criteria.equalsAny('tags.id', ...)` でフィルタを設定
3. 選択がない場合：フィルタを削除
4. `getList()` を呼び出して一覧を再取得

### 7-フィルタサイドバー開閉（closeContent）

フィルタサイドバーの開閉処理：

1. サイドバーが開いている場合：閉じる
2. サイドバーが閉じている場合：開く

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 一覧表示 | newsletter_recipient | SELECT | 受信者一覧データの取得 |
| 一覧表示 | sales_channel | SELECT | 販売チャネル情報の取得 |
| フィルタ初期化 | language | SELECT | 言語一覧の取得 |
| フィルタ初期化 | sales_channel | SELECT | 販売チャネル一覧の取得 |
| フィルタ初期化 | tag | SELECT | タグ一覧の取得 |
| インライン編集 | newsletter_recipient | UPDATE | 受信者情報の更新 |
| 受信者削除 | newsletter_recipient | DELETE | 受信者の削除 |

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

#### newsletter_recipient

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, email, first_name, last_name, status, zip_code, city, street, language_id, sales_channel_id, created_at, updated_at | フィルタ・ソート条件 | 関連テーブル含む |
| UPDATE | email, first_name, last_name | インライン編集値 | インライン編集時 |

## メッセージ仕様

| メッセージID | 種別 | 表示条件 | メッセージ内容 |
|-------------|------|----------|--------------|
| sw-newsletter-recipient.list.notSet | 情報 | ステータスが未設定 | 未設定 |
| sw-newsletter-recipient.list.direct | 情報 | ステータスが直接 | 直接登録 |
| sw-newsletter-recipient.list.optIn | 情報 | ステータスがオプトイン | オプトイン |
| sw-newsletter-recipient.list.optOut | 情報 | ステータスがオプトアウト | オプトアウト |

## 例外処理

| 例外種別 | 発生条件 | 処理内容 |
|---------|---------|---------|
| 検索結果なし | 条件に一致する受信者なし | 空の状態を表示 |
| フィルタデータ取得失敗 | API通信エラー | isLoading を false に設定 |
| インライン編集エラー | バリデーション失敗 | エラー通知表示 |

## 備考

- デフォルトソートは作成日時の降順（DESC）
- メールアドレスはIDNエンコード対応（国際化ドメイン名）
- フィルタ初期化時に言語、販売チャネル、タグを100件まで取得
- `searchConfigEntity`、`tagCollection`、`dateFilter`、`handleTagFilter`、`handleBooleanFilter`、`onChange` は v6.8.0 で非推奨
- フィルタは v6.8.0 から専用の `on___SelectionChanged` 関数を使用

---

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

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

### 推奨読解順序

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

ニュースレター受信者エンティティの構造を理解します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | default-search-configuration.js | `src/Administration/Resources/app/administration/src/module/sw-newsletter-recipient/default-search-configuration.js` | 検索対象フィールドの設定 |

**読解のコツ**: ニュースレター受信者は`newsletter_recipient`テーブルに保存され、`sales_channel`（販売チャネル）、`language`（言語）、`tag`（タグ）などと関連します。`status`フィールドで購読状態を管理します。

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

モジュール登録とルーティング設定を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.js | `src/Administration/Resources/app/administration/src/module/sw-newsletter-recipient/index.js` | モジュール登録、ルート定義、権限設定 |

**主要処理フロー**:
1. **19行目**: `Module.register('sw-newsletter-recipient', {...})` でモジュール登録
2. **32-39行目**: `routes.index` で一覧ページのルーティング定義
3. **37行目**: `meta.privilege: 'newsletter_recipient.viewer'` で閲覧権限を設定
4. **30行目**: `entityDisplayProperty: 'email'` でメールアドレスを表示プロパティに設定
5. **51-60行目**: ナビゲーションメニューの定義

#### Step 3: 一覧コンポーネントを理解する

ニュースレター受信者一覧ページのメインロジックを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.js | `src/Administration/Resources/app/administration/src/module/sw-newsletter-recipient/page/sw-newsletter-recipient-list/index.js` | 一覧表示のdata、computed、methodsの実装 |

**主要処理フロー**:
- **27-51行目**: `data()` で状態変数を定義（items, filterValues, internalFilters等）
- **88-95行目**: `statusData` でステータス選択肢を定義
- **103-126行目**: `createdComponent()` でフィルタ用データを取得
- **128-160行目**: `getList()` で受信者データを取得
- **162-204行目**: 各フィルタ変更ハンドラ（onStatusSelectionChanged等）
- **218-276行目**: `getColumns()` で一覧カラムを定義

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

```
sw-newsletter-recipient (モジュール)
    |
    +-- sw-newsletter-recipient-list (一覧ページコンポーネント)
           |
           +-- createdComponent()
           |      +-- Promise.all([languages, salesChannels, tags])
           |      +-- getList()
           |
           +-- getList()
           |      +-- Object.values(internalFilters).forEach(addFilter)
           |      +-- addQueryScores()
           |      +-- newsletterRecipientRepository.search(criteria)
           |
           +-- onStatusSelectionChanged(value)
           |      +-- internalFilters.status = Criteria.equalsAny()
           |      +-- getList()
           |
           +-- onLanguageSelectionChanged(value)
           |      +-- internalFilters.languageId = Criteria.equalsAny()
           |      +-- getList()
           |
           +-- onSalesChannelSelectionChanged(value)
           |      +-- internalFilters.salesChannelId = Criteria.equalsAny()
           |      +-- getList()
           |
           +-- onTagSelectionChanged(value)
           |      +-- internalFilters.tags = Criteria.equalsAny()
           |      +-- getList()
           |
           +-- closeContent()
                  +-- filterSideBar.openContent/closeContent()
```

### データフロー図

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

createdComponent   ───▶   Promise.all                ───▶   フィルタ選択肢
                          ・言語取得                         ・languageFilters
                          ・販売チャネル取得                  ・salesChannelFilters
                          ・タグ取得                         ・tagFilters

検索キーワード  ───▶      getList()                  ───▶   受信者一覧表示
フィルタ条件              ・internalFilters適用              ページネーション
ページ番号                ・Criteria構築
                          ・repository.search()

フィルタ変更   ───▶       on___SelectionChanged      ───▶   フィルタ適用
                          ・internalFilters更新              一覧再取得
                          ・getList()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.js | `src/Administration/Resources/app/administration/src/module/sw-newsletter-recipient/index.js` | ソース | モジュール登録・ルーティング定義 |
| index.js | `src/Administration/Resources/app/administration/src/module/sw-newsletter-recipient/page/sw-newsletter-recipient-list/index.js` | ソース | 一覧ページのメインロジック |
| sw-newsletter-recipient-list.html.twig | `src/Administration/Resources/app/administration/src/module/sw-newsletter-recipient/page/sw-newsletter-recipient-list/sw-newsletter-recipient-list.html.twig` | テンプレート | 一覧ページのHTMLテンプレート |
| sw-newsletter-recipient-detail | `src/Administration/Resources/app/administration/src/module/sw-newsletter-recipient/page/sw-newsletter-recipient-detail/` | ソース | 詳細ページコンポーネント |
| sw-newsletter-recipient-filter-switch | `src/Administration/Resources/app/administration/src/module/sw-newsletter-recipient/component/sw-newsletter-recipient-filter-switch/` | ソース | フィルタスイッチコンポーネント |
| acl/index.js | `src/Administration/Resources/app/administration/src/module/sw-newsletter-recipient/acl/index.js` | ソース | 権限マッピング定義 |
| default-search-configuration.js | `src/Administration/Resources/app/administration/src/module/sw-newsletter-recipient/default-search-configuration.js` | ソース | 検索設定 |
