# 画面設計書 8-プロファイル編集画面

## 概要

本ドキュメントは、InvenTree Flutterアプリケーションのプロファイル編集画面（ProfileEditWidget）の画面設計を定義するものである。

### 本画面の処理概要

プロファイル編集画面はサーバープロファイルの新規作成と既存プロファイルの編集機能を提供する。

**業務上の目的・背景**：InvenTreeサーバーに接続するためには、サーバーのURL情報が必要である。本画面はプロファイル名（識別用）とサーバーURL（接続先）を入力・保存する機能を提供し、複数サーバー環境の管理を容易にする。

**画面へのアクセス方法**：サーバー選択画面のAppBar「+」アイコンをタップ（新規作成）、またはプロファイル長押しメニューの「編集」を選択（編集）。

**主要な操作・処理内容**：
1. プロファイル名の入力と検証
2. サーバーURLの入力と検証（http/httpsプロトコルチェック）
3. 入力内容のバリデーション
4. プロファイルの保存（新規作成または更新）

**画面遷移**：
- 遷移元：サーバー選択画面
- 遷移先：サーバー選択画面（保存後に戻る）

**権限による表示制御**：本画面は全ユーザーがアクセス可能。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | ユーザープロファイル管理 | 主機能 | サーバープロファイルの新規作成・編集フォーム処理 |

## 画面種別

入力フォーム画面（新規作成/編集兼用）

## URL/ルーティング

- Flutter Widget：`ProfileEditWidget`（StatefulWidget）
- 状態クラス：`_ProfileEditState`

## 入出力項目

| 項目名 | 入力/出力 | データ型 | 必須 | 説明 |
|--------|----------|----------|------|------|
| profile | 入力 | UserProfile? | 任意 | 編集対象プロファイル（nullの場合は新規作成） |
| name | 入力 | String | 必須 | プロファイル名（識別用） |
| server | 入力 | String | 必須 | サーバーURL（http/https） |

## 表示項目

| 項目名 | データ型 | 説明 | 取得元 |
|--------|----------|------|--------|
| AppBarタイトル | String | 「プロファイル追加」または「プロファイル編集」 | widget.profile == null ? profileAdd : profileEdit |
| プロファイル名入力欄 | TextFormField | プロファイル名の入力フィールド | profile?.name |
| サーバーURL入力欄 | TextFormField | サーバーURLの入力フィールド | profile?.server |

## イベント仕様

### 1-保存ボタン押下

AppBarのチェックアイコンをタップした際にプロファイルを保存する。

**処理フロー**：
1. ユーザーがチェックアイコンをタップ
2. `formKey.currentState!.validate()`でバリデーションを実行
3. バリデーション成功の場合、`formKey.currentState!.save()`で値を保存
4. `widget.profile == null`（新規作成）の場合：
   - `UserProfile(name: name, server: server)`で新規プロファイルを作成
   - `UserProfileDBManager().addProfile(profile)`でDBに追加
5. `widget.profile != null`（編集）の場合：
   - `prf.name = name`, `prf.server = server`で値を更新
   - `UserProfileDBManager().updateProfile(prf)`でDBを更新
6. `Navigator.of(context).pop()`で前画面に戻る

### 2-プロファイル名入力

プロファイル名入力欄にテキストを入力する。

**バリデーションルール**：
- 必須入力（空文字不可）
- 前後の空白は自動でトリム

**処理フロー**：
1. ユーザーがテキストを入力
2. `onSaved`で`name = value?.trim() ?? ""`として保存
3. `validator`で空文字チェック
4. 空の場合は`L10().valueCannotBeEmpty`を返す

### 3-サーバーURL入力

サーバーURL入力欄にテキストを入力する。

**バリデーションルール**：
- 必須入力（空文字不可）
- スペースを含まないこと
- http:// または https:// で始まること
- 有効なホスト名を含むこと

**処理フロー**：
1. ユーザーがテキストを入力
2. `onSaved`で`server = value?.trim() ?? ""`として保存
3. `validator`で以下をチェック：
   - 空文字の場合: `L10().serverEmpty`
   - スペースを含む場合: `L10().invalidHost`
   - Uri.tryParse()がnullまたはホストが空の場合: `L10().invalidHost`
   - スキームがhttp/https以外の場合: `L10().serverStart`

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 新規保存 | profiles（ローカル） | INSERT | 新規プロファイルの追加 |
| 編集保存 | profiles（ローカル） | UPDATE | 既存プロファイルの更新 |

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

#### profiles（ローカルDB - Sembast）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | name | ユーザー入力値（トリム済） | 必須 |
| INSERT/UPDATE | server | ユーザー入力値（トリム済） | 必須 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|--------------|---------|
| MSG-001 | タイトル | L10().profileAdd | 新規作成時（profile == null） |
| MSG-002 | タイトル | L10().profileEdit | 編集時（profile != null） |
| MSG-003 | ラベル | L10().profileName | プロファイル名入力欄ラベル |
| MSG-004 | ラベル | L10().server | サーバーURL入力欄ラベル |
| MSG-005 | ヒント | "http[s]://<server>:<port>" | サーバーURL入力欄ヒント |
| MSG-006 | エラー | L10().valueCannotBeEmpty | プロファイル名が空の場合 |
| MSG-007 | エラー | L10().serverEmpty | サーバーURLが空の場合 |
| MSG-008 | エラー | L10().invalidHost | URLが無効な場合 |
| MSG-009 | エラー | L10().serverStart | スキームがhttp/https以外の場合 |

