Commit Graph

8 Commits

Author SHA1 Message Date
Guillem Arias Fauste
9d3879a859 feat(plan): unify Budgets and Goals under a single Plan tab (#2687)
* feat(plan): unify Budgets and Goals under a single Plan tab

Preview users get one "Plan" nav entry (compass icon) in place of the
separate Budgets and preview-gated Goals items. It fronts a new /plan
hub with two summary cards — this month's budget (spent vs budgeted,
days left, top categories) and active goals (total saved vs targets,
behind/pending counts, per-goal rows) — each drilling into the existing
/budgets and /goals pages, whose breadcrumbs now start Home > Plan.

The two features share a home, not a model: no schema changes, no URL
changes. Users without preview features keep exactly the pre-Plan nav
(Budgets entry, Goals hidden), and /plan falls through to /budgets for
them.

Supporting changes:
- Goal.active_prepared_for: index-style sorted active goals with the
  family-wide pooled-allocations + market-flows injection reused
- Goal::FUNDABLE_ACCOUNT_TYPES and Goal::ACTIVE_DISPLAY_STATUS_RANK
  extracted from GoalsController
- Budget#days_remaining (same day math as suggested_daily_spending)
- Breadcrumbable#plan_breadcrumb_prefix for the conditional Plan crumb
- New BudgetsController web tests (previously untested) + Plans tests

* fix(plan): address review feedback on the Plan hub

- Keep the "All goals" footer link rendered when the family has only
  completed/archived goals — the hub is a preview user's only route to
  the goals index now that the Goals nav entry is gone (Codex P2)
- Replace the two hand-rolled footer button-links with DS::Link
  (variant secondary, full_width, right icon) per DS Drift Patrol
- Fix DS::Link template comparing icon_position against the string
  "right" — the initializer symbolizes it, so right-positioned icons
  never rendered on links (DS::Button already compared symbols);
  existing callers passing icon_position now get the layout they asked for
- Clamp progress-bar percentages to 0..100 instead of capping only the
  upper bound (CodeRabbit)

* refactor(plan): single source of truth for goal loading, sorting, and counts

Addresses jjmata's draft review notes:

- GoalsController#index now builds on the shared Goal loaders instead
  of hand-rolling its own copy: Goal.prepared_for (preloads + family-
  wide backing-math injection, scope-able) and Goal.active_display_sort
  carry the algorithm once; active_prepared_for composes them for the
  hub. The controller-side constant alias is gone
- One definition of "behind pace": Goal#behind_pace? (excludes paused —
  pausing stops the pace clock on purpose). Both the Plan hub summary
  and GoalsController#kpi_payload's behind/needs-this-month figures use
  it, so adjacent pages can't disagree. While there, the kpi on-track
  numerator also excludes paused goals — it was counted against a
  paused-excluding denominator, so the "X of Y" fraction could exceed
  its own total
- BudgetCategory#suggested_daily_spending calls Budget#days_remaining
  instead of keeping an inline copy of the day math
- Per the fat-model convention, the hub's aggregation moved off the
  controller: Budget#top_spending_categories(limit:) and
  Goal.summary_for(goals, currency:)

* fix(plan): move Edit budget/New goal into their own cards

