# 通知設計書 3-ログアウト失敗

## 概要

本ドキュメントは、Liamアプリケーションにおけるログアウト失敗時のUI通知（トースト）の設計仕様を定義する。ユーザーがログアウト操作を実行した際に、何らかの理由で処理が失敗した場合に表示されるエラー通知について、表示仕様、トリガー条件、処理フローを詳細に記載する。

### 本通知の処理概要

ログアウト失敗通知は、ユーザーがアプリケーションからログアウトしようとした際に、ログアウトAPIへのリクエストが失敗した場合、またはレスポンスが正常でない場合に表示されるエラートースト通知である。

**業務上の目的・背景**：ログアウト処理の失敗をユーザーに明確に通知することで、セッションがまだ有効であることを認識させ、セキュリティリスクを回避させる。特に共有端末やパブリックな環境では、ログアウトの失敗を見逃すとセキュリティ上の問題につながるため、明確なエラー表示が重要である。

**通知の送信タイミング**：ユーザーがヘッダーのユーザードロップダウンメニューから「Logout」を選択し、`/api/logout`へのPOSTリクエストが失敗した場合、または正常なレスポンスが返されなかった場合に即座に表示される。

**通知の受信者**：ログアウト操作を実行したユーザー本人。現在表示している画面上でトースト通知が表示される（ページ遷移は行われない）。

**通知内容の概要**：「Logout failed」というタイトルと「Unable to complete logout. Please try again.」という説明文が、エラー（error）ステータスのトースト通知として表示される。

**期待されるアクション**：ユーザーは再度ログアウトを試みるか、問題が解決しない場合はブラウザのクッキーやキャッシュをクリアする、またはサポートに連絡するなどの対応を行うことが期待される。

## 通知種別

UI通知（トースト）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（API失敗検知時に即座に表示） |
| 優先度 | 高 |
| リトライ | なし（UI表示のため不要、ユーザーが手動で再試行） |

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

ログアウト操作を実行したユーザーの現在のページ上でトースト通知を表示する。ページ遷移は行われない。

## 通知テンプレート

### UI通知の場合

| 項目 | 内容 |
|-----|------|
| タイトル | Logout failed |
| 説明文 | Unable to complete logout. Please try again. |
| ステータス | error |
| 表示位置 | header（ヘッダー下部） |

### 本文テンプレート

```
タイトル: Logout failed
説明: Unable to complete logout. Please try again.
```

### 添付ファイル

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

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| なし | - | - | 固定文言のため変数なし |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| API失敗 | ログアウトAPIリクエストの失敗 | fetchがエラーをスローした場合 | ネットワークエラーなど |
| API失敗 | ログアウトAPIレスポンス異常 | response.okがfalseの場合 | サーバーエラーなど |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| API成功時 | `/api/logout`が正常レスポンスを返した場合は表示しない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[ユーザーがLogoutをクリック] --> B[handleLogout関数実行]
    B --> C[/api/logout POSTリクエスト]
    C --> D{リクエスト結果}
    D -->|fetch失敗| E[result.isErr() = true]
    D -->|fetch成功| F{response.ok確認}
    F -->|ok = false| G[result.value.ok = false]
    F -->|ok = true| H[/login?logout=success にリダイレクト]
    E --> I[エラートースト表示]
    G --> I
    I --> J[終了]
    H --> J
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| なし | - | UI通知のためデータベース参照なし |

### テーブル別参照項目詳細

UI通知のため、データベースからの参照は行わない。

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| なし | - | UI通知のためデータベース更新なし |

#### 送信ログテーブル

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| - | - | - | UI通知のため送信ログなし |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| ネットワークエラー | fetchがエラーをスローした場合 | エラートースト表示 |
| サーバーエラー | response.okがfalseの場合 | エラートースト表示 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし（ユーザー操作による手動リトライ） |
| リトライ間隔 | - |
| リトライ対象エラー | - |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | なし（UI表示のため制限不要） |
| 1日あたり上限 | なし |

### 配信時間帯

特に制限なし。ユーザー操作に応じて即座に表示される。

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

- ログアウト失敗時はセッションがまだ有効な可能性があるため、ユーザーに明確に通知する必要がある
- エラーメッセージには詳細な技術情報を含めず、セキュリティ情報の漏洩を防止
- ユーザーに再試行を促し、セッション管理の安全性を確保

## 備考

- `fromAsyncThrowable`を使用してfetch呼び出しをResult型でラップし、エラーハンドリングを簡潔に実装
- ページ遷移は行わず、現在のページ上でエラートーストを表示
- ユーザーはドロップダウンメニューから再度ログアウトを試みることができる

---

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

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

### 推奨読解順序

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

まず、トースト通知の型定義を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | types.ts | `frontend/packages/ui/src/components/Toast/types.ts` | ToastOptions型（3-7行目）でトースト通知の構造を理解する |

**読解のコツ**: `ToastStatus`が`'success' | 'error' | 'warning' | 'info'`の4種類であり、本通知では`error`を使用する。

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

処理の起点となるログアウトトリガーとエラーハンドリングを特定。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | UserDropdown.tsx | `frontend/apps/app/components/CommonLayout/AppBar/UserDropdown.tsx` | handleLogout関数（48-64行目）でエラー時のトースト表示処理を理解する |

**主要処理フロー**:
1. **49-51行目**: `fromAsyncThrowable`でfetch呼び出しをラップ
2. **53-55行目**: 成功時（`result.isOk() && result.value.ok`）はリダイレクト
3. **58-63行目**: 失敗時にエラートースト表示

#### Step 3: neverthrowの使用方法を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | UserDropdown.tsx | `frontend/apps/app/components/CommonLayout/AppBar/UserDropdown.tsx` | fromAsyncThrowableの使用（2行目インポート、49行目使用）を理解する |

**主要処理フロー**:
- **2行目**: `@liam-hq/neverthrow`からfromAsyncThrowableをインポート
- **49-51行目**: asyncの関数をResult型でラップ
- **53行目**: `result.isOk()`でエラーチェック、`result.value.ok`でレスポンス確認

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Toast.tsx | `frontend/packages/ui/src/components/Toast/Toast.tsx` | Toastコンポーネントの実装（20-47行目）でerrorスタイルを理解する |

**主要処理フロー**:
- **32行目**: `status === 'error' && styles.error`でエラースタイル適用

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

```
UserDropdown.tsx
    │
    └─ handleLogout()
           │
           ├─ fromAsyncThrowable()
           │      │
           │      └─ fetch('/api/logout', { method: 'POST' })
           │
           └─ [失敗時] toast({ title, description, status: 'error' })
                         │
                         └─ ToastContext (Toast.tsx)
```

### データフロー図

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

ユーザー操作             ───▶ handleLogout()              ───▶ エラートースト
(Logoutクリック)               │                               (ページ遷移なし)
                              │
                              └─▶ /api/logout POST
                                    │
                                    └─▶ [失敗] toast({ status: 'error' })
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| UserDropdown.tsx | `frontend/apps/app/components/CommonLayout/AppBar/UserDropdown.tsx` | ソース | ログアウト処理とエラーハンドリング |
| Toast.tsx | `frontend/packages/ui/src/components/Toast/Toast.tsx` | ソース | トースト通知UIコンポーネント |
| useToast.tsx | `frontend/packages/ui/src/components/Toast/useToast.tsx` | ソース | トーストフック |
| types.ts | `frontend/packages/ui/src/components/Toast/types.ts` | ソース | トースト型定義 |
