# 通知設計書 53-ダッシュボードインポート成功

## 概要

本ドキュメントは、ダッシュボード設定をJSONファイルからインポートした際に成功した場合に表示される通知の設計書です。

### 本通知の処理概要

ダッシュボードのインポート操作が正常に完了した場合に、ユーザーに対して成功を通知し、元に戻す（Undo）オプションを提供します。

**業務上の目的・背景**：Actual Budget のダッシュボード機能では、ウィジェット配置やカスタムレポートを含むダッシュボード設定をJSONファイルとしてエクスポート・インポートできます。これにより、異なる環境間でダッシュボード設定を共有したり、バックアップから復元したりすることが可能です。この通知は、インポート操作が成功したことをユーザーに伝え、誤操作の場合に備えてUndo機能を提供します。

**通知の送信タイミング**：ダッシュボードのメニューから「Import」オプションを選択し、JSONファイルを選択後、`dashboard-import` API呼び出しが正常完了した直後に通知が発行されます。

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

**通知内容の概要**：インポート成功のメッセージと、Ctrl+Z（またはCmd+Z）でUndo可能であることを示すリンク付きメッセージが表示されます。

**期待されるアクション**：ユーザーはインポート結果を確認し、必要に応じてUndo操作（Ctrl+Zまたはメッセージ内のリンクをクリック）を実行することが期待されます。30秒のタイムアウト後、通知は自動的に消えます。

## 通知種別

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

## 送信仕様

### 基本情報

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

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

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

## 通知テンプレート

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

| 項目 | 内容 |
|-----|------|
| 通知タイプ | message |
| 通知ID | import |
| sticky | true |
| timeout | 30000ms（30秒） |

### 本文テンプレート

```
Dashboard has been successfully imported. Don't like what you see? You can always press [ctrl+z](#undo) to undo.
```

**注意**: メッセージ内の `[ctrl+z](#undo)` はMarkdown形式のリンクで、クリックするとUndo操作が実行されます。

### 添付ファイル

該当なし

## テンプレート変数

該当なし（固定メッセージ）

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 画面操作 | ダッシュボードメニューの「Import」選択 + ファイル選択 | `dashboard-import` API呼び出しが正常完了（errorプロパティなし） | onImport関数内で発行 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| APIレスポンスにerrorが含まれる場合 | json-parse-error、validation-error、その他エラー時は成功通知ではなくエラー通知を表示 |
| ファイルダイアログがキャンセルされた場合 | ファイルが選択されなかった場合は処理中断 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[Importメニュー選択] --> B{openFileDialog存在?}
    B -->|No| C[エラー通知: ダイアログを開けない]
    B -->|Yes| D[ファイル選択ダイアログ表示]
    D --> E[JSONファイル選択]
    E --> F[closeNotifications実行]
    F --> G[isImportingをtrueに設定]
    G --> H[dashboard-import API呼び出し]
    H --> I[isImportingをfalseに設定]
    I --> J{APIレスポンス確認}
    J -->|errorあり| K[エラー通知表示]
    J -->|正常完了| L[onDispatchSucessNotification呼び出し]
    L --> M[成功通知表示]
    M --> N{ユーザーアクション}
    N -->|Undoクリック| O[closeNotifications + undo実行]
    N -->|30秒経過| P[自動クローズ]
    N -->|手動クローズ| Q[通知を閉じる]
    O --> R[終了]
    P --> R
    Q --> R
    K --> R
    C --> R
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| dashboard | 現在のウィジェット一覧取得 | インポート前の削除対象特定 |
| custom_reports | 既存カスタムレポートの確認 | 重複チェック |

### テーブル別参照項目詳細

#### dashboard

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| id | ウィジェットID | dashboard_page_id = 対象ダッシュボードID AND tombstone = 0 |

#### custom_reports

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| id | カスタムレポートID | 全レコード |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| dashboard | DELETE（論理） | 既存ウィジェットの削除（tombstone = 1） |
| dashboard | INSERT | インポートされたウィジェットの挿入 |
| custom_reports | INSERT | 新規カスタムレポートの挿入 |
| custom_reports | UPDATE | 既存カスタムレポートの更新 |

#### dashboardテーブル更新詳細

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| DELETE | tombstone | 1 | 既存の全ウィジェットを論理削除 |
| INSERT | id, type, x, y, width, height, meta, dashboard_page_id | インポートファイルの値 | ウィジェットを挿入 |

#### custom_reportsテーブル更新詳細

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| INSERT | 全カラム | インポートファイルのカスタムレポート定義 | 新規レポートの場合 |
| UPDATE | 全カラム + tombstone=false | インポートファイルの値 | 既存IDが存在する場合は更新 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| json-parse-error | インポートファイルがJSON形式でない | エラー通知「Failed parsing the imported JSON.」を表示 |
| validation-error | JSONスキーマが不正 | エラー通知にサーバーからのエラーメッセージを表示 |
| internal-error | その他の内部エラー | エラー通知「Failed importing the dashboard file.」を表示 |
| ファイルダイアログエラー | ダイアログを開けない | エラー通知「Fatal error occurred: unable to open import file dialog.」を表示 |

