# 機能設計書 63-タグ管理

## 概要

本ドキュメントは、Actualアプリケーションにおけるタグ管理機能の設計仕様を記載したものである。取引にタグを付けて分類する機能を提供する。

### 本機能の処理概要

**業務上の目的・背景**：カテゴリだけでは取引の分類が不十分な場合がある。タグ機能は、カテゴリとは別の切り口で取引を分類・検索できるようにする。例えば、「旅行」「プロジェクトA」「確定申告対象」といったタグを付けることで、複数のカテゴリにまたがる取引を横断的に管理できる。タグには色と説明を設定可能で、視覚的な識別も容易。

**機能の利用シーン**：
- 特定のイベント（旅行、引っ越し）に関連する支出を追跡したい場合
- 確定申告用の経費を識別したい場合
- プロジェクト別のコスト管理をしたい場合
- 複数カテゴリにまたがる支出をグループ化したい場合

**主要な処理内容**：
1. タグの作成（名前、色、説明）
2. タグの編集（色、説明の変更）
3. タグの削除（単一・複数選択）
4. 既存タグの検索・抽出（取引ノートからの自動検出）
5. タグのフィルタリング（名前による絞り込み）

**関連システム・外部連携**：
- Redux Store（tagsSlice）によるグローバル状態管理
- バックエンドAPI（tags-get, tags-create, tags-update, tags-delete等）

**権限による制御**：特別な権限制御はなく、予算ファイルにアクセス可能なユーザーが利用可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 29 | タグ管理画面 | 主画面 | 取引タグの一覧表示・作成・編集・削除 |

## 機能種別

CRUD操作

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| tag | string | Yes | タグ名 | 空文字不可、#で始まらない |
| color | string | No | タグの色（16進数） | 有効なカラーコード |
| description | string | No | タグの説明 | - |
| id | string | Yes（更新時） | タグID | 有効なUUID |

### 入力データソース

- 画面入力（ManageTags, TagCreationRow, TagEditorコンポーネント経由）
- Redux Store（tagsSlice）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | string | 生成されたタグID |
| tag | string | タグ名 |
| color | string | タグの色 |
| description | string | タグの説明 |

### 出力先

- Redux Store（tags状態）
- ローカルストレージ（tagsテーブル）
- 画面表示（TagsList, TagRowコンポーネント）

## 処理フロー

### 処理シーケンス

