# 画面設計書 21-部品一覧画面

## 概要

本ドキュメントは、InvenTree Flutterアプリケーションにおける「部品一覧画面」の設計仕様を定義する。部品データをページネーション付きで一覧表示し、検索・フィルタリング・ソート機能を提供する画面である。

### 本画面の処理概要

部品一覧画面は、InvenTreeサーバーに登録されている部品（Part）をページネーション付きの一覧形式で表示する画面である。ユーザーはこの画面から部品を検索・フィルタリングし、目的の部品を素早く見つけて詳細画面へ遷移できる。

**業務上の目的・背景**：製造業やハードウェア開発において、数百〜数千に及ぶ部品を効率的に管理・検索することは不可欠である。本画面は、大量の部品データから目的の部品を迅速に発見するためのインターフェースを提供し、在庫確認や発注判断などの業務効率化に貢献する。ページネーションにより大量データでもパフォーマンスを維持し、フィルタリング機能により条件に合った部品のみを表示できる。

**画面へのアクセス方法**：
- 部品カテゴリ画面の「部品」タブから直接アクセス
- 検索画面から部品検索結果として遷移
- 部品詳細画面からバリアント一覧として遷移

**主要な操作・処理内容**：
1. 部品一覧のページネーション表示（25件ずつ取得）
2. 検索ボックスによるインクリメンタルサーチ（500msデバウンス）
3. フィルタリング（アクティブ、アセンブリ、コンポーネント、在庫あり等）
4. ソート（名前、在庫数、IPN順）
5. 部品タップで詳細画面へ遷移

**画面遷移**：
- 遷移元：部品カテゴリ画面、検索画面、部品詳細画面（バリアント一覧）
- 遷移先：部品詳細画面（PartDetailWidget）

**権限による表示制御**：部品の閲覧権限（part）がない場合、この画面にはアクセスできない。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 4 | パーツ一覧表示 | 主機能 | パーツのページネーション付き一覧表示 |
| 5 | パーツ詳細表示 | 遷移先機能 | パーツ選択時にパーツ詳細画面へ遷移 |

## 画面種別

一覧

## URL/ルーティング

Flutter Navigator によるプッシュ遷移。明示的なURLパスはなく、`PartList` ウィジェットのインスタンス化時に `filters` パラメータを渡して表示内容を制御する。

## 入出力項目

| 項目名 | 入出力 | 型 | 必須 | 説明 |
|--------|--------|------|------|------|
| filters | 入力 | Map<String, String> | No | 一覧表示時のフィルタ条件 |
| title | 入力 | String | No | 画面タイトル（デフォルト: "Parts"） |
| searchTerm | 入力 | String | No | 検索キーワード |

## 表示項目

| 項目名 | 表示位置 | データ型 | 説明 |
|--------|----------|----------|------|
| 部品サムネイル | ListTile.leading | Image | 部品の画像（サムネイル） |
| 部品名 | ListTile.title | String | 部品のフルネーム（IPN | 名前 | リビジョン） |
| 説明 | ListTile.subtitle | String | 部品の説明文 |
| 在庫数 | ListTile.trailing | String | 利用可能在庫 / 総在庫（単位付き） |
| 検索結果件数 | タイトル下 | String | "Results: N" 形式で表示 |

## イベント仕様

### 1-部品アイテムタップ

部品リストの各アイテムをタップすると、該当部品の詳細画面（PartDetailWidget）へ遷移する。

**処理フロー**：
1. `onTap` コールバックが発火
2. `part.goToDetailPage(context)` を呼び出し
3. `Navigator.push` で `PartDetailWidget(this)` へ遷移

### 2-検索ボックス入力

検索ボックスにテキストを入力すると、500msのデバウンス後にサーバーへ検索クエリを送信する。

**処理フロー**：
1. `TextFormField.onChanged` でテキスト変更を検知
2. `updateSearchTerm()` メソッドを呼び出し
3. 500msタイマー後に `_pagingController.refresh()` で再検索

### 3-フィルタリング設定

フィルタアイコンをタップすると、ソートとフィルタリングのオプションダイアログが表示される。

**処理フロー**：
1. フィルタアイコン（`Icons.filter_alt`）をタップ
2. `_setOrderingOptions(context)` を呼び出し
3. `launchApiForm` でオプションダイアログを表示
4. 設定保存後に `_pagingController.refresh()` で一覧を再取得

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

画面を下に引っ張ると、データをリフレッシュする。

**処理フロー**：
1. `RefreshIndicator.onRefresh` が発火
2. `_pagingController.refresh()` を呼び出し
3. ページ0から再取得

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 一覧表示 | part | SELECT | 部品データのページネーション取得 |
| 検索 | part | SELECT | 検索条件に合致する部品の取得 |

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

#### Part テーブル（読み取りのみ）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | pk, name, description, image, thumbnail, in_stock, unallocated_stock, units, IPN, revision | フィルタ条件とページネーションパラメータに基づく | APIエンドポイント: /api/part/ |

