mirror of
https://github.com/we-promise/sure.git
synced 2026-07-27 20:22:16 +00:00
* Add monthly net worth chart with group breakdown to Reports Adds a net worth trend chart to the Reports > Net Worth section, rendered in the same design scheme as the dashboard chart. The chart shows one data point per month across the period selected at the top of the Reports page, and its hover tooltip breaks the hovered month down into per-account-group balances (Cash, Investments, Credit Cards, Loans, etc.) under Assets and Liabilities headings with section totals. - BalanceSheet::NetWorthBreakdownSeriesBuilder builds the monthly series by running Balance::ChartSeriesBuilder per account group (grouped by accountable type), with liabilities reported as positive magnitudes and all-zero groups omitted; cached with the same invalidation pattern as the existing net worth series - net_worth_chart Stimulus controller extends the existing time_series_chart controller, overriding only data normalization and the tooltip template - Reports controller passes the series through the existing net_worth_metrics hash; tooltip headings reuse existing locale keys Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Address review: month-over-month tooltip deltas, Stimulus value labels - Recompute each chart point's trend from the previous monthly point instead of inheriting the raw series trend, which at a monthly interval compared the underlying balance row's own start/end and so reflected only the last balance update before the sample date (chatgpt-codex-connector). The first point has no prior month and renders the standard flat state. - Pass tooltip section labels to the Stimulus controller as declared values (data-*-value attributes) per coding guidelines (coderabbit) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
80 lines
2.5 KiB
JavaScript
80 lines
2.5 KiB
JavaScript
import TimeSeriesChartController from "controllers/time_series_chart_controller";
|
|
|
|
// Net worth chart for the Reports page. Inherits all drawing behavior from
|
|
// the time series chart and extends the tooltip with a per-account-group
|
|
// breakdown of assets and liabilities at the hovered point.
|
|
export default class extends TimeSeriesChartController {
|
|
static values = {
|
|
assetsLabel: { type: String, default: "Assets" },
|
|
liabilitiesLabel: { type: String, default: "Liabilities" },
|
|
};
|
|
|
|
_normalizeDataPoints() {
|
|
super._normalizeDataPoints();
|
|
|
|
const rawValues = this.dataValue.values || [];
|
|
this._normalDataPoints = this._normalDataPoints.map((point, i) => ({
|
|
...point,
|
|
assets: rawValues[i]?.assets,
|
|
liabilities: rawValues[i]?.liabilities,
|
|
groups: rawValues[i]?.groups || [],
|
|
}));
|
|
}
|
|
|
|
_tooltipTemplate(datum) {
|
|
return `${super._tooltipTemplate(datum)}${this._breakdownTemplate(datum)}`;
|
|
}
|
|
|
|
_breakdownTemplate(datum) {
|
|
const assetGroups = datum.groups.filter(
|
|
(group) => group.classification === "asset",
|
|
);
|
|
const liabilityGroups = datum.groups.filter(
|
|
(group) => group.classification === "liability",
|
|
);
|
|
|
|
const sections = [
|
|
this._sectionTemplate(this.assetsLabelValue, datum.assets, assetGroups),
|
|
this._sectionTemplate(
|
|
this.liabilitiesLabelValue,
|
|
datum.liabilities,
|
|
liabilityGroups,
|
|
),
|
|
]
|
|
.filter(Boolean)
|
|
.join("");
|
|
|
|
if (!sections) return "";
|
|
|
|
return `<div class="mt-2 pt-2 border-t border-secondary space-y-2">${sections}</div>`;
|
|
}
|
|
|
|
_sectionTemplate(label, total, groups) {
|
|
if (groups.length === 0) return "";
|
|
|
|
const rows = groups
|
|
.map(
|
|
(group) => `
|
|
<div class="flex items-center justify-between gap-4">
|
|
<div class="flex items-center gap-1.5 text-secondary">
|
|
<span class="w-2 h-2 rounded-full shrink-0" style="background-color: ${group.color};"></span>
|
|
${group.name}
|
|
</div>
|
|
<span class="text-primary tabular-nums">${this._extractFormattedValue(group.value)}</span>
|
|
</div>
|
|
`,
|
|
)
|
|
.join("");
|
|
|
|
return `
|
|
<div class="space-y-1 text-xs">
|
|
<div class="flex items-center justify-between gap-4 font-medium">
|
|
<span class="text-secondary uppercase">${label}</span>
|
|
<span class="text-primary tabular-nums">${this._extractFormattedValue(total)}</span>
|
|
</div>
|
|
${rows}
|
|
</div>
|
|
`;
|
|
}
|
|
}
|