# 通知設計書 54-ダッシュボードインポートエラー

## 概要

本ドキュメントは、ダッシュボード設定をJSONファイルからインポートした際に、バリデーションエラーや内部エラーが発生した場合に表示されるエラー通知の設計書です。

### 本通知の処理概要

ダッシュボードのインポート操作中にバリデーションエラー（validation-error）またはその他の内部エラーが発生した場合に、ユーザーに対してエラーを通知します。

**業務上の目的・背景**：Actual Budget のダッシュボードインポート機能では、JSONファイルの構造が厳密にバリデーションされます。インポートファイルが必要なプロパティを欠いている場合、不正なデータ型が含まれている場合、またはサポートされていないウィジェットタイプが指定されている場合、システムはエラーを返します。この通知は、何が問題だったのかをユーザーに伝え、正しいファイルでの再試行を促すことを目的としています。

**通知の送信タイミング**：ダッシュボードのメニューから「Import」オプションを選択し、JSONファイルを選択後、`dashboard-import` API呼び出しの結果として `validation-error` または その他のエラーが返された直後に通知が発行されます。

**通知の受信者**：ダッシュボードのインポート操作を実行した現在のユーザーが受信者となります。

**通知内容の概要**：validation-error の場合はサーバーから返されたエラーメッセージが表示され、その他のエラーの場合は汎用的なエラーメッセージが表示されます。

**期待されるアクション**：ユーザーはエラーメッセージを確認し、インポートファイルを修正するか、正しい形式のファイルを選択して再度インポートを試みることが期待されます。

## 通知種別

アプリ内通知（トースト通知）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（即時） |
| 優先度 | 高 |
| リトライ | 無し |

### 送信先決定ロジック

現在アプリケーションを操作しているユーザーに対して表示されます。サーバーサイドでの送信先決定は行われず、クライアントサイドで直接通知が表示されます。

## 通知テンプレート

### アプリ内通知の場合

| 項目 | 内容 |
|-----|------|
| 通知タイプ | error |
| 通知ID | import |
| sticky | false（デフォルト） |

### 本文テンプレート

#### validation-error の場合

```
{サーバーから返されたエラーメッセージ}
```

例：
- `Missing field 'version' in Dashboard`
- `Missing field 'widgets' in Dashboard`
- `Invalid dashboard.widgets data type: it must be an array of widgets.`
- `Missing field 'type' in Dashboard widget #0`
- `Invalid widget.0.type value unknown-widget.`

#### その他のエラー（default）の場合

```
Failed importing the dashboard file.
```

### 添付ファイル

該当なし

## テンプレート変数

### validation-error の場合

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| message | エラーメッセージ | APIレスポンス.message | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 画面操作 | ダッシュボードメニューの「Import」選択 + ファイル選択 | `dashboard-import` APIが validation-error を返した場合 | バリデーション失敗 |
| 画面操作 | ダッシュボードメニューの「Import」選択 + ファイル選択 | `dashboard-import` APIが internal-error を返した場合 | 内部エラー |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| API呼び出しが成功した場合 | 成功時は成功通知（No.53）が表示される |
| json-parse-error の場合 | 別のエラー通知（No.55）が表示される |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[dashboard-import API呼び出し] --> B{APIレスポンス確認}
    B -->|errorなし| C[成功通知表示]
    B -->|json-parse-error| D[JSONパースエラー通知]
    B -->|validation-error| E[addNotification発行]
    B -->|その他エラー| F[addNotification発行]
    E --> G[バリデーションエラー通知表示]
    F --> H[汎用エラー通知表示]
    G --> I[終了]
    H --> I
    C --> I
    D --> I
