# 画面設計書 8-カテゴリ商品一覧画面

## 概要

本ドキュメントは、Bagisto eコマースモバイルアプリのカテゴリ商品一覧画面に関する設計書です。特定カテゴリに属する商品を一覧表示し、フィルタリング・ソート機能を提供する画面です。

### 本画面の処理概要

**業務上の目的・背景**：カテゴリ商品一覧画面は、ユーザーが特定のカテゴリに属する商品を効率的に閲覧・検索するための画面です。フィルタリング（属性、価格帯）やソート（価格順、名前順等）機能により、大量の商品から目的の商品を素早く見つけることができます。また、無限スクロールによるページネーションで、大量の商品データをスムーズに読み込みます。

**画面へのアクセス方法**：
- ホーム画面のカテゴリタップ
- ドロワーメニューからのカテゴリ選択
- サブカテゴリ画面からの遷移
- 検索結果からのカテゴリタップ

**主要な操作・処理内容**：
1. カテゴリ別商品一覧の表示
2. 商品フィルタリング（属性、価格帯など）
3. 商品ソート（価格、名前など）
4. 無限スクロールによる追加データ読み込み
5. 商品のカートへの追加
6. 商品のウィッシュリストへの追加/削除
7. グリッド/リスト表示の切り替え
8. 商品詳細画面への遷移

**画面遷移**：
- 遷移元：ホーム画面、サブカテゴリ画面、ドロワーメニュー
- 遷移先：商品詳細画面、サブカテゴリ画面

**権限による表示制御**：
- ウィッシュリスト機能：ログインユーザーのみ
- カートへの追加：全ユーザー可能

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 10 | 商品一覧表示 | 主機能 | カテゴリ別の商品一覧を表示 |
| 13 | 商品フィルタリング | 補助機能 | 属性・価格等による商品絞り込み処理 |
| 14 | 商品ソート | 補助機能 | 価格・名前等による商品並び替え処理 |
| 20 | カートに追加 | API連携 | 一覧から商品をカートに追加 |
| 28 | ウィッシュリストに追加 | API連携 | 一覧から商品をお気に入りに追加 |

## 画面種別

一覧画面

## URL/ルーティング

- ルート名: `categoryScreen`
- パス: `/category/:categorySlug`

## 入出力項目

| 項目名 | 入出力 | データ型 | 必須 | 説明 |
|--------|--------|----------|------|------|
| id | 入力 | String | Yes | カテゴリID |
| title | 入力 | String | No | カテゴリ名（AppBar表示用） |
| categorySlug | 入力 | String | Yes | カテゴリスラッグ |
| image | 入力 | String | No | カテゴリバナー画像URL |
| metaDescription | 入力 | String | No | カテゴリ説明 |
| filters | 入力 | List<Map> | No | 追加フィルタ条件 |

## 表示項目

| 項目名 | データ型 | 説明 |
|--------|----------|------|
| カテゴリ名 | String | AppBarに表示されるカテゴリ名 |
| 商品一覧 | List<NewProducts> | グリッドまたはリスト形式の商品一覧 |
| 商品名 | String | 各商品カードに表示される商品名 |
| 商品価格 | String (HTML) | 価格HTML（セール価格・通常価格） |
| 商品画像 | String (URL) | 商品サムネイル画像 |
| セールバッジ | Widget | セール中の商品に表示されるバッジ |
| 新着バッジ | Widget | 新着商品に表示されるバッジ |
| フィルタアイコン | IconButton | フィルタモーダルを開くボタン |
| ソートアイコン | IconButton | ソートオプションを表示するボタン |
| 表示切替アイコン | IconButton | グリッド/リスト表示切り替え |
| ローディングインジケータ | Widget | 追加データ読み込み中の表示 |

## イベント仕様

### 1-画面初期化イベント（initState）

**トリガー**: 画面がマウントされたタイミング

**処理フロー**:
1. ソート名をリセット（`appStoragePref.setSortName("")`）
2. フィルタ条件の初期化（widget.filtersまたはcategory_idフィルタ）
3. ログイン状態を取得
4. ScrollControllerにリスナーを登録（無限スクロール用）
5. `CategoryBloc`をcontext.readで取得
6. `FilterFetchEvent`を送信してフィルタ属性を取得

### 2-スクロール終端到達イベント

**トリガー**: リスト末端までスクロール

