家計診断Proに「データのダウンロード/アップロード」機能を追加しました

家計診断Pro

家計診断Pro(Financial Health Check Pro)に、
入力したデータをファイルとして保存し、あとから再読み込みできる機能を追加しました。

これで、

  • FPが作成した診断データを、ファイルでクライアントに渡す
  • クライアント側で追加入力したデータを、再びFPに送り返す
  • そのやり取りを ブラウザ内のファイルのみで完結 させる

という運用ができるようになります。
サーバ側に個人情報を持たなくて良いので、データ流出リスクを最小限に抑えつつ、実務で使えるツールに一歩近づきました。


方針:画面状態を 1 個の JSON にまとめてやり取りする

今回の実装方針はシンプルで、

「画面の状態(家計データ+コメント+相談者情報+FP情報)を
ひとまとめのオブジェクトにして JSON ファイルに保存する」

という考え方です。

具体的には、こんなイメージのデータ構造を作っています(実際のコードはもっと項目がありますが概念的に):

const payload = {
  version: 1,           // 将来の拡張用
  createdAt: new Date().toISOString(),

  // キャッシュフロー表(20年分の rows)
  rows,

  // 各年のイベントメモ
  eventNotes,

  // AI診断結果(必要に応じて)
  diag,

  // FPコメント(4ページ目右側の所見欄)
  fpComment,

  // 相談者情報+FP情報(今回追加したブロック)
  meta: {
    clientCompany,
    clientName,
    fpCompany,
    fpLicense,
    fpName,
    fpHp,
    // 提出書類チェックなどもここに含められます
  }
};

この payloadJSON.stringify() で文字列にして、
ローカルに .json ファイルとしてダウンロードします。
逆に、アップロード時はその JSON を JSON.parse() して各 state に流し込む、という流れです。


ダウンロード機能の実装

ボタンを設置する

画面の下部(PDF作成ボタンの近く)に、
こんな感じのボタンを追加しました:

<button
  type="button"
  className="btn btn--ghost text-sm"
  onClick={handleExportData}
>
  データをファイルに保存(JSON)
</button>

handleExportData が実際に JSON ファイルを生成する関数です。

JSON を作って Blob としてダウンロード

handleExportData の中身は、ざっくり以下のような流れです。

function handleExportData() {
  // 1. 画面状態から payload を作る
  const payload = {
    version: 1,
    createdAt: new Date().toISOString(),
    rows,
    eventNotes,
    diag,
    fpComment,
    meta: {
      clientCompany,
      clientName,
      fpCompany,
      fpLicense,
      fpName,
      fpHp,
      // 提出書類チェックもここに追加可能
    }
  };

  // 2. JSON 文字列に変換
  const json = JSON.stringify(payload, null, 2);

  // 3. Blob を作成
  const blob = new Blob([json], { type: "application/json" });
  const url  = URL.createObjectURL(blob);

  // 4. ダウンロード用の <a> 要素を一時的に作ってクリック
  const a = document.createElement("a");
  const dateStr = new Date().toISOString().slice(0, 10).replace(/-/g, "");
  a.href = url;
  a.download = `fhc-data-${dateStr}.json`;
  document.body.appendChild(a);
  a.click();

  // 5. 後片付け
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
}

ポイントは:

  • サーバを経由せず、ブラウザの中だけで完結していること
  • ファイル名に日付を含めて、あとから見ても分かりやすいこと

この .json ファイルをそのままクライアントに渡せば、
クライアント側でアップロードして同じ状態を再現できます。


アップロード機能の実装

非表示の <input type="file"> を用意する

アップロードは、隠しファイル入力を使って実装しました。

<input
  type="file"
  id="fhc-file-input"
  accept="application/json"
  className="hidden"
  onChange={handleImportData}
/>

<button
  type="button"
  className="btn btn--ghost text-sm"
  onClick={() => document.getElementById("fhc-file-input")?.click()}
>
  保存データを読み込む(JSON)
</button>
  • ユーザーはボタンを押すだけ
  • 実際のファイル選択は hidden な <input> が担当する
    というよくあるパターンです。

FileReaderでJSONを読み込んでstateに反映

handleImportData の中身はこんな感じです:

function handleImportData(e) {
  const file = e.target.files?.[0];
  if (!file) return;

  const reader = new FileReader();
  reader.onload = (ev) => {
    try {
      const text = ev.target?.result;
      const data = JSON.parse(text);

      // ざっくりバリデーション(version など)
      if (!data || typeof data !== "object") {
        alert("不正なデータ形式です。");
        return;
      }

      // rows
      if (Array.isArray(data.rows)) {
        setRows(data.rows);
        localStorage.setItem("rows", JSON.stringify(data.rows));
      }

      // eventNotes
      if (Array.isArray(data.eventNotes)) {
        setEventNotes(data.eventNotes);
        localStorage.setItem("eventNotes", JSON.stringify(data.eventNotes));
      }

      // fpComment
      if (typeof data.fpComment === "string") {
        setFpComment(data.fpComment);
        localStorage.setItem("fpComment", data.fpComment);
      }

      // meta(相談者+FP情報)
      if (data.meta && typeof data.meta === "object") {
        setClientCompany(data.meta.clientCompany || "");
        setClientName(data.meta.clientName || "");
        setFpCompany(data.meta.fpCompany || "");
        setFpLicense(data.meta.fpLicense || "");
        setFpName(data.meta.fpName || "");
        setFpHp(data.meta.fpHp || "");

        localStorage.setItem("pdfMeta", JSON.stringify(data.meta));
      }

      // diag(AI診断結果)なども必要に応じて復元可能
      // if (data.diag) { setDiag(data.diag); }

      alert("データを読み込みました。");
    } catch (err) {
      console.error(err);
      alert("JSON の読み込みに失敗しました。");
    } finally {
      // 同じファイルを続けて選べるようにクリア
      e.target.value = "";
    }
  };

  reader.readAsText(file, "utf-8");
}

ポイントは:

  • 壊れたデータで画面が壊れないように、最低限のチェックをしている
  • setRows / setEventNotes / setFpComment など、
    既存の React state に素直に代入している
  • localStorage も一緒に更新しているので、リロードしても状態を保持できる

これで、FPとクライアントが
1つのJSONファイルだけで家計診断Proの状態を共有できるようになりました。


セキュリティと運用上の考え方

今回の実装でこだわっているのは、

  • サーバに個人情報を保存しない
  • データのやり取りは、ユーザーの手元のファイル(JSON)だけで完結させる

という点です。

そのため:

  • FPは、顧客ごとに fhc-data-YYYYMMDD.json を保存して管理
  • クライアントに送るときも、パスワード付きZIPや安全な共有手段を使えばOK
  • Webアプリ側は「ブラウザの一時ツール」として割り切り、
    データ保管はユーザー側に委ねられます

「クラウドで全部持つ」のではなく、
「ブラウザ+ファイル」というシンプルな構成で完結する診断プラットフォームを目指しています。


今回の機能追加の位置づけ

今回の「JSONダウンロード/アップロード」機能は、
家計診断Proを

特定のFPだけのツールではなく、
誰でも無料で使える“共通の家計診断プラットフォーム”

にしていくための、重要な一歩だと思っています。

  • FP事務所ごとにサーバを持たなくても、ブラウザとJSONファイルだけで実務に耐える
  • 将来的に、他FPの方にもそのまま使ってもらえる形に近づけていく
  • データ形式(JSON)を公開しておけば、他ツールとの連携も可能になる

そんなオープンな方向をイメージしながら、機能を足していっています。


まとめ

今回は、

  • 家計診断Proに JSONファイルでのデータ保存/読み込み機能を追加
  • ダウンロード:画面状態をまとめて1つのJSONにしてBlob経由で保存
  • アップロード:FileReader+JSON.parseで復元 → state&localStorageに反映
  • サーバを介さず、ブラウザ+ファイルだけで顧客⇔FPのデータ受け渡しができる

というところまで実装しました。

今後は、

  • 形式のバージョン管理(version の活用)
  • 一部項目だけのインポート/エクスポート
  • 他ツール(CSVやExcel)との変換

なども検討していく予定です。

コメント