# 画面設計書 4-通知画面

## 概要

本ドキュメントは、InvenTree Flutterアプリケーションの通知画面（NotificationWidget）の画面設計を定義するものである。

### 本画面の処理概要

通知画面はサーバーからの通知一覧を表示し、ユーザーが通知を確認・既読化できる機能を提供する。

**業務上の目的・背景**：在庫管理システムにおいて、在庫切れ警告、発注承認依頼、出荷準備完了など、業務上重要なイベントをユーザーに通知することは運用効率向上に不可欠である。本画面はサーバーから送信された通知をリアルタイムで確認でき、既読処理により確認済みの通知を管理できる。

**画面へのアクセス方法**：ドロワーメニューの「通知」項目をタップ。

**主要な操作・処理内容**：
1. 未読通知一覧の取得と表示（サーバーAPIからの取得）
2. 通知の詳細確認（タイトルとメッセージの表示）
3. 通知の既読化（ブックマークアイコンタップで既読処理）
4. 画面のプルダウンリフレッシュ（最新通知の再取得）

**画面遷移**：
- 遷移元：ドロワーメニュー
- 遷移先：なし（本画面で完結）

**権限による表示制御**：サーバー接続状態に依存。未接続時はアクセス不可。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 51 | 通知一覧表示 | 主機能 | サーバーからの通知一覧を取得・表示 |
| 52 | 通知既読化 | 補助機能 | 通知の既読ボタン押下時に既読化処理を実行 |

## 画面種別

一覧画面（リスト表示型）

## URL/ルーティング

- Flutter Widget：`NotificationWidget`（StatefulWidget）
- 状態クラス：`_NotificationState`（RefreshableStateを継承）

## 入出力項目

| 項目名 | 入力/出力 | データ型 | 必須 | 説明 |
|--------|----------|----------|------|------|
| なし | - | - | - | 本画面は入力パラメータを持たない |

## 表示項目

| 項目名 | データ型 | 説明 | 取得元 |
|--------|----------|------|--------|
| 通知ヘッダー | ListTile | 通知件数とタイトルを表示 | notifications.length |
| 通知タイトル | String | 各通知のタイトル | notification.name |
| 通知メッセージ | String | 各通知の詳細メッセージ | notification.message |
| 既読ボタン | IconButton | 通知を既読にするボタン | TablerIcons.bookmark |
| 空の通知メッセージ | Text | 通知がない場合のメッセージ | L10().notificationsEmpty |

## イベント仕様

### 1-画面表示時のデータ取得

画面が表示された際に通知一覧を取得する。

**処理フロー**：
1. `_NotificationState`の`initState()`が呼び出される
2. `onBuild()`経由で`refresh()`が呼び出される
3. `request()`メソッドで`InvenTreeNotification().list()`を実行
4. 取得した通知リストを`notifications`変数に格納
5. `setState()`でUIを更新

### 2-通知既読化

通知の既読ボタン（ブックマークアイコン）をタップした際に既読処理を実行する。

**処理フロー**：
1. ユーザーが既読ボタンをタップ
2. `dismissNotification()`メソッドが呼び出される
3. `isDismissing`フラグをtrueに設定（二重タップ防止）
4. `notification.dismiss()`で既読APIを呼び出し
5. API v82以上の場合は`update(values: {"read": "true"})`を使用
6. API v82未満の場合は`POST ${url}read/`を使用
7. `refresh()`で通知一覧を再取得
8. `isDismissing`フラグをfalseに戻す

### 3-プルダウンリフレッシュ

画面をプルダウンした際に通知一覧を再取得する。

**処理フロー**：
1. ユーザーが画面をプルダウン
2. RefreshIndicatorの`onRefresh`が呼び出される
3. `refresh()`メソッドで`request()`を実行
4. 最新の通知一覧を取得してUIを更新

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面表示時 | notifications（サーバー） | SELECT | 未読通知一覧の取得 |
| 既読化 | notifications（サーバー） | UPDATE | 通知の既読フラグを更新 |

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

#### notifications（サーバーAPI）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | - | read=false | 未読通知のみ取得 |
| UPDATE | read | true | 既読化処理 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|--------------|---------|
| MSG-001 | タイトル | L10().notifications | AppBarタイトルとヘッダータイル |
| MSG-002 | 情報 | L10().notificationsEmpty | 通知が0件の時 |

## 例外処理

