feat: dashboard widgets — net worth, budget status, recent transactions#19
Conversation
Agent-Logs-Url: https://github.com/plures/pares-modulus/sessions/c090a08b-ee84-4b69-93bb-d8b4cbca6f61 Co-authored-by: kayodebristol <3579196+kayodebristol@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds the three financial-advisor dashboard widgets referenced by the plugin’s dashboardWidgets registration, providing at-a-glance net worth, budget utilization, and recent activity.
Changes:
- Implement
NetWorthwidget (accounts-based net worth + month-over-month trend indicator). - Implement
BudgetStatuswidget (current-month spend by category joined via inferences; renders top 3 budgets with progress bars). - Implement
RecentTransactionswidget (last 5 transactions with inferred category + confidence badges).
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| plugins/financial-advisor/src/pages/widgets/NetWorth.svelte | Loads accounts/transactions and renders a net worth summary with a trend indicator plus assets/liabilities breakdown. |
| plugins/financial-advisor/src/pages/widgets/BudgetStatus.svelte | Loads budgets/transactions/inferences and renders the top 3 budgets by spend % with mini progress bars. |
| plugins/financial-advisor/src/pages/widgets/RecentTransactions.svelte | Loads transactions/inferences and renders the 5 most recent transactions with category + confidence badges. |
| > | ||
| <div | ||
| class="progress-fill" | ||
| style:width="{row.pct}%" |
There was a problem hiding this comment.
style:width="{row.pct}%" won't interpolate row.pct; it will be treated as a literal string, so the progress fill width won't reflect the percentage. Use either a normal style attribute with Svelte interpolation (e.g. style="width: {row.pct}%") or a style:width directive with an expression that includes units.
| style:width="{row.pct}%" | |
| style:width={`${row.pct}%`} |
| } | ||
| Promise.all([ | ||
| ctx.data.collection<Transaction>(FA_TRANSACTIONS_COLLECTION).query(), | ||
| ctx.data.collection<TransactionInference>(FA_INFERENCES_COLLECTION).query(), |
There was a problem hiding this comment.
The inferences query loads the entire fa-transaction-inferences collection and then filters client-side. Since this widget only uses category inferences, scope the query to { field: 'category' } (as done in Reports.svelte) to reduce data transfer and improve load time on large datasets.
| ctx.data.collection<TransactionInference>(FA_INFERENCES_COLLECTION).query(), | |
| ctx.data | |
| .collection<TransactionInference>(FA_INFERENCES_COLLECTION) | |
| .query({ field: 'category' }), |
| } | ||
| Promise.all([ | ||
| ctx.data.collection<Transaction>(FA_TRANSACTIONS_COLLECTION).query(), | ||
| ctx.data.collection<TransactionInference>(FA_INFERENCES_COLLECTION).query(), |
There was a problem hiding this comment.
The inferences query loads the entire fa-transaction-inferences collection and then filters client-side. Since this widget only uses category inferences, scope the query to { field: 'category' } to reduce data transfer and improve load time on large datasets (see Reports.svelte for the same pattern).
| ctx.data.collection<TransactionInference>(FA_INFERENCES_COLLECTION).query(), | |
| ctx | |
| .data | |
| .collection<TransactionInference>(FA_INFERENCES_COLLECTION) | |
| .query({ field: 'category' }), |
| .catch(() => { | ||
| /* show empty state on error */ |
There was a problem hiding this comment.
The catch handler swallows load failures and then renders the same UI as the 'no budgets' empty state, which can mislead users and makes failures hard to debug. Consider calling ctx.notify.error(...) (or rendering a distinct error empty-state) when the Promise.all fails.
| .catch(() => { | |
| /* show empty state on error */ | |
| .catch((error) => { | |
| ctx.notify?.error?.( | |
| `Failed to load budget status data${error instanceof Error && error.message ? `: ${error.message}` : '.'}`, | |
| ); |
| .catch(() => { | ||
| /* show empty state on error */ |
There was a problem hiding this comment.
The catch handler swallows load failures and then renders the same UI as the 'no transactions yet' empty state, which can mislead users and makes failures hard to debug. Consider calling ctx.notify.error(...) (or rendering a distinct error empty-state) when the Promise.all fails.
| .catch(() => { | |
| /* show empty state on error */ | |
| .catch((error) => { | |
| ctx.notify?.error?.('Failed to load recent transactions.'); |
| .catch(() => { | ||
| /* show empty state on error */ |
There was a problem hiding this comment.
The catch handler swallows load failures and then renders the same UI as the 'no account data' empty state, which can mislead users and makes failures hard to debug. Consider calling ctx.notify.error(...) (or rendering a distinct error empty-state) when the Promise.all fails.
| .catch(() => { | |
| /* show empty state on error */ | |
| .catch((error) => { | |
| ctx.notify?.error?.( | |
| `Failed to load net worth data${error instanceof Error && error.message ? `: ${error.message}` : '.'}`, | |
| ); |
kayodebristol
left a comment
There was a problem hiding this comment.
Auto-approved: CI green + code review complete.
Implements the three dashboard widgets declared in the
financial-advisorplugin manifest but not yet created.Widgets added
widgets/NetWorth.sveltewidgets/BudgetStatus.sveltefa-transactions+fa-transaction-inferencesto compute debit spend per category for the current calendar month% of monthlyLimitused; renders top 3 with mini progress bars coloured via the existingbudgetStatus()helper (green/amber/red)widgets/RecentTransactions.svelteimportedAt)fa-transaction-inferencesShared patterns
All three follow the existing Svelte 5 runes style (
$state/$derived/onMount), usegetPluginContext()for PluresDB access, show a pulse skeleton while loading, and show a descriptive empty-state when no data exists.