## 例外処理

| 例外種別 | 発生条件 | 対応処理 |
|---------|---------|---------|
| バリデーションエラー | 入力値が無効 | エラーメッセージを表示、保存処理をスキップ |
| DB追加失敗 | addProfile()が失敗 | サイレント失敗（画面遷移せず） |
| DB更新失敗 | updateProfile()が失敗 | サイレント失敗（画面遷移せず） |

## 備考

- 新規作成と編集は同一Widgetで処理（profileの有無で判定）
- URLのプロトコルチェックはやや緩め（現在はコメントアウトされた箇所あり）
- 入力値は自動的にトリムされる
- フォームはSingleChildScrollViewでラップされている

---

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

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

### 推奨読解順序

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

まず、プロファイルデータの構造を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | user_profile.dart | `lib/user_profile.dart` | UserProfileクラスのコンストラクタとtoJson()を理解する |

**読解のコツ**: UserProfileはname, serverが必須フィールドであり、tokenは後でログイン時に設定される。

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | select_server.dart | `lib/settings/select_server.dart` | ProfileEditWidgetと_ProfileEditStateを理解する（行284-421） |

**主要処理フロー**:
1. **行284-291**: ProfileEditWidgetクラスの定義、profileをコンストラクタで受け取る
2. **行293-300**: _ProfileEditStateクラスの定義、formKey, name, server変数の宣言
3. **行302-336**: `build()`メソッドでScaffoldとFormを構築
4. **行307-335**: AppBarのチェックアイコンで保存処理
5. **行313-327**: 新規/編集の分岐処理
6. **行344-361**: プロファイル名TextFormFieldの定義
7. **行362-413**: サーバーURLTextFormFieldの定義（バリデーション含む）

#### Step 3: バリデーションロジックを理解する

入力値検証の詳細を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | select_server.dart | `lib/settings/select_server.dart` | validator関数の実装（行355-360, 374-411） |

**主要処理フロー**:
- **行355-360**: プロファイル名のバリデーション（空文字チェック）
- **行374-411**: サーバーURLのバリデーション
  - **行375-377**: 空文字チェック
  - **行381-383**: スペースチェック
  - **行391-393**: Uri.tryParseでの有効性チェック
  - **行394-407**: スキームチェック（http/https）

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

```
ProfileEditWidget (select_server.dart)
    |
    +-- _ProfileEditState
            |
            +-- build()
                    |
                    +-- Scaffold
                            |
                            +-- AppBar
                            |       +-- IconButton (チェック)
                            |               +-- formKey.currentState!.validate()
                            |               +-- formKey.currentState!.save()
                            |               |
                            |               +-- [profile == null]
                            |               |       +-- UserProfile(name, server)
                            |               |       +-- UserProfileDBManager().addProfile()
                            |               |
                            |               +-- [profile != null]
                            |               |       +-- prf.name = name
                            |               |       +-- prf.server = server
                            |               |       +-- UserProfileDBManager().updateProfile()
                            |               |
                            |               +-- Navigator.of(context).pop()
                            |
                            +-- Form
                                    |
                                    +-- SingleChildScrollView
                                            |
                                            +-- Column
                                                    |
                                                    +-- TextFormField (プロファイル名)
                                                    |       +-- validator: 空文字チェック
                                                    |       +-- onSaved: name = value
                                                    |
                                                    +-- TextFormField (サーバーURL)
                                                            +-- validator: URL検証
                                                            +-- onSaved: server = value
```

### データフロー図

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

profile (既存/null)     -->  build()                          -->  フォーム初期値設定
                                |
                                v
ユーザー入力             -->  TextFormField.onChanged         -->  リアルタイム入力
(name, server)                  |
                                v
[保存ボタン]             -->  validate()                      -->  バリデーション
                                |
        +-------------------+---+
        |                       |
        v                       v
[成功]                  [失敗]
save()                  エラーメッセージ表示
        |
        v
[profile == null]       [profile != null]
        |                       |
        v                       v
addProfile()            updateProfile()
        |                       |
        +----------+------------+
                   |
                   v
           Navigator.pop()                                    -->  前画面へ戻る
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| select_server.dart | `lib/settings/select_server.dart` | ソース | ProfileEditWidgetの定義（行284-421） |
| user_profile.dart | `lib/user_profile.dart` | ソース | UserProfile/UserProfileDBManagerクラス |
| app_colors.dart | `lib/app_colors.dart` | ソース | COLOR_APP_BARの定義 |
| l10.dart | `lib/l10.dart` | ソース | 多言語対応 |
