# 通知設計書 28-設定保存成功通知

## 概要

本ドキュメントは、Etherpad管理画面において設定の保存が成功した際に表示されるトースト通知の設計について記載する。

### 本通知の処理概要

この通知は、管理者がSettingsページで設定を編集し、保存ボタンを押した際に、設定がJSON形式として有効であれば成功メッセージをトースト通知として表示する機能である。Radix UIのToastコンポーネントを使用して実装されている。

**業務上の目的・背景**：Etherpadの設定はJSON形式で管理されており、設定の変更は即座にサーバーに反映される。この通知により、管理者に対して設定の保存が正常に完了したことを明確にフィードバックし、操作の確認と安心感を提供する。

**通知の送信タイミング**：管理者が設定ページで「Save Settings」ボタンをクリックし、入力されたJSONが有効（isJSONClean関数で検証）と判定された直後に表示される。

**通知の受信者**：設定保存操作を実行した管理画面セッションの管理者。クライアントサイドのReactコンポーネントによって表示されるため、操作を行った本人のみが受信する。

**通知内容の概要**：「Successfully saved settings」というメッセージが成功を示す緑色のスタイルで表示される。

**期待されるアクション**：特に必要なし。確認用の通知であり、自動的に消去される。必要に応じてサーバーの再起動を行う。

## 通知種別

アプリ内通知（Radix Toast）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（クライアントサイド処理） |
| 優先度 | 中 |
| リトライ | なし |

### 送信先決定ロジック

設定保存操作を実行した管理者に対してのみ表示する。Zustand storeのtoastStateを更新することで表示をトリガーする。

## 通知テンプレート

### Radix Toast

| 項目 | 内容 |
|-----|------|
| スタイルクラス | ToastRootSuccess（緑色） |
| 自動消去 | あり（Radix Toastのデフォルト動作） |

### 本文テンプレート

```
Successfully saved settings
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| title | 通知タイトル | setToastStateの引数 | Yes |
| success | 成功/失敗フラグ | setToastStateの引数 | Yes |
| open | 表示/非表示フラグ | setToastStateの引数 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 画面操作 | Save Settingsボタンクリック | isJSONClean(settings) === true | 設定JSONが有効な形式 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| isJSONClean(settings) === false | 設定JSONが無効な形式の場合はエラー通知を表示 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[管理者が設定を編集] --> B[Save Settingsボタンをクリック]
    B --> C{isJSONClean?}
    C -->|true| D[settingsSocket.emit 'saveSettings']
    C -->|false| E[エラー通知を表示]
    D --> F[setToastState呼び出し]
    F --> G[title: 'Successfully saved settings']
    G --> H[success: true]
    H --> I[open: true]
    I --> J[トースト通知を表示]
```

## データベース参照・更新仕様

### 参照テーブル一覧

該当なし（クライアントサイドのみの処理）

### 更新テーブル一覧

該当なし（サーバーへの設定送信は別途socket.emitで実行）

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| JSON無効 | isJSONClean === false | エラー通知を表示（No.29参照） |

### リトライ仕様

該当なし

## 配信設定

### レート制限

該当なし

### 配信時間帯

制限なし（24時間対応）

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

- 設定ページへのアクセスには管理者認証が必要
- 設定内容はWebSocketを通じてサーバーに送信される
- 設定保存はクライアントサイドでのJSON検証を通過した場合のみ実行される

## 備考

- Radix UIのToast.Providerがアプリケーションのルートでラップされている
- ToastDialogコンポーネントがZustand storeのtoastStateを監視して表示を制御
- 成功時はToastRootSuccessクラス（緑色）、失敗時はToastRootFailureクラス（赤色）が適用される
- 設定の実際の保存はsocket.emit('saveSettings')で実行される

---

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

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

### 推奨読解順序

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

ToastStateの型定義とZustand storeの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | store.ts | `admin/src/store/store.ts` | ToastState型の定義（6-11行目） |
| 1-2 | store.ts | `admin/src/store/store.ts` | toastStateとsetToastState（23-24行目、41-47行目） |

**読解のコツ**: Zustandのcreate関数でstoreを作成し、setToastStateで状態を更新する流れを確認する。

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

設定保存処理とトースト表示のトリガーを特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | SettingsPage.tsx | `admin/src/pages/SettingsPage.tsx` | 保存ボタンのonClickハンドラ（17-34行目） |

**主要処理フロー**:
1. **18行目**: onClickハンドラ開始
2. **19行目**: isJSONClean(settings)でJSON検証
3. **21行目**: settingsSocket.emit('saveSettings', settings)で保存
4. **22-26行目**: setToastStateで成功通知を設定

#### Step 3: 通知表示コンポーネントを理解する

ToastDialogコンポーネントの実装を追跡する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Toast.tsx | `admin/src/utils/Toast.tsx` | ToastDialogコンポーネント全体（5-26行目） |
| 3-2 | main.tsx | `admin/src/main.tsx` | Toast.ProviderとToastDialogの配置（36-38行目） |

**主要処理フロー**:
- **6行目**: useStoreでtoastStateを取得
- **7-9行目**: success値に基づいてクラス名を決定
- **13行目**: Toast.Rootのopen属性をtoastState.openでバインド
- **19行目**: Toast.Titleにtoaststate.titleを表示

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

```
[Admin] SettingsPage.tsx
    │
    ├─ IconButton onClick [18行目]
    │      │
    │      ├─ isJSONClean(settings) [19行目]
    │      │
    │      ├─ settingsSocket.emit('saveSettings') [21行目]
    │      │
    │      └─ useStore.getState().setToastState() [22行目]
    │             │
    │             └─ { open: true, title: 'Successfully saved settings', success: true }
    │
    │
    ▼ Zustand state更新
    │
    │
[Admin] Toast.tsx
    │
    └─ ToastDialog [5行目]
           │
           ├─ useStore(state => state.toastState) [6行目]
           │
           ├─ resultingClass = 'ToastRootSuccess' [7-9行目]
           │
           └─ Toast.Root表示 [13-23行目]
```

### データフロー図

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

Save Settingsクリック ───▶ isJSONClean() ───▶ JSON検証
                                  │
                                  ▼
                          settingsSocket.emit()
                                  │
                                  ▼
                          setToastState({
                            open: true,
                            title: 'Successfully saved settings',
                            success: true
                          })
                                  │
                                  ▼
                          ToastDialog ───▶ トースト通知
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| SettingsPage.tsx | `admin/src/pages/SettingsPage.tsx` | ソース | 設定ページと保存処理 |
| Toast.tsx | `admin/src/utils/Toast.tsx` | ソース | トースト通知コンポーネント |
| store.ts | `admin/src/store/store.ts` | ソース | Zustand store（toastState管理） |
| main.tsx | `admin/src/main.tsx` | ソース | Toast.ProviderとToastDialogの配置 |
| utils.ts | `admin/src/utils/utils.ts` | ソース | isJSONClean関数 |
| index.css | `admin/src/index.css` | スタイル | ToastRootSuccess/Failureクラス定義 |
