Skip to content

feat: Budgets page — category budgets with spend tracking and MoM comparison#17

Merged
kayodebristol merged 3 commits into
mainfrom
copilot/add-budgets-page-features
Apr 4, 2026
Merged

feat: Budgets page — category budgets with spend tracking and MoM comparison#17
kayodebristol merged 3 commits into
mainfrom
copilot/add-budgets-page-features

Conversation

Copilot AI commented Apr 3, 2026

Copy link
Copy Markdown
Contributor

Implements the Budgets page for the financial-advisor plugin: category-based monthly spending limits with live progress tracking, alert thresholds, and month-over-month comparison against fa-budgets PluresDB collection.

New files

  • lib/categories.ts — Extracts the category list from Transactions.svelte into a single source of truth. Exports TRANSACTION_CATEGORIES (full list, incl. Income/Transfer/Refund) and BUDGET_CATEGORIES (spend-only subset, non-budgetable types excluded).

  • lib/budgets.tsBudget type + FA_BUDGETS_COLLECTION constant + helpers: budgetStatus(), spendPercent(), currentMonthPrefix(), previousMonthPrefix(), monthLabel().

  • pages/Budgets.svelte — Full page using Svelte 5 runes:

    • Create/edit/delete via native <dialog> (category, monthly limit, alert threshold %)
    • Progress bars with a threshold-marker tick; fill color driven by status
    • 🔴 Over budget / 🟡 Near limit / 🟢 On track — badge + card background tint
    • MoM label per card (e.g. +$42.00 vs Mar 2026) colored red/green by direction
    • Cards sorted by severity (over → warning → ok); duplicates blocked at the category level
    • Spending derived by joining fa-transactions debits with fa-transaction-inferences category field

Modified files

  • pages/Transactions.svelte — Now imports TRANSACTION_CATEGORIES from lib/categories.ts instead of defining the array inline.

Copilot AI changed the title [WIP] Add budgets page for category budgets and spending tracking feat: Budgets page — category budgets with spend tracking and MoM comparison Apr 4, 2026
Copilot AI requested a review from kayodebristol April 4, 2026 00:02
@kayodebristol kayodebristol marked this pull request as ready for review April 4, 2026 09:14
Copilot AI review requested due to automatic review settings April 4, 2026 09:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Budgets page to the financial-advisor plugin, enabling per-category monthly budgets with spending progress and month-over-month comparisons, backed by the new fa-budgets collection and shared category constants.

Changes:

  • Introduces Budgets.svelte with create/edit/delete flows, spend progress UI, and MoM comparison derived from transactions + inferences.
  • Adds shared helpers/types in lib/budgets.ts and extracts shared category lists into lib/categories.ts.
  • Updates Transactions.svelte to consume the shared transaction category list.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
plugins/financial-advisor/src/pages/Transactions.svelte Replaces inline category list with an import from shared category constants.
plugins/financial-advisor/src/pages/Budgets.svelte New Budgets page UI + logic (CRUD, spending aggregation, status display, MoM).
plugins/financial-advisor/src/lib/categories.ts New single source of truth for transaction vs budget category lists.
plugins/financial-advisor/src/lib/budgets.ts New Budget model + helpers (status, percent, month prefix/labels, collection constant).

const prevMo = previousMonthPrefix();

const allTx: Transaction[] = (await txCollection?.query()) ?? [];
const allInf: TransactionInference[] = (await infCollection?.query()) ?? [];

Copilot AI Apr 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In computeSpending, infCollection.query() loads every inference record and then filters in JS. Since DataCollection.query supports a filter object and TransactionInference has a field, consider querying only category inferences (e.g., filter by { field: 'category' }) to reduce data transfer and processing time as the dataset grows.

Suggested change
const allInf: TransactionInference[] = (await infCollection?.query()) ?? [];
const allInf: TransactionInference[] =
(await infCollection?.query({ field: 'category' })) ?? [];

Copilot uses AI. Check for mistakes.
const prev = previousSpend[category] ?? 0;
if (prev === 0 && curr === 0) return '';
const diff = curr - prev;
const sign = diff >= 0 ? '+' : '';

Copilot AI Apr 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

momLabel() adds a leading "+" when diff === 0 (because it uses diff >= 0), which produces labels like "+$0.00 vs " while momClass() treats diff === 0 as neutral. Consider only prefixing "+" when diff > 0 (or returning a dedicated "No change" label) to avoid misleading output.

Suggested change
const sign = diff >= 0 ? '+' : '';
const sign = diff > 0 ? '+' : '';

Copilot uses AI. Check for mistakes.
Comment on lines +19 to +22
let ctx: ReturnType<typeof getPluginContext>;
let budgetCollection: any;
let txCollection: any;
let infCollection: any;

Copilot AI Apr 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

budgetCollection, txCollection, and infCollection are typed as any, which drops type-safety for query/put/delete and makes it easier to misuse the PluresDB API. Other pages (e.g. Transactions.svelte) type these collections via ReturnType<NonNullable<typeof ctx>['data']['collection']> or DataCollection<T>; aligning here would improve maintainability and catch mistakes at compile time.

Suggested change
let ctx: ReturnType<typeof getPluginContext>;
let budgetCollection: any;
let txCollection: any;
let infCollection: any;
type PluginContext = NonNullable<ReturnType<typeof getPluginContext>>;
type CollectionOf<T> = ReturnType<PluginContext['data']['collection']<T>>;
let ctx: ReturnType<typeof getPluginContext>;
let budgetCollection: CollectionOf<Budget>;
let txCollection: CollectionOf<Transaction>;
let infCollection: CollectionOf<TransactionInference>;

Copilot uses AI. Check for mistakes.
Comment on lines +10 to +15
export interface Budget {
id: string;
/** Must match a category recognised by the transaction inference engine. */
category: string;
/** Maximum allowed spend for this category per calendar month (positive). */
monthlyLimit: number;

Copilot AI Apr 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Budget.category is typed as a plain string, even though the UI restricts selection to BUDGET_CATEGORIES and the repo now provides BudgetCategory/TransactionCategory unions in lib/categories.ts. Narrowing this field to the appropriate category union would prevent invalid records from being persisted and simplify downstream code (less defensive checking/casting).

Copilot uses AI. Check for mistakes.

@kayodebristol kayodebristol left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auto-approved: CI green + code review complete.

@kayodebristol kayodebristol merged commit 49f8392 into main Apr 4, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Budgets page — create category budgets, track spending

3 participants