# 通知設計書 22-情報通知

## 概要

本ドキュメントは、アプリ内での操作結果を知らせる情報レベルのトースト通知「情報通知」の設計仕様を定義する。ユーザーインターフェース上で一時的に表示され、ユーザーに情報を伝達するための非侵襲的な通知手段である。

### 本通知の処理概要

情報通知（Info Toast）は、アプリケーション内でユーザーに対して一般的な情報を提供するためのUI通知コンポーネントである。成功・警告・エラー以外の中立的な情報を伝達する目的で使用される。

**業務上の目的・背景**：ユーザーがアプリケーションを操作する中で、処理の進行状況や補足情報を即座にフィードバックすることは、優れたユーザーエクスペリエンスの実現に不可欠である。情報通知は、ユーザーの操作を中断することなく、必要な情報を視覚的に伝達する。これにより、ユーザーは現在の状態を把握し、次のアクションを判断できる。特にバックグラウンド処理の完了通知、データ同期状況、機能に関するヒントなど、確認を必要としない情報の伝達に適している。

**通知の送信タイミング**：アプリケーション内の特定のイベント発生時にリアルタイムで表示される。具体的には、toast関数またはinfo関数の呼び出し時に即座に画面右下にトースト通知が表示される。表示はpreserveオプションがtrueの場合は手動で閉じるまで表示され続け、falseの場合は自動的に消える。

**通知の受信者**：現在アプリケーションを操作中のユーザー本人のみが受信する。トースト通知はクライアントサイドで生成され、ブラウザのビューポート内に表示される。他のユーザーや外部システムには送信されない。

**通知内容の概要**：トースト通知には、メッセージ（message）が必須で、オプションとしてタイトル（title）、アクションボタン（action）を含めることができる。情報通知は青色のスタイルで表示され、視覚的に中立的な情報であることを示す。テキストは--color-infoカラートークンで定義された色で表示される。

**期待されるアクション**：ユーザーはトースト通知を確認し、必要に応じてアクションボタンをクリックするか、閉じるボタン（X）で通知を消去する。情報通知は確認のみを目的としており、必ずしもユーザーのアクションを必要としない。

## 通知種別

アプリ内通知（Toast / UI通知）

## 送信仕様

### 基本情報

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

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

- 現在のブラウザセッションでアプリケーションを操作中のユーザー
- ToastProviderコンポーネントがレンダリングされている画面のみ

## 通知テンプレート

### UI通知の場合

| 項目 | 内容 |
|-----|------|
| 表示位置 | 画面右下（bottom-right） |
| 幅 | 360px（最大100vw） |
| 背景色 | 青色グラデーション（--color-info系） |
| 形式 | Glass morphism UI |

### 本文テンプレート