**処理フロー**:
1. `hasMoreData()`でさらにデータがあるか確認
2. ページ番号をインクリメント
3. `HasMoreSubCategoryLoadingEvent`を送信
4. `FetchSubCategoryEvent`を送信して追加データ取得

### 3-フィルタ適用イベント

**トリガー**: フィルタモーダルで条件選択後

**処理フロー**:
1. フィルタ条件をfiltersリストに追加
2. ページ番号を1にリセット
3. `FetchSubCategoryEvent`を送信

### 4-ソート選択イベント

**トリガー**: ソートオプション選択

**処理フロー**:
1. ソート条件をfiltersに追加
2. ページ番号を1にリセット
3. `FetchSubCategoryEvent`を送信

### 5-カートに追加イベント

**トリガー**: 商品カードの「カートに追加」ボタンタップ

**処理フロー**:
1. インターネット接続を確認
2. 商品タイプを確認（simpleまたはvirtual以外は商品詳細画面へ誘導）
3. `AddToCartSubCategoryEvent`を送信
4. 成功/失敗メッセージを表示

### 6-ウィッシュリスト追加/削除イベント

**トリガー**: 商品カードのハートアイコンタップ

**処理フロー**:
1. ログイン状態を確認
2. 未ログインの場合：警告メッセージを表示
3. ログイン済みの場合：
   - ウィッシュリストに存在する場合：`RemoveWishlistEvent`を送信
   - 存在しない場合：`FetchDeleteAddItemCategoryEvent`を送信

### 7-商品詳細遷移イベント

**トリガー**: 商品カードタップ

**処理フロー**:
1. `Navigator.pushNamed(context, productScreen, arguments: PassProductData(...))`を実行
2. 商品詳細画面に遷移

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| カートに追加 | cart_items (サーバー側) | INSERT/UPDATE | 商品をカートに追加 |
| ウィッシュリストに追加 | wishlist_items (サーバー側) | INSERT | 商品をウィッシュリストに追加 |
| ウィッシュリストから削除 | wishlist_items (サーバー側) | DELETE | 商品をウィッシュリストから削除 |

## メッセージ仕様

| メッセージID | メッセージ種別 | 表示条件 | メッセージ内容 |
|-------------|--------------|----------|--------------|
| MSG-001 | 成功 | カート追加成功 | APIから返却されるメッセージ |
| MSG-002 | エラー | カート追加失敗 | エラーメッセージ |
| MSG-003 | 成功 | ウィッシュリスト追加成功 | APIから返却されるメッセージ |
| MSG-004 | エラー | ウィッシュリスト操作失敗 | エラーメッセージ |
| MSG-005 | 警告 | 未ログイン | 「Please login」 |
| MSG-006 | 警告 | オプション選択必要 | 「Please select options」 |
| MSG-007 | エラー | ネットワークエラー | 「Internet connection issue」 |
| MSG-008 | 情報 | データなし | 「No products found」 |

## 例外処理

| 例外ケース | 処理内容 | ユーザーへの影響 |
|-----------|---------|----------------|
| 商品データ取得失敗 | ErrorMessage.errorMsg()を表示 | 商品一覧が表示されない |
| フィルタ取得失敗 | エラー状態をemit | フィルタ機能が使用できない |
| カート追加失敗 | エラーメッセージを表示 | カートに追加されない |
| 追加データ取得失敗 | ローディング状態を解除 | 追加商品が表示されない |

## 備考

- 無限スクロール実装（ScrollControllerでoffset監視）
- ページネーションはサーバーサイド（paginatorInfo使用）
- フィルタ属性は`FilterFetchEvent`で動的に取得
- グリッド/リスト表示の切り替えはisGridフラグで制御
- 商品タイプがsimple/virtual以外の場合は商品詳細画面でオプション選択が必要

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | new_product_data.dart | `lib/screens/home_page/data_model/new_product_data.dart` | 商品データモデルの構造（paginatorInfo含む） |
| 1-2 | get_filter_attribute.dart | `lib/screens/categories_screen/data_model/get_filter_attribute.dart` | フィルタ属性データモデル |

**読解のコツ**: NewProductsModelにはpaginatorInfoが含まれ、totalやperPageなどのページネーション情報がある。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | categories_screen.dart | `lib/screens/categories_screen/categories_screen.dart` | カテゴリ商品一覧画面のメインウィジェット |