```

## データベース参照・更新仕様

### 参照テーブル一覧

該当なし（バリデーション段階でエラーが発生するため、DB操作は行われない）

### 更新テーブル一覧

該当なし（エラー時はDB更新は行われない）

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| validation-error | インポートファイルの構造が不正 | サーバーからのエラーメッセージを表示 |
| internal-error | ファイル読み込み失敗、その他の内部エラー | 汎用エラーメッセージを表示 |

### バリデーションエラーの詳細

| バリデーション項目 | エラーメッセージ例 |
|------------------|-----------------|
| version フィールドの欠落 | Missing field 'version' in Dashboard |
| widgets フィールドの欠落 | Missing field 'widgets' in Dashboard |
| widgets が配列でない | Invalid dashboard.widgets data type: it must be an array of widgets. |
| ウィジェットの type 欠落 | Missing field 'type' in Dashboard widget #0 |
| x, y, width, height の型不正 | Invalid widget.0.x data-type for value undefined. |
| 不正なウィジェットタイプ | Invalid widget.0.type value unknown-widget. |
| カスタムレポートの meta 欠落 | Missing field 'meta' in Dashboard widget #0 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0回（自動リトライなし） |
| リトライ間隔 | - |
| リトライ対象エラー | - |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし（ユーザー操作に応じて即時表示）

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

- エラーメッセージには具体的なバリデーションエラーが含まれるが、内部実装の詳細は露出しない
- ファイルパスなどの機密情報はエラーメッセージに含まれない
- 不正なファイルによる攻撃はバリデーション段階でブロックされる

## 備考

- 通知ID「import」は、他のダッシュボードインポート/リセット関連通知と共有されている
- validation-error の場合、サーバーからのエラーメッセージがそのまま表示されるため、i18n対応されていない
- エラー通知は sticky ではないため、一定時間後に自動的に消える

---

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

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

### 推奨読解順序

#### Step 1: バリデーションルールを理解する

まず、インポートファイルに対するバリデーションルールを理解します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | app.ts | `packages/loot-core/src/server/dashboard/app.ts` | exportModel.validate（47-100行目）でバリデーションルールを確認 |
| 1-2 | models.ts | `packages/loot-core/src/server/models.ts` | requiredFields関数の実装 |

**読解のコツ**:
- requiredFields関数は必須フィールドの存在を検証し、欠落時にValidationErrorをスロー
- isWidgetType関数（31-44行目）でサポートされているウィジェットタイプを確認

#### Step 2: エラーハンドリングを理解する

クライアント側とサーバー側のエラーハンドリングを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Overview.tsx | `packages/desktop-client/src/components/reports/Overview.tsx` | onImport関数のエラーハンドリング（320-358行目） |
| 2-2 | app.ts | `packages/loot-core/src/server/dashboard/app.ts` | importDashboard関数のcatchブロック（334-346行目） |

**主要処理フロー**:
1. **334-346行目（サーバー）**: try-catch でエラーをキャッチし、エラータイプに応じたレスポンスを返す
2. **320-358行目（クライアント）**: APIレスポンスのerrorプロパティをチェックし、タイプに応じた通知を発行

#### Step 3: エラータイプと通知の対応を理解する

エラータイプごとの通知内容を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Overview.tsx | `packages/desktop-client/src/components/reports/Overview.tsx` | switch文（321-357行目）で各エラータイプの処理 |

**主要処理フロー**:
- **333-343行目**: validation-error の場合、res.message をそのまま表示
- **345-355行目**: default（その他エラー）の場合、固定メッセージを表示

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

```
Overview.tsx (ダッシュボード画面)
    │
    ├─ onImport (285行目)
    │      │
    │      ├─ send('dashboard-import', {...}) (314行目)
    │      │      └─ dashboard/app.ts: importDashboard (257行目)
    │      │             ├─ fs.readFile (269行目)
    │      │             ├─ JSON.parse (270行目)
    │      │             ├─ exportModel.validate (272行目)
    │      │             │      ├─ requiredFields (48行目)
    │      │             │      │      └─ [エラー] throw ValidationError
    │      │             │      └─ isWidgetType (90行目)
    │      │             │             └─ [エラー] throw ValidationError
    │      │             │
    │      │             └─ catch (334行目)
    │      │                    ├─ SyntaxError → json-parse-error
    │      │                    ├─ ValidationError → validation-error + message
    │      │                    └─ Other → internal-error
    │      │
    │      └─ [エラー応答時] switch(res.error)
    │             ├─ validation-error → addNotification(res.message)
    │             └─ default → addNotification("Failed importing...")
    │
    └─ 通知表示
```

### データフロー図

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

JSONファイル ───▶ fs.readFile ───▶ JSON.parse ───▶ validate
                                                       │
                              ┌──────────────────────────┘
                              │
                              ▼
                    [ValidationError発生]
                              │
                              ▼
                    { error: 'validation-error',
                      message: エラー詳細 }
                              │
                              ▼
                    addNotification({
                      type: 'error',
                      message: res.message
                    })
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Overview.tsx | `packages/desktop-client/src/components/reports/Overview.tsx` | ソース | ダッシュボード画面コンポーネント |
| app.ts | `packages/loot-core/src/server/dashboard/app.ts` | ソース | ダッシュボードAPIハンドラー・バリデーション |
| notificationsSlice.ts | `packages/desktop-client/src/notifications/notificationsSlice.ts` | ソース | 通知状態管理スライス |
| errors.ts | `packages/loot-core/src/server/errors.ts` | ソース | ValidationErrorクラス定義 |
| models.ts | `packages/loot-core/src/server/models.ts` | ソース | requiredFields関数 |
| dashboard.ts | `packages/loot-core/src/types/models/dashboard.ts` | 型定義 | エクスポート/インポート型定義 |