```
┌──────────────────────────────────────┐
│ [タイトル（オプション）]           [X] │
│ メッセージ本文                        │
│                      [アクションボタン]│
└──────────────────────────────────────┘
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| なし | - | - | UI通知のため添付ファイルなし |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| message | 通知メッセージ本文 | 呼び出し元コンポーネント | Yes |
| title | 通知タイトル | 呼び出し元コンポーネント | No |
| id | トーストの一意識別子 | 自動生成またはオプション指定 | No |
| preserve | 自動消去の有無 | オプション（デフォルト: true） | No |
| action.label | アクションボタンのラベル | オプション | No |
| action.onClick | アクションボタンのクリックハンドラ | オプション | No |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 画面操作 | toast()関数呼び出し | type='info'または未指定 | 汎用トースト関数による情報通知 |
| 画面操作 | info()関数呼び出し | 常時 | 情報通知専用ショートカット関数 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| ToastProvider未配置 | ToastProviderがコンポーネントツリーに存在しない場合はエラー |
| SSRコンテキスト | サーバーサイドレンダリング中は表示されない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[イベント発生] --> B[toast/info関数呼び出し]
    B --> C{ToastContext存在?}
    C -->|Yes| D[トーストID生成/取得]
    C -->|No| E[エラースロー]
    D --> F[toasts配列に追加/更新]
    F --> G[ToastPrimitive.Root描画]
    G --> H[ユーザーに表示]
    H --> I{preserve=true?}
    I -->|Yes| J[手動クローズ待機]
    I -->|No| K[自動消去]
    J --> L[dismiss呼び出し]
    K --> L
    L --> M[toasts配列から削除]
    M --> N[終了]
    E --> N
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| なし | - | クライアントサイドのみで完結するためDB参照なし |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| なし | - | クライアントサイドのみで完結するためDB更新なし |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| Context未定義エラー | ToastProviderの外でuseToastsを呼び出し | "useToast must be used within a ToastProvider"エラーをスロー |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし |
| リトライ間隔 | - |
| リトライ対象エラー | - |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | なし（制限なし） |
| 1日あたり上限 | なし（制限なし） |

### 配信時間帯

制限なし（24時間、ユーザー操作に応じて即時表示）

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

- トースト通知は現在のユーザーセッションでのみ表示され、他ユーザーには漏洩しない
- XSS対策としてReactのエスケープ機能により、メッセージ内のHTMLはエスケープされる
- 機密情報はトースト通知に含めないこと

## 備考

- 実装ファイル: `internal-packages/ui/components/toast.tsx`
- Radix UIのToastプリミティブを使用
- Tailwind CSSでスタイリング
- clsx/liteでクラス名を条件付き結合
- カラートークン: --color-info

---

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

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

### 推奨読解順序

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

まず、トースト通知のデータ構造（インターフェース）を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | toast.tsx | `internal-packages/ui/components/toast.tsx` | Toastインターフェース（18-25行目）でトースト通知のプロパティ構造を確認 |
| 1-2 | toast.tsx | `internal-packages/ui/components/toast.tsx` | ToastOptionsインターフェース（29-34行目）でオプション構造を確認 |
| 1-3 | toast.tsx | `internal-packages/ui/components/toast.tsx` | ToastContextType（40-47行目）でコンテキストAPIを確認 |

**読解のコツ**: typeフィールドが"info" | "success" | "warning" | "error"の4種類あることに注目。

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

トースト通知の呼び出し元となるカスタムフックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | toast.tsx | `internal-packages/ui/components/toast.tsx` | useToastsフック（91-97行目）がコンテキストからtoast関数を提供 |

**主要処理フロー**:
1. **91行目**: useToasts関数定義
2. **92行目**: useContextでToastContextを取得
3. **93-95行目**: コンテキスト未定義時のエラーハンドリング
4. **96行目**: { toast, info, error }を返却

#### Step 3: 情報通知専用関数を理解する

info関数の実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | toast.tsx | `internal-packages/ui/components/toast.tsx` | info関数（129-139行目）が情報通知のショートカット |

**主要処理フロー**:
- **130-131行目**: メッセージとオプションを受け取る
- **132-137行目**: _toast関数に{ type: "info", preserve: true }で委譲

#### Step 4: トースト表示コンポーネントを理解する

ToastProviderコンポーネントのレンダリングロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | toast.tsx | `internal-packages/ui/components/toast.tsx` | ToastProvider（99-253行目）がトースト管理とレンダリングを担当 |
| 4-2 | toast.tsx | `internal-packages/ui/components/toast.tsx` | ToastPrimitive.Root（146-248行目）で各トーストをレンダリング |

**主要処理フロー**:
- **102行目**: useState([])でtoasts配列を管理
- **145行目**: toasts.map()で全トーストを描画
- **153-165行目**: data-typeに応じた背景色グラデーションを適用
- **172-210行目**: タイトルとメッセージの条件付きレンダリング
- **249行目**: ToastPrimitive.Viewportで表示位置を定義

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

```
useToasts() hook
    |
    +-- ToastContext.useContext()
          |
          +-- { toast, info, error }
                    |
                    +-- _toast() (内部関数)
                    |     |
                    |     +-- upsertToastArray()
                    |           |
                    |           +-- setToasts()
                    |
                    +-- info() (情報通知専用)
                          |
                          +-- _toast({ type: "info" })

ToastProvider
    |
    +-- ToastContext.Provider
    |     +-- value={{ toast, info, error }}
    |
    +-- ToastPrimitive.Provider
          |
          +-- {toasts.map()}
          |     |
          |     +-- ToastPrimitive.Root
          |           +-- ToastPrimitive.Title
          |           +-- ToastPrimitive.Description
          |           +-- ToastPrimitive.Close
          |           +-- ToastPrimitive.Action
          |
          +-- ToastPrimitive.Viewport
```

### データフロー図

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

info(message, options) ───> useCallback内の_toast() ───> setToasts(追加)
                                    |
                                    v
                            upsertToastArray()
                                    |
                                    v
                            [React State: toasts[]]
                                    |
                                    v
                            toasts.map(render) ───────> <ToastPrimitive.Root>
                                                              |
                                                              v
                                                        画面右下に表示
                                                              |
                                                              v
                            onOpenChange(false) ─────> dismiss(id)
                                    |
                                    v
                            setToasts(削除) ───────> 画面から消去
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| toast.tsx | `internal-packages/ui/components/toast.tsx` | ソース | トースト通知コンポーネント・フック |
| index.ts | `internal-packages/ui/components/index.ts` | ソース | コンポーネントエクスポート |
| package.json | `internal-packages/ui/package.json` | 設定 | 依存関係定義（radix-ui, clsx） |