| 例外種別 | 発生条件 | 対応処理 |
|---------|---------|---------|
| 通知取得失敗 | APIリクエスト失敗 | 空のリストを表示 |
| 既読化失敗 | dismiss()が失敗 | isDismissingフラグをリセット、エラー表示なし |
| ウィジェット未マウント | 処理中に画面が閉じられた | mountedチェックで処理をスキップ |

## 備考

- 通知はデフォルトで未読（read=false）のみを取得する
- 既読化処理中はisDismissingフラグで二重タップを防止
- API v82以上とそれ未満で既読化のエンドポイントが異なる
- RefreshableStateを継承しているため、プルダウンリフレッシュが自動的に有効

---

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

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

### 推奨読解順序

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

まず、通知データの構造を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | notification.dart | `lib/inventree/notification.dart` | InvenTreeNotificationクラスの構造（name, message, dismiss()）を理解する |

**読解のコツ**: InvenTreeNotificationはInvenTreeModelを継承しており、標準的なCRUD操作が使用可能。dismiss()メソッドでAPIバージョンに応じた既読化処理を行う。

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

処理の起点となるファイル・関数を特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | notifications.dart | `lib/widget/notifications.dart` | NotificationWidgetと_NotificationStateの構造を理解する |

**主要処理フロー**:
1. **行10-13**: NotificationWidgetクラスの定義（StatefulWidget）
2. **行15-16**: _NotificationStateクラスのコンストラクタ
3. **行18**: `notifications`リストの定義
4. **行20**: `isDismissing`フラグの定義
5. **行23**: `getAppBarTitle()`でAppBarタイトルを返す
6. **行25-36**: `request()`で通知一覧を取得
7. **行41-62**: `dismissNotification()`で既読化処理
8. **行67-100**: `getTiles()`でUIを構築

#### Step 3: 通知モデルを理解する

通知データの取得・更新を担当するモデルを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | notification.dart | `lib/inventree/notification.dart` | InvenTreeNotificationクラスの詳細 |

**主要処理フロー**:
- **行19**: `URL => "notifications/"`でAPIエンドポイントを定義
- **行22-25**: `defaultListFilters()`でデフォルトフィルタ（read=false）を設定
- **行27**: `message`ゲッターでメッセージを取得
- **行40-47**: `dismiss()`でAPIバージョンに応じた既読化

#### Step 4: RefreshableStateを理解する

基底クラスの機能を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | refreshable_state.dart | `lib/widget/refreshable_state.dart` | RefreshableStateの構造（refresh, request, getTiles）を理解する |

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

```
NotificationWidget (notifications.dart)
    |
    +-- _NotificationState
            |
            +-- initState()
            |       +-- onBuild()
            |               +-- refresh()
            |                       +-- request()
            |                               +-- InvenTreeNotification().list()
            |
            +-- getTiles()
            |       |
            |       +-- ListTile (ヘッダー)
            |       |
            |       +-- ListTile x N (各通知)
            |               +-- IconButton (既読ボタン)
            |                       +-- dismissNotification()
            |
            +-- dismissNotification()
                    |
                    +-- setState(isDismissing = true)
                    +-- notification.dismiss()
                    |       |
                    |       +-- [API >= 82] update({"read": "true"})
                    |       +-- [API < 82] api.post("${url}read/")
                    |
                    +-- refresh()
                    +-- setState(isDismissing = false)
```

### データフロー図

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

画面表示                -->  request()                       -->  通知一覧取得
                                |
                                v
InvenTreeNotification   -->  list()                          -->  APIリクエスト
.list()                         |
                                v
                        notifications = results              -->  状態更新
                                |
                                v
                        getTiles()                           -->  UI描画
                                |
                                v
[既読ボタンタップ]       -->  dismissNotification()           -->  既読処理
                                |
                                v
notification.dismiss()  -->  API更新                         -->  サーバー更新
                                |
                                v
                        refresh()                            -->  一覧再取得
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| notifications.dart | `lib/widget/notifications.dart` | ソース | 通知画面のメインWidget |
| notification.dart | `lib/inventree/notification.dart` | ソース | 通知データモデル |
| model.dart | `lib/inventree/model.dart` | ソース | InvenTreeModel基底クラス |
| refreshable_state.dart | `lib/widget/refreshable_state.dart` | ソース | RefreshableState基底クラス |
| api.dart | `lib/api.dart` | ソース | API通信・バージョン判定 |
| l10.dart | `lib/l10.dart` | ソース | 多言語対応 |