Both actions lived in the hub's shared page header, unlinked to either
card and, on mobile, wrapping above all content before any real data
appeared. Each now lives in its own card's header instead: Edit budget
as a compact icon-only control next to the status pill (only when a
budget exists — the uninitialized state already has its own "Set up"
CTA), New goal as a small outline button next to the goals count (only
once there's a goal to sit beside; the empty state keeps its own CTA).

Also swaps the edit icon from "pencil" to "square-pen" — at the sizes
these header controls render, lucide's plain pencil is a thin diagonal
stroke that reads noticeably smaller than a neighboring bold glyph like
"plus", even in the same size box. square-pen carries more visual mass
and reads clearly at the same footprint.

* fix(plan): match established DS precedent for the card header actions

Edit budget was a bare icon-only button; verified against the app's
own precedent for this exact action (app/views/budgets/_budget_donut.html.erb,
the budget card already shipped on /budgets) and it's a labeled
secondary link with a trailing pencil, not icon-only and not a
three-dot menu. Matched that: DS::Link, variant secondary, size sm,
icon right. New goal gets the same treatment for consistency between
the two cards' header actions, rather than the full-page-scoped
"primary" weight goals/index.html.erb uses for its own create button —
that's calibrated for a whole page's sole CTA, not a compact card.

Adding a labeled button (wider than the bare icon this replaces)
crowded the header row on mobile enough to wrap "This month" onto two
lines and truncate the "· July 2026" meta away entirely. Header rows
now wrap as a whole (flex-wrap) with the title pinned (shrink-0) so
the action cluster drops to its own line instead of squeezing the
title and meta text.

Also drops the hub's footer note ("Budgets cap your spend; goals track
what you're saving toward...") — redundant with the subtitle right
above the cards.

* fix(plan): lead the budget card header with status, not the edit action

On Track/Over/Warning is what a glance at the card wants first; Edit
budget is the secondary action. Swapped their order so status leads
and the edit control trails, gap-2 unchanged.

* fix(plan): put the status pill on the left, next to the title

Meant the left side of the card, not just left of the edit button. On
Track/Over/Warning now sits beside "This month · July 2026" in normal
flow; ml-auto carries only the Edit budget link, alone on the right —
matching the goals card's own left-meta/right-action split ("· 7
active" left, "New goal" right).

* fix(plan): lead Edit budget with its icon, matching same-shape precedent

Wrong axis on the earlier match: _budget_donut's trailing pencil labels
the VALUE itself ("$12,850 ✎"), not a static action. Our button's label
is a static "Edit budget", and that shape takes a leading icon
everywhere else it appears — the categories "Edit" on budgets/show.html.erb
(icon: settings-2) and "Edit split" in transactions/show.html.erb both
lead with their icon. Drops icon_position: :right so it defaults to
left, matching New goal's shape in the sibling card.

* fix(plan): use the divider token for row separators, not border-primary

Traced against the dashboard outflows list (pages/dashboard/_outflows_donut.html.erb),
which renders its row separators via shared/_ruler → border-divider
(border-tertiary: black/8%, white/10%). Our category and goal rows used
border-b border-primary instead (black/15%, white/30%) — 2-3x heavier
than the established row-separator weight elsewhere in the app. Swapped
both to border-divider.

* fix(plan): lift the duplicated card shell into DS::Card

Codex P1: _budget_card.html.erb and _goals_card.html.erb hand-rolled
the identical "bg-container rounded-xl shadow-border-xs p-5 flex
flex-col" shell twice, with no DS:: card primitive to reach for
instead. Extracted a minimal wrapper — content-only, no header/footer
slots — matching what both cards actually need right now; the roadmap
cards (envelopes #2153, retirement #2044) can adopt it too instead of
copying the class string a third time.

Verified pixel-identical in a browser: same classes, same DOM shape,
just rendered through the component.

* fix(plan): batch pace queries before sorting goals

Codex P2: active_display_sort calls goal.status per goal to build the
sort key; Goal#status reaches Goal#pace for any goal with a
target_date, which fired its own Entry.sum(:amount) query per goal.
The /plan hub renders only the first 5 of active_prepared_for's list,
but paid the full O(N) query cost sorting all of them.

Adds Goal.pace_for(family) (account_id => 90-day net inflow), grouped
in one query and injected via inject_backing_math! alongside the
existing pooled_allocations/market_flows pattern. #pace now sums from
that shared map instead of firing its own query — same math, same
90-day window, same exclusions, just computed once per family instead
of once per goal.
2026-08-01 08:51:08 +02:00
Brendon Scheiber
7411db5689 feat(i18n): add Hungarian translations for strings extracted in #1806 (#1817)
* add missing Hungarian translations for newly extracted strings

Replace hard-coded UI strings with I18n lookups across controllers, models and views (breadcrumbs, dashboard, reports, settings, transactions, balance sheet, MFA status). Update models to use translations for category defaults, account/display names, classification group and period labels; remove a few hardcoded display_name methods. Add and update numerous locale files (English and extensive Hungarian translations, plus model/view/doorkeeper entries) to provide the required keys. These changes centralize copy for localization and prepare the app for Hungarian/English UI text.

* Pluralize account type labels; tidy Crypto model

Update English locale account type labels to use plural forms for consistency (Investment(s), Properties, Vehicles, Other Assets, Credit Cards, Loans, Other Liabilities). Also remove an extra blank line in app/models/crypto.rb to tidy up formatting.

* Back to singular

* fix(i18n): separate singular and group account labels

* Update _accountable_group.html.erb

* Use I18n plural names for account types

Change Accountable#display_name to look up pluralized account type names via I18n (accounts.types_plural.<underscored_class>) with a fallback to the legacy display logic. Add legacy_display_name helper to preserve previous behavior (singular for Depository and Crypto, pluralized otherwise). Add corresponding types_plural entries in English and Hungarian locale files for various account types.

---------

Co-authored-by: Juan José Mata <jjmata@jjmata.com>
Co-authored-by: sure-admin <sure-admin@splashblot.com>
2026-05-18 20:49:28 +02:00
soky srm
9410e5b38d Providers sharing (#1273)
* third party provider scoping

* Simplify logic and allow only admins to mange providers

* Broadcast fixes

* FIX tests and build

* Fixes

* Reviews

* Scope merchants

* DRY fixes
2026-03-25 17:47:04 +01:00
lolimmlost
158e18cd05 Add budget rollover: copy from previous month (#1100)
* Add budget rollover: copy from previous month

When navigating to an uninitialized budget month, show a prompt
offering to copy amounts from the most recent initialized budget.
Copies budgeted_spending, expected_income, and all matching category
allocations. Also fixes over-allocation warning showing on uninitialized
budgets.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Redirect copy_previous to categories wizard for review

Matches the normal budget setup flow (edit → categories → show)
so users can review/tweak copied allocations before confirming.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Address code review: eager-load categories, guard against overwrite

- Add .includes(:budget_categories) to most_recent_initialized_budget
  to avoid N+1 when copy_from! iterates source categories
- Guard copy_previous action against overwriting already-initialized
  budgets (prevents crafted POST from clobbering existing data)
- Add i18n key for already_initialized flash message

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add invariant guards to copy_from! for defensive safety

Validate that source budget belongs to the same family and precedes
the target budget before copying. Protects against misuse from
other callers beyond the controller.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix button overflow on small screens in copy previous prompt

Stack buttons vertically on mobile, side-by-side on sm+ breakpoint.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 21:13:59 +01:00
MkDev11
68efe71cdb feat: Customizable Budget Month Start Day (#810)
* Add customizable budget month start day (#253)

Allow users to set a custom month-to-date start date (1st-28th) for
budgeting and MTD calculations. Useful for users who want budget
periods aligned with their pay schedule (e.g., 25th to 24th).

Changes:
- Add month_start_day column to families table (default: 1)
- Add database check constraint for valid range (1-28)
- Add Family#uses_custom_month_start?, custom_month_start_for,
  custom_month_end_for, current_custom_month_period helper methods
- Add Period.current_month_for(family), last_month_for(family) methods
- Update Budget model for custom month boundaries in find_or_bootstrap,
  param_to_date, budget_date_valid?, current?, and name methods
- Add month_start_day setting to Settings > Preferences UI
- Add warning message when custom month start day is configured
- Add comprehensive tests with travel_to for date robustness

Fixes #253

* Add /api/v1/user endpoint for Flutter mobile app and PWA

Expose user preferences including month_start_day via API endpoint
following existing pattern for default_period. This allows Flutter
mobile app and PWA to read/update user preferences through a
consistent API contract.

Endpoints:
- GET /api/v1/user - Read user preferences including family settings
- PATCH /api/v1/user - Update user preferences

Response includes: id, email, first_name, last_name, default_period,
locale, and family settings (currency, timezone, date_format, country,
month_start_day).

* Update Periodable to use family-aware MTD periods

When users select 'current_month' or 'last_month' period filters on
dashboard/reports, now respects the family's custom month_start_day
setting instead of using static calendar month boundaries.

This ensures MTD filter on dashboard is consistent with how budgets
calculate their periods when custom month start day is configured.

* Fix param_to_date to correctly map budget params to custom periods

When a family uses a custom start day, the previous implementation
called custom_month_start_for on the 1st of the month, which incorrectly
shifted dates before the start day to the previous month.

Now we directly construct the date using family.month_start_day, so
'jan-2026' with month_start_day=25 correctly returns Jan 25, 2026
instead of Dec 25, 2025.

* Fix param_to_date and use Current pattern in API controller

- Fix param_to_date to directly construct date with family.month_start_day
  instead of using custom_month_start_for which incorrectly shifted dates
- Replace current_user with Current.user/Current.family in API controller
  to follow project convention used in other API v1 controllers

* Add i18n for budget name method

Use I18n.t for localizable budget period names to follow
project conventions for user-facing strings.

* Remove unused budget_end variable in budget_date_valid?

* Use Date.current for timezone consistency in Budget#current?

* Address PR review feedback

- Remove API users endpoint (mobile won't use yet)
- Remove user route from config/routes.rb
- Remove ai_summary/document_type schema bleed from pdf-import-ai branch

* Pass family to param_to_date for custom month logic

* Run migration to add month_start_day column to schema

* Schema regressions

---------

Co-authored-by: mkdev11 <jaysmth689+github@users.noreply.github.com>
Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
2026-02-04 23:58:09 +01:00
Josh Pigford
a4874815a6 Add breadcrumbs support across application (#1897)
* Add breadcrumbs support across application

Fixes #1896

* Potential fix for tests

* Simplify breadcrumbs implementation

Remove complex breadcrumbs logic from controllers and concern, replacing with a simpler default approach that sets a basic breadcrumb based on the current controller name

* Refactor page header and breadcrumbs rendering

Remove complex breadcrumbs helper method and update layout to use more flexible content_for approach for page headers and breadcrumbs

* Add fallback breadcrumbs rendering to settings layout
2025-02-25 10:14:07 -06:00
Zach Gollwitzer
d75be2282b New Design System + Codebase Refresh (#1823)
Since the very first 0.1.0-alpha.1 release, we've been moving quickly to add new features to the Maybe app. In doing so, some parts of the codebase have become outdated, unnecessary, or overly-complex as a natural result of this feature prioritization.

Now that "core" Maybe is complete, we're moving into a second phase of development where we'll be working hard to improve the accuracy of existing features and build additional features on top of "core". This PR is a quick overhaul of the existing codebase aimed to:

- Establish the brand new and simplified dashboard view (pictured above)
- Establish and move towards the conventions introduced in Cursor rules and project design overview #1788
- Consolidate layouts and improve the performance of layout queries
- Organize the core models of the Maybe domain (i.e. Account::Entry, Account::Transaction, etc.) and break out specific traits of each model into dedicated concerns for better readability
- Remove stale / dead code from codebase
- Remove overly complex code paths in favor of simpler ones
2025-02-21 11:57:59 -05:00
Zach Gollwitzer
195ec85d96 Budgeting V1 (#1609)
* Budgeting V1

* Basic UI template

* Fully scaffolded budgeting v1

* Basic working budget

* Finalize donut chart for budgets

* Allow categorization of loan payments for budget

* Include loan payments in incomes_and_expenses scope

* Add budget allocations progress

* Empty states

* Clean up budget methods

* Category aggregation queries

* Handle overage scenarios in form

* Finalize budget donut chart controller

* Passing tests

* Fix allocation naming

* Add income category migration

* Native support for uncategorized budget category

* Formatting

* Fix subcategory sort order, padding

* Fix calculation for category rollups in budget
2025-01-16 14:36:37 -05:00