家計簿アプリを作る #29:収支の月次推移グラフ

家計簿アプリ作成シリーズの第29回です。前回の選択月の収支グラフに加えて、複数月にわたる収支の月次推移グラフを追加します。

実装方針

  • allTransactions(全件取得済み)を月ごとに集計して monthlyData を作成
  • +page.server.ts で集計して return で渡す
  • +page.svelte$derived を使ってグラフデータに変換

+page.server.ts の実装

// 月次の収支の推移(昇順)
const monthlyData = [...uniqueMonths].reverse().map(month => {
  const monthTx = allTransactions.filter(t => t.date.startsWith(month));
  return {
    month,
    income: monthTx.filter(t => t.type === 'income').reduce((sum, t) => sum + t.amount, 0),
    expense: monthTx.filter(t => t.type === 'expense').reduce((sum, t) => sum + t.amount, 0),
  };
});

uniqueMonths は降順(新しい順)で作っているため、[...uniqueMonths].reverse() で昇順にしてからグラフ用に集計します。... でコピーしてから reverse() することで元の uniqueMonths(プルダウン用)には影響を与えません。

+page.svelte の実装

const monthlyData = $derived({
  labels: data.monthlyData.map((d) => d.month),
  datasets: [
    {
      label: '収入',
      data: data.monthlyData.map((d) => d.income),
      backgroundColor: 'rgba(34, 197, 94, 0.5)',
      borderColor: 'rgb(34, 197, 94)',
      borderWidth: 1,
    },
    {
      label: '支出',
      data: data.monthlyData.map((d) => d.expense),
      backgroundColor: 'rgba(239, 68, 68, 0.5)',
      borderColor: 'rgb(239, 68, 68)',
      borderWidth: 1,
    },
  ],
});

datasets に収入・支出の2つを追加することで、同じグラフに2本の棒が並びます。

グラフにタイトルを付ける

options.plugins.title でタイトルを表示できます。TitleChart.register() に追加する必要があります。

import { Chart, BarElement, CategoryScale, LinearScale, Tooltip, Legend, Title } from "chart.js";

Chart.register(BarElement, CategoryScale, LinearScale, Tooltip, Legend, Title);
<Bar
  data={monthlyData}
  options={{
    plugins: {
      title: {
        display: true,
        text: '月次推移',
      },
    },
  }}
/>

datasets の label を省略しない

datasets の各オブジェクトに label を設定しないと、グラフ上部の凡例に undefined と表示されます。必ず設定してください。

// ❌ labelなし → 凡例にundefinedが表示される
{ data: [...] }

// ✅ labelあり
{ label: '収入', data: [...] }

まとめ

ポイント 内容
月次集計 allTransactionsfilter + reduce で月ごとに集計
昇順ソート [...uniqueMonths].reverse() でコピーしてから反転
複数データセット datasets に収入・支出を2つ追加
グラフタイトル Title を register して options.plugins.title で設定
凡例の undefined datasets の各オブジェクトに label を必ず設定する
← トップページに戻る