# 画面設計書 23-ルール管理画面（モバイル）

## 概要

本ドキュメントは、Actual Budgetアプリケーションにおけるモバイル向けルール管理画面の設計仕様を定義するものである。

### 本画面の処理概要

モバイルデバイス向けに最適化された取引自動分類ルールの一覧表示・管理画面である。ユーザーはこの画面でルールの一覧確認、検索、個別編集画面への遷移、削除操作を行うことができる。

**業務上の目的・背景**：モバイル環境でもルール管理機能へのアクセスを提供することで、外出先でのルール確認・編集を可能にする。デスクトップ版と同等の機能を提供しつつ、タッチ操作に適したUIで操作性を確保している。特に銀行同期後に新規取引が追加された際、迅速にルールを調整できることで、カテゴリ分類の精度向上に寄与する。

**画面へのアクセス方法**：モバイルナビゲーションの「More」メニューから「Rules」を選択、またはサイドメニューから `/rules` ルートに遷移することでアクセスする。ナロー画面幅（モバイル）では本コンポーネント `MobileRulesPage` が表示される。

**主要な操作・処理内容**：
1. ルール一覧の表示（リスト形式）
2. ルールのフィルタリング・検索
3. ルール行タップでルール編集画面（`/rules/:id`）へ遷移
4. スワイプまたは長押しによるルール削除
5. ヘッダーの「+」ボタンで新規ルール作成画面へ遷移

**画面遷移**：
- 遷移元：モバイルナビゲーション、サイドメニュー
- 遷移先：ルール編集画面（モバイル） `/rules/:id` または `/rules/new`

**権限による表示制御**：特別な権限制御は設けられていない。予算ファイルにアクセスできるユーザーであればルール管理が可能。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 14 | 取引ルール | 主機能 | モバイルでのルール一覧表示 |
| 60 | レスポンシブUI | 補助機能 | モバイル最適化表示 |

## 画面種別

一覧

## URL/ルーティング

`/rules`

- モバイル（ナロー画面幅）では `MobileRulesPage` コンポーネント
- デスクトップ（ワイド画面幅）では `ManageRulesPage` コンポーネント

## 入出力項目

| 項目名 | 項目ID | データ型 | 入力/出力 | 必須 | 説明 |
|--------|--------|----------|-----------|------|------|
| 検索フィルタ | filter | string | 入力 | 任意 | ルール検索用のテキスト入力 |
| 表示ルールID | visibleRulesParam | string | 入力（URL） | 任意 | カンマ区切りのルールID。特定ルールのみ表示する場合に使用 |

## 表示項目

| 項目名 | データ型 | 説明 |
|--------|----------|------|
| ヘッダータイトル | string | "Rules" と表示 |
| 追加ボタン | button | ヘッダー右側の「+」ボタン（新規ルール作成） |
| 検索フィールド | input | ルール検索用入力フィールド |
| ルール一覧 | list | 条件・アクション情報を含むルール行 |
| ローディング表示 | indicator | データ取得中の表示 |

## イベント仕様

### 1-画面初期表示

画面表示時にルール一覧をサーバーから取得する。

**処理フロー**:
1. `loadRules()` 関数を実行
2. `rules-get` API を呼び出し
3. 取得したルールを `allRules` ステートに設定
4. `visibleRulesParam` が指定されている場合、該当ルールのみフィルタ
5. 完了したスケジュールに紐づくルールをフィルタアウト
6. フィルタ文字列に基づいて表示ルールを絞り込み
7. Undoイベントリスナーを設定

### 2-ルール検索

検索フィールドへの入力でルールをフィルタリングする。

**処理フロー**:
1. `onSearchChange` イベントで `filter` ステートを更新
2. `ruleToString()` 関数でルールを文字列化
3. 正規化した検索文字列との部分一致でフィルタリング

### 3-ルール行タップ

ルール行タップ時にルール編集画面へ遷移する。

**処理フロー**:
1. `handleRulePress` でルールオブジェクトを取得
2. `navigate('/rules/${rule.id}')` で編集画面へ遷移

### 4-ルール削除

ルール行のスワイプまたは削除アクションでルールを削除する。

**処理フロー**:
1. `handleRuleDelete` でルールIDを取得
2. `rule-delete-all` API を呼び出し（単一IDの配列）
3. スケジュール紐づきルールの場合は警告通知を表示
4. 成功時にUndo通知を表示
5. `loadRules()` を再実行して一覧更新

### 5-新規ルール作成

ヘッダーの追加ボタンタップで新規ルール作成画面へ遷移する。

**処理フロー**:
1. `AddRuleButton` コンポーネントのタップイベント
2. `/rules/new` へ遷移

### 6-Undoイベント監視

Undo操作時にルール一覧を再読み込みする。

**処理フロー**:
1. `listen('undo-event', onUndo)` でイベント監視
2. Undoイベント発生時に `loadRules()` を再実行

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| ルール削除 | rules | DELETE | 単一ルールの削除 |

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

#### rules

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| DELETE | - | WHERE id = :id | 指定IDのルールを削除 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| MSG-001 | 成功 | "Rule deleted successfully" | 削除成功時（Undo通知） |
| MSG-002 | 警告 | "This rule could not be deleted because it is linked to a schedule." | スケジュール紐づきルール削除試行時 |
| MSG-003 | エラー | "Failed to delete rule. Please try again." | 削除失敗時 |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| ルール取得失敗 | コンソールエラー出力、空配列を設定、ローディング解除 |
| スケジュール紐づきルールの削除試行 | 警告通知を表示、一覧は更新 |
| 削除API失敗 | エラー通知を表示 |

## 備考

- 本画面はモバイル（ナロー画面幅）専用のコンポーネント
- URLパラメータ `visible-rules` で特定ルールのみ表示可能（支払先画面からの遷移時に使用）
- Undo機能と連携し、削除操作を取り消し可能
- デスクトップ版と異なり、ルール編集は別画面（`MobileRuleEditPage`）で行う

---

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

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

### 推奨読解順序

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

ルールデータの型定義を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | models/index.d.ts | `packages/loot-core/src/types/models/index.d.ts` | RuleEntity型の定義を確認 |

**読解のコツ**: RuleEntityは `id`, `stage`, `conditionsOp`, `conditions`, `actions` 等のプロパティを持つ。

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

画面コンポーネントの全体構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | MobileRulesPage.tsx | `packages/desktop-client/src/components/mobile/rules/MobileRulesPage.tsx` | メインコンポーネント。フック・ステート・イベントハンドラを確認 |

**主要処理フロー**:
1. **30-38行目**: コンポーネント定義とステート初期化（allRules, isLoading, filter）
2. **40-54行目**: フィルタデータ取得（schedules, categories, payees, accounts）
3. **56-65行目**: visibleRulesパラメータによるフィルタリング
4. **67-80行目**: 検索フィルタリングとスケジュール除外
5. **82-94行目**: loadRules関数（rules-get API呼び出し）
6. **101-112行目**: Undoイベントリスナー設定
7. **114-119行目**: handleRulePress（遷移処理）
8. **128-167行目**: handleRuleDelete（削除処理）
9. **169-206行目**: JSXレンダリング

#### Step 3: ルール一覧コンポーネントを理解する

モバイル用ルール一覧の表示を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | RulesList.tsx | `packages/desktop-client/src/components/mobile/rules/RulesList.tsx` | モバイル用ルール一覧コンポーネント |
| 3-2 | AddRuleButton.tsx | `packages/desktop-client/src/components/mobile/rules/AddRuleButton.tsx` | 新規ルール追加ボタン |

#### Step 4: 共通処理を理解する

ルール文字列化と正規化処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | ManageRules.tsx | `packages/desktop-client/src/components/ManageRules.tsx` | ruleToString関数（79-120行目） |
| 4-2 | normalisation.ts | `packages/loot-core/src/shared/normalisation.ts` | getNormalisedString関数 |

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

```
MobileRulesPage
    │
    ├─ useSchedules() / useCategories() / usePayees() / useAccounts()
    │      └─ フィルタ用マスタデータ取得
    │
    ├─ useUrlParam('visible-rules')
    │      └─ URL パラメータからルールID取得
    │
    ├─ loadRules()
    │      └─ send('rules-get')
    │             └─ サーバーAPI呼び出し
    │
    ├─ listen('undo-event')
    │      └─ Undoイベント監視
    │
    ├─ MobilePageHeader
    │      └─ AddRuleButton
    │             └─ navigate('/rules/new')
    │
    ├─ Search コンポーネント
    │
    ├─ RulesList
    │      ├─ handleRulePress()
    │      │      └─ navigate('/rules/:id')
    │      │
    │      └─ handleRuleDelete()
    │             ├─ send('rule-delete-all')
    │             ├─ showUndoNotification()
    │             └─ loadRules()
    │
    └─ ruleToString() (フィルタリング用)
           └─ mapField() / friendlyOp() / mapValue()
```

### データフロー図

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

サーバーDB ───────────▶ send('rules-get') ───────────────▶ allRules state
                             │
visible-rules param ─▶ visibleRules計算 ─────────────────▶ 表示対象ルール
                             │
useSchedules() ──────▶ スケジュール紐づき ───────────────▶ フィルタアウト
                       ルールの除外
                             │
filter入力 ──────────▶ ruleToString() ───────────────────▶ filteredRules
                       + getNormalisedString()
                             │
                             ▼
                       RulesList
                             │
ルール行タップ ──────▶ handleRulePress() ─────────────────▶ /rules/:id へ遷移
                             │
削除操作 ────────────▶ handleRuleDelete() ────────────────▶ Undo通知
                             │
                             └─▶ loadRules() ──────────────▶ 一覧更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| MobileRulesPage.tsx | `packages/desktop-client/src/components/mobile/rules/MobileRulesPage.tsx` | ソース | メインコンポーネント |
| RulesList.tsx | `packages/desktop-client/src/components/mobile/rules/RulesList.tsx` | ソース | ルール一覧コンポーネント |
| AddRuleButton.tsx | `packages/desktop-client/src/components/mobile/rules/AddRuleButton.tsx` | ソース | 追加ボタンコンポーネント |
| ManageRules.tsx | `packages/desktop-client/src/components/ManageRules.tsx` | ソース | ruleToString関数（共有） |
| useUrlParam.ts | `packages/desktop-client/src/hooks/useUrlParam.ts` | ソース | URLパラメータ取得フック |
| useUndo.ts | `packages/desktop-client/src/hooks/useUndo.ts` | ソース | Undo通知表示フック |
| Search.tsx | `packages/desktop-client/src/components/common/Search.tsx` | ソース | 検索入力コンポーネント |
| Page.tsx | `packages/desktop-client/src/components/Page.tsx` | ソース | ページレイアウトコンポーネント |
| FinancesApp.tsx | `packages/desktop-client/src/components/FinancesApp.tsx` | ソース | ルーティング定義（291-292行目） |
| undo.ts | `packages/loot-core/src/platform/client/undo.ts` | ソース | Undo状態管理 |