## メッセージ仕様

| メッセージID | メッセージ種別 | 表示条件 | メッセージ内容 |
|-------------|---------------|----------|---------------|
| MSG_NO_RESULTS | 情報 | 検索結果が0件 | "No results" |
| MSG_RESULTS | 情報 | 検索結果表示 | "Results: {件数}" |

## 例外処理

| 例外条件 | 処理内容 | ユーザーへの表示 |
|---------|---------|-----------------|
| APIエラー | エラーをSentryに報告 | 一覧表示が停止、エラー状態を表示 |
| ネットワークエラー | 再試行可能な状態を維持 | プルダウンでリフレッシュ可能 |
| 権限不足 | ウィジェット非表示 | 画面にアクセスできない |

## 備考

- ページサイズは25件固定（`_pageSize = 25`）
- フィルタ・ソート設定は `InvenTreeSettingsManager` によりローカルに永続化される
- 検索のデバウンス時間は500ms

---

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

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

### 推奨読解順序

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

まず、部品データを表す `InvenTreePart` モデルクラスを理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | part.dart | `lib/inventree/part.dart` | InvenTreePartクラス（138-459行）の構造。fullname, stockString(), thumbnailなどのゲッターメソッド |
| 1-2 | model.dart | `lib/inventree/model.dart` | InvenTreeModelの基底クラス。listPaginated()メソッドの実装 |

**読解のコツ**: DartのgetterメソッドはJavaScript/TypeScriptのプロパティに相当する。`String get fullname` は `fullname` プロパティとしてアクセスできる。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | part_list.dart | `lib/widget/part/part_list.dart` | PartListウィジェット（13-40行）とPaginatedPartList（42-133行）の関係 |

**主要処理フロー**:
1. **13-22行**: `PartList` StatefulWidgetの定義。filtersとtitleをコンストラクタで受け取る
2. **37-39行**: `getBody()` で `PaginatedPartList` を返す
3. **42-51行**: `PaginatedPartList` がStatefulWidgetとして定義
4. **53-133行**: `_PaginatedPartListState` で実際のページネーションロジックを実装

#### Step 3: ページネーション機構を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | paginator.dart | `lib/widget/paginator.dart` | PaginatedSearchStateの抽象クラス（34-569行）。_fetchPage(), buildItem()の仕組み |

**主要処理フロー**:
- **37行**: `_pageSize = 25` でページサイズを定義
- **283-295行**: `initState()` でページリクエストリスナーを設定
- **314-383行**: `_fetchPage()` でサーバーからデータを取得
- **386-415行**: `updateSearchTerm()` でデバウンス付き検索を実装

#### Step 4: リストアイテム構築を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | part_list.dart | `lib/widget/part/part_list.dart` | buildItem()メソッド（119-132行）。ListTileの構築方法 |

**主要処理フロー**:
- **120行**: InvenTreeModelをInvenTreePartにキャスト
- **122-131行**: ListTileを構築。title, subtitle, trailing, leadingを設定
- **128-130行**: onTapで `part.goToDetailPage(context)` を呼び出し

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

```
PartList (StatefulWidget)
    │
    └─ _PartListState (RefreshableState)
           │
           └─ getBody() → PaginatedPartList
                              │
                              └─ _PaginatedPartListState (PaginatedSearchState)
                                     │
                                     ├─ requestPage() → InvenTreePart().listPaginated()
                                     │                      │
                                     │                      └─ API GET /api/part/
                                     │
                                     └─ buildItem() → ListTile
                                                        │
                                                        └─ onTap → part.goToDetailPage()
                                                                      │
                                                                      └─ PartDetailWidget
```

### データフロー図

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

filters (Map)    ───▶ PaginatedPartList              ───▶ ListView
                         │
                         ├─ requestPage()
                         │      │
                         │      └─ API: /api/part/?limit=25&offset=N
                         │             │
                         │             └─ InvenTreePageResponse
                         │
                         └─ buildItem()
                                │
                                └─ InvenTreePart → ListTile
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| part_list.dart | `lib/widget/part/part_list.dart` | ソース | 部品一覧画面のメインウィジェット |
| paginator.dart | `lib/widget/paginator.dart` | ソース | ページネーション共通基盤クラス |
| part.dart | `lib/inventree/part.dart` | ソース | 部品データモデル |
| model.dart | `lib/inventree/model.dart` | ソース | モデル基底クラス（API通信含む） |
| api.dart | `lib/api.dart` | ソース | API通信クライアント |
| refreshable_state.dart | `lib/widget/refreshable_state.dart` | ソース | リフレッシュ可能なState基底クラス |
| link_icon.dart | `lib/widget/link_icon.dart` | ソース | LargeTextウィジェット（在庫数表示用） |
| l10.dart | `lib/l10.dart` | ソース | 多言語対応（ローカライゼーション） |