### リトライ仕様

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

## 配信設定

### レート制限

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

### 配信時間帯

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

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

- インポートファイルはexportModel.validateで厳密にバリデーションされる
- 不正なウィジェットタイプやプロパティはValidationErrorとして拒否
- batchMessagesによりトランザクション管理が行われ、部分的なインポートを防止
- undoable操作として実装されており、誤操作からの復旧が可能

## 備考

- 通知ID「import」は、ダッシュボードのリセット操作と共有されている
- インポート前に既存の通知（同じID）はクローズされる
- カスタムレポートウィジェットの場合、関連するカスタムレポート定義も同時にインポートされる
- 既存のカスタムレポートIDと重複する場合は更新処理が行われる

---

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

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

### 推奨読解順序

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

まず、インポート/エクスポートのデータ構造を理解します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | dashboard.ts | `packages/loot-core/src/types/models/dashboard.ts` | ExportImportDashboard型（133-139行目）でインポートファイルの構造を確認 |
| 1-2 | dashboard.ts | `packages/loot-core/src/types/models/dashboard.ts` | ExportImportDashboardWidget型（128-131行目） |

**読解のコツ**: インポートファイルは { version: 1, widgets: [...] } の構造を持ち、各ウィジェットにはtype, x, y, width, height, metaが必要。

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

処理の起点となるダッシュボード画面コンポーネントのインポート処理を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Overview.tsx | `packages/desktop-client/src/components/reports/Overview.tsx` | onImport関数（285-366行目） |

**主要処理フロー**:
1. **286-300行目**: openFileDialogの存在確認、なければエラー通知
2. **302-310行目**: JSONファイル選択ダイアログを表示
3. **312-318行目**: closeNotifications実行、isImporting設定、API呼び出し
4. **320-358行目**: エラーハンドリング（json-parse-error, validation-error, default）
5. **361-365行目**: 成功時のonDispatchSucessNotification呼び出し

#### Step 3: サーバーサイド処理を理解する

インポートAPIハンドラーとバリデーションロジックを理解します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | app.ts | `packages/loot-core/src/server/dashboard/app.ts` | importDashboard関数（257-347行目） |
| 3-2 | app.ts | `packages/loot-core/src/server/dashboard/app.ts` | exportModel.validate（47-100行目） |

**主要処理フロー**:
- **264-270行目**: ファイル読み込みとJSONパース
- **272行目**: exportModel.validateでバリデーション
- **274-277行目**: 既存カスタムレポートIDの取得
- **279-283行目**: 既存ウィジェットの取得
- **285-330行目**: batchMessages内でDB更新（削除・挿入・更新）
- **334-346行目**: エラーハンドリング

#### Step 4: 通知システムを理解する

成功通知の構築と表示を理解します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Overview.tsx | `packages/desktop-client/src/components/reports/Overview.tsx` | onDispatchSucessNotification（184-202行目） |

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

```
Overview.tsx (ダッシュボード画面)
    │
    ├─ onImport (285行目)
    │      ├─ openFileDialog チェック (286行目)
    │      │      └─ [エラー] addNotification (289行目)
    │      │
    │      ├─ openFileDialog (302行目)
    │      │      └─ JSONファイル選択
    │      │
    │      ├─ closeNotifications (312行目)
    │      │      └─ removeNotification({ id: 'import' })
    │      │
    │      ├─ send('dashboard-import', {...}) (314行目)
    │      │      └─ dashboard/app.ts: importDashboard (257行目)
    │      │             ├─ fs.readFile (269行目)
    │      │             ├─ JSON.parse (270行目)
    │      │             ├─ exportModel.validate (272行目)
    │      │             └─ batchMessages (285行目)
    │      │                    ├─ db.delete_ (288行目)
    │      │                    ├─ db.insertWithSchema (291行目)
    │      │                    └─ db.insertWithSchema/updateWithSchema (306-329行目)
    │      │
    │      ├─ [エラー時] addNotification (323-355行目)
    │      │
    │      └─ [成功時] onDispatchSucessNotification (361行目)
    │             └─ addNotification (185行目)
    │
    └─ useUndo (182行目)
           └─ undo関数 - messageActions.undoで利用
```

### データフロー図

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

ファイル選択 ───▶ JSONパース ───▶ バリデーション ───▶ DB更新
                     │               │                    │
                     ▼               ▼                    ▼
              [パースエラー]   [バリデーションエラー]  [成功/内部エラー]
                     │               │                    │
                     ▼               ▼                    ▼
              エラー通知        エラー通知           成功通知/エラー通知
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| 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` | ソース | 通知状態管理スライス |
| useUndo.ts | `packages/desktop-client/src/hooks/useUndo.ts` | ソース | Undoフック |
| dashboard.ts | `packages/loot-core/src/types/models/dashboard.ts` | 型定義 | ウィジェット・エクスポート型定義 |
| errors.ts | `packages/loot-core/src/server/errors.ts` | ソース | ValidationErrorクラス |