```
1. タグ管理画面の表示
   └─ useTags Hookでタグ一覧を取得

2. 新規タグ作成
   └─ TagCreationRowで入力、createTag actionをdispatch

3. タグの編集（色変更）
   └─ TagEditorのColorPickerで色選択、updateTag actionをdispatch

4. タグの削除
   └─ 選択したタグに対しdeleteAllTags actionをdispatch

5. 既存タグの検索
   └─ findTags actionで取引ノートからタグを自動検出
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[ManageTags表示]
    B --> C[useTags()でタグ取得]
    C --> D{操作選択}
    D -->|Add New| E[TagCreationRow表示]
    E --> F[タグ名・色・説明入力]
    F --> G[createTag dispatch]
    G --> H[tags-create API呼び出し]
    H --> I[Redux Store更新]
    I --> B
    D -->|Find Existing| J[findTags dispatch]
    J --> K[tags-find API呼び出し]
    K --> I
    D -->|色変更| L[TagEditor表示]
    L --> M[ColorPickerで色選択]
    M --> N[updateTag dispatch]
    N --> O[tags-update API呼び出し]
    O --> I
    D -->|削除| P[選択タグをdeleteAllTags]
    P --> Q[tags-delete-all API呼び出し]
    Q --> I
    D -->|終了| R[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-63-001 | タグ名フォーマット | 表示時はタグ名の前に#を付与 | 表示時 |
| BR-63-002 | フィルタリング | 正規化された文字列で部分一致検索 | フィルタ入力時 |
| BR-63-003 | 選択状態管理 | useSelectedフックで複数選択を管理 | タグ選択時 |
| BR-63-004 | dirty状態管理 | CRUD操作後はisTagsDirty=trueに設定 | 操作完了時 |
| BR-63-005 | 自動タグ検出 | 取引ノートから#で始まる文字列をタグとして検出 | findTags実行時 |

### 計算ロジック

特になし

## データベース操作仕様

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| タグ作成 | tags | INSERT | 新規タグの作成 |
| タグ更新 | tags | UPDATE | タグの色・説明の更新 |
| タグ削除 | tags | DELETE | タグの削除 |
| タグ一覧取得 | tags | SELECT | 全タグの取得 |
| タグ検索 | tags, notes | SELECT | 取引ノートからタグを検出 |

### テーブル別操作詳細

#### tags

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | UUID生成 | 主キー |
| INSERT | tag | タグ名 | 必須 |
| INSERT | color | 16進数カラーコード | 省略可 |
| INSERT | description | 説明文 | 省略可 |
| UPDATE | color | 新しい色 | - |
| UPDATE | description | 新しい説明 | - |
| DELETE | id | 削除対象ID | - |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ERR-63-001 | 取得失敗 | tags-get API失敗 | isTagsLoading=false、エラーログ出力 |
| ERR-63-002 | 作成失敗 | tags-create API失敗 | エラー通知 |
| ERR-63-003 | 更新失敗 | tags-update API失敗 | エラー通知 |
| ERR-63-004 | 削除失敗 | tags-delete-all API失敗 | エラー通知 |

### リトライ仕様

Redux Thunkのconditionオプションにより、ロード中は重複リクエストを抑制。

## トランザクション仕様

各API呼び出しは個別のトランザクションで処理される。削除操作（deleteAllTags）は複数タグを一括で削除。

## パフォーマンス要件

- タグ一覧取得: 200ms以内
- タグ作成/更新/削除: 500ms以内
- フィルタリング: 100ms以内（クライアントサイド処理）

## セキュリティ考慮事項

- タグデータはローカルファイルに保存され、E2E暗号化の対象となる
- クラウド同期時は暗号化されて転送される

## 備考

- タグ名は#なしで保存され、表示時に#を付与
- getNormalisedStringで正規化された文字列でフィルタリング

---

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

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

### 推奨読解順序

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

まず、タグエンティティの型定義とRedux状態を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | tagsSlice.ts | `packages/desktop-client/src/tags/tagsSlice.ts` | TagsState型、initialState定義 |
| 1-2 | models.ts | `loot-core/types/models/index.ts` | TagEntity型の定義 |

**読解のコツ**: TagsStateの4つのフラグ（tags, isTagsLoading, isTagsLoaded, isTagsDirty）の状態遷移を理解する。

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

処理の起点となるコンポーネントを特定。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ManageTags.tsx | `packages/desktop-client/src/components/tags/ManageTags.tsx` | メインコンポーネント、タグ一覧表示 |

**主要処理フロー**:
1. **28行目**: ManageTags関数コンポーネント定義
2. **31-34行目**: useState、useTags、useDispatchの初期化
3. **36-42行目**: useMemoでフィルタリング処理
4. **44行目**: useSelectedで複数選択管理
5. **46-49行目**: onDeleteSelectedで選択タグ削除
6. **74-77行目**: Add Newボタン、findTagsボタン
7. **95-104行目**: TagsListでタグ一覧表示

#### Step 3: Redux Sliceを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | tagsSlice.ts | `packages/desktop-client/src/tags/tagsSlice.ts` | Slice定義、AsyncThunk定義 |

**主要処理フロー（tagsSlice.ts）**:
- **11-16行目**: TagsState型定義
- **18-23行目**: initialState定義
- **25-77行目**: createSliceによるSlice定義とextraReducers
- **79-91行目**: getTags AsyncThunk（conditionによる重複リクエスト抑制）
- **93-99行目**: reloadTags AsyncThunk
- **101-107行目**: createTag AsyncThunk
- **109-115行目**: deleteTag AsyncThunk
- **117-123行目**: deleteAllTags AsyncThunk
- **125-131行目**: updateTag AsyncThunk
- **133-139行目**: findTags AsyncThunk
- **156-165行目**: _loadTags, _markTagsDirtyヘルパー関数

#### Step 4: 子コンポーネントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | TagCreationRow.tsx | `packages/desktop-client/src/components/tags/TagCreationRow.tsx` | 新規タグ作成UI |
| 4-2 | TagEditor.tsx | `packages/desktop-client/src/components/tags/TagEditor.tsx` | タグ色編集（ColorPicker） |
| 4-3 | TagRow.tsx | `packages/desktop-client/src/components/tags/TagRow.tsx` | タグ行表示 |
| 4-4 | TagsList.tsx | `packages/desktop-client/src/components/tags/TagsList.tsx` | タグ一覧リスト |

**主要処理フロー（TagEditor.tsx）**:
- **17-21行目**: TagEditorコンポーネント
- **24-28行目**: ColorPickerでonChangeからupdateTag dispatch

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

```
ManageTags (メインコンポーネント)
    │
    ├─ useTags() ──────────────────────┐
    │                                   │
    ├─ useDispatch()                    │
    │      │                            │
    │      ├─ createTag ───────▶ tags-create API
    │      ├─ deleteAllTags ───▶ tags-delete-all API
    │      ├─ findTags ────────▶ tags-find API
    │      └─ updateTag ───────▶ tags-update API
    │                                   │
    ├─ useSelected('manage-tags')       │
    │                                   │
    ├─ TagsHeader                       │
    │                                   │
    ├─ TagCreationRow                   │
    │      └─ createTag dispatch        │
    │                                   │
    └─ TagsList                         │
           └─ TagRow                    │
                  └─ TagEditor          │
                         └─ updateTag dispatch
                                        │
                                        ▼
                              Redux Store (tags slice)
                                        │
                                        ▼
                              tagsSlice.ts
                                        │
                    ┌───────────────────┼───────────────────┐
                    │                   │                   │
                    ▼                   ▼                   ▼
              _loadTags           _markTagsDirty      extraReducers
```

### データフロー図

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

ユーザー操作 ─────────┐
(タグ作成/編集/削除)  │
                     ▼
              ┌─────────────────┐
              │ dispatch(action)│
              │ - createTag     │
              │ - updateTag     │
              │ - deleteAllTags │
              │ - findTags      │
              └────────┬────────┘
                       │
                       ▼
              ┌─────────────────┐
              │ AsyncThunk      │
              │ send('tags-xxx')│
              └────────┬────────┘
                       │
                       ▼
              ┌─────────────────┐
              │ Backend API     │
              │ tags-get        │
              │ tags-create     │
              │ tags-update     │
              │ tags-delete-all │
              │ tags-find       │
              └────────┬────────┘
                       │
                       ▼
              ┌─────────────────┐
              │ Redux Store更新 │
              │ extraReducers   │
              │ - _loadTags     │
              │ - _markTagsDirty│
              └────────┬────────┘
                       │
    ┌──────────────────┴──────────────────┐
    ▼                                      ▼
┌──────────────────┐              ┌──────────────────┐
│ useTags()        │              │ DB保存           │
│ → tags配列取得   │              │ tagsテーブル     │
└────────┬─────────┘              └──────────────────┘
         │
         ▼
┌──────────────────┐
│ 画面再レンダリング │
│ ManageTags       │
│ TagsList         │
└──────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ManageTags.tsx | `packages/desktop-client/src/components/tags/ManageTags.tsx` | ソース | メインコンポーネント |
| TagsHeader.tsx | `packages/desktop-client/src/components/tags/TagsHeader.tsx` | ソース | ヘッダー行 |
| TagCreationRow.tsx | `packages/desktop-client/src/components/tags/TagCreationRow.tsx` | ソース | 新規作成行 |
| TagsList.tsx | `packages/desktop-client/src/components/tags/TagsList.tsx` | ソース | タグ一覧リスト |
| TagRow.tsx | `packages/desktop-client/src/components/tags/TagRow.tsx` | ソース | タグ行 |
| TagEditor.tsx | `packages/desktop-client/src/components/tags/TagEditor.tsx` | ソース | タグ編集（色） |
| tagsSlice.ts | `packages/desktop-client/src/tags/tagsSlice.ts` | ソース | Redux Slice |
| useTags.ts | `packages/desktop-client/src/hooks/useTags.ts` | ソース | タグ取得Hook |
| ManageTagsPage.tsx | `packages/desktop-client/src/components/tags/ManageTagsPage.tsx` | ソース | ページラッパー |