**主要処理フロー**:
1. **49-71行目**: `initState()`でフィルタ初期化、FilterFetchEvent送信
2. **73-84行目**: `_setItemScrollListener()`で無限スクロール処理
3. **86-89行目**: `hasMoreData()`でページネーション判定
4. **99-141行目**: `_setSubCategoryData()`でBlocConsumer構築
5. **143-193行目**: `buildContainer()`で各状態に応じたUI構築

#### Step 3: Blocレイヤーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | category_bloc.dart | `lib/screens/categories_screen/bloc/category_bloc.dart` | 状態管理Bloc |
| 3-2 | category_event.dart | `lib/screens/categories_screen/bloc/category_event.dart` | イベント定義 |

**主要処理フロー**:
- `FilterFetchEvent`: フィルタ属性取得
- `FetchSubCategoryEvent`: 商品一覧取得（フィルタ・ページ指定）
- `AddToCartSubCategoryEvent`: カート追加
- `FetchDeleteAddItemCategoryEvent`: ウィッシュリスト追加

#### Step 4: ビューレイヤーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | sub_categories_view.dart | `lib/screens/categories_screen/widget/sub_categories_view.dart` | 商品一覧ビュー |

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

```
SubCategoryScreen (StatefulWidget)
    │
    ├─ initState()
    │      ├─ appStoragePref.setSortName("")
    │      ├─ filters初期化
    │      ├─ ScrollController.addListener()
    │      │      └─ _setItemScrollListener()
    │      │             └─ hasMoreData()確認
    │      │                    └─ FetchSubCategoryEvent (次ページ)
    │      ├─ CategoryBloc (context.read)
    │      └─ CategoryBloc.add(FilterFetchEvent)
    │
    ├─ build()
    │      └─ Scaffold
    │             ├─ CommonAppBar(title)
    │             └─ _setSubCategoryData() → BlocConsumer<CategoryBloc>
    │                    └─ buildContainer()
    │                           ├─ SubCategoriesLoader (ローディング)
    │                           └─ SubCategoriesView
    │
    └─ CategoryBloc
           ├─ FilterFetchEvent → FilterFetchState
           │      └─ FetchSubCategoryEvent自動発行
           ├─ FetchSubCategoryEvent → FetchSubCategoryState
           ├─ AddToCartSubCategoryEvent → AddToCartSubCategoriesState
           ├─ FetchDeleteAddItemCategoryEvent → ウィッシュリスト追加
           └─ RemoveWishlistEvent → ウィッシュリスト削除
```

### データフロー図

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

categoryId ─────────────▶ FilterFetchEvent ─────────────▶ GetFilterAttribute
    │                            │
    │                            ▼
    │                    FetchSubCategoryEvent ─────────▶ NewProductsModel
    │                            │
    │                            ▼
    └───────────────────▶ SubCategoriesView表示

スクロール到達 ─────────▶ ページ番号++ ───────────────────▶ FetchSubCategoryEvent
                                                              │
                                                              ▼
                                                    追加データ取得・結合

フィルタ選択 ───────────▶ filters更新 ───────────────────▶ FetchSubCategoryEvent (page=1)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| categories_screen.dart | `lib/screens/categories_screen/categories_screen.dart` | ソース | カテゴリ商品一覧画面のメインウィジェット |
| sub_categories_view.dart | `lib/screens/categories_screen/widget/sub_categories_view.dart` | ソース | 商品一覧ビュー |
| category_bloc.dart | `lib/screens/categories_screen/bloc/category_bloc.dart` | ソース | 状態管理Bloc |
| category_event.dart | `lib/screens/categories_screen/bloc/category_event.dart` | ソース | イベント定義 |
| category_state.dart | `lib/screens/categories_screen/bloc/category_state.dart` | ソース | 状態定義 |
| category_repository.dart | `lib/screens/categories_screen/bloc/category_repository.dart` | ソース | API通信処理 |
| new_product_data.dart | `lib/screens/home_page/data_model/new_product_data.dart` | ソース | 商品データモデル |
| get_filter_attribute.dart | `lib/screens/categories_screen/data_model/get_filter_attribute.dart` | ソース | フィルタ属性モデル |
| index.dart | `lib/screens/categories_screen/utils/index.dart` | ソース | エクスポート集約 |
| route_constants.dart | `lib/utils/route_constants.dart` | ソース | ルーティング定数 |
