mirror of
https://github.com/we-promise/sure.git
synced 2026-05-25 13:34:58 +00:00
65bed0db2fb0e4e8a36c0cb9a255d92917de55c2
219 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
1ddd8bd040 |
feat(i18n): complete Catalan translations + extract residual hardcoded strings (#1836)
* feat(i18n): complete Catalan translations + extract residual hardcoded strings
CA coverage
- All view/model/breadcrumb/doorkeeper/mailer locale files for ca: 0 missing
keys (was ~3,400). Translations follow informal "tu" register, sentence case,
domain glossary (Compte/Saldo/Transacció/Posició/Operació/Pressupost/...).
- Catalan pluralization test: ca uses one/other; mirrors
test/lib/polish_pluralization_test.rb.
- 8 LanguageTool-flagged grammar fixes applied (Connexió òrfena, Secret de
l'API, comma-pero, apostrophe elisions, etc).
Hardcoded string extraction (also fixes EN parity)
- UI::Account::Chart#title + chart.html.erb view tabs -> UI.account.chart.*
- UI::Account::BalanceReconciliation labels + tooltips ->
UI.account.balance_reconciliation.{labels,tooltips}.*
- transactions/_transfer_match.html.erb (Auto-matched, A/M, Confirm/Reject
match, Payment/Transfer is confirmed) -> transactions.transfer_match.*
- AccountOrder labels (Name/Balance asc/desc) -> account_order.* keys with
fallback to existing hardcoded labels.
- Depository::SUBTYPES surface in account list -> depositories.subtypes.*.*
- User role badge -> users.roles.* (admin / member / super_admin).
- 110+ country names -> countries.* (config/locales/countries.ca.yml).
Breadcrumb locale fix
- Breadcrumbable was a before_action that ran before Localize's around_action
switched I18n.locale, so default crumbs rendered in EN even when locale=ca.
- Convert to helper_method that defers translation to render-time (when
I18n.locale is already correct). Add all missing breadcrumb keys to ca + en.
- Layouts switched from @breadcrumbs to breadcrumbs helper.
Locale-aware helpers / formatters
- ApplicationHelper#localized_ordinal: ordinalize that respects ca
(1r/2n/3r/4t/Nè). Wired into preferences month_start_day select.
- Family#moniker_label / moniker_label_plural: translate the default "Family"/
"Group" monikers via shared.family_moniker.* with fallback to the family's
custom override.
- Budget#name: use I18n.l for month_year/short/long instead of strftime("%B %Y")
so the budget header date follows the active locale.
Tooling
- script/lt_check_ca.rb: batched LanguageTool checker (premium endpoint when
LT_USERNAME/LT_API_KEY are set, free fallback otherwise), picky mode,
motherTongue=en for false-friend detection.
- lib/tasks/i18n_screenshot.rake: dev-only rake to set user.locale=ca and
role=super_admin on the demo user so the i18n surfaces can be walked.
Out of scope (pre-existing, not introduced here)
- Native browser file input "Choose Files / No file chosen" (browser locale).
- D3.js client-side chart x-axis dates (JS-side Intl.DateTimeFormat needed).
- Sankey/donut labels = seed category names (data, not i18n).
- 2 rails-i18n datetime/errors interpolation warnings inherited from
config/locales/defaults/ca.yml.
* fix(i18n): apply idiomatic Catalan review (3-agent + native review)
Three parallel review agents flagged 203 findings (31 high / 73 medium / 99 low)
across all 111 ca.yml files. This commit applies the high-severity bugs plus a
curated subset of medium-impact fixes.
Grammar / agreement
- provider_sync_summary.health.stale_pending: `(exclòs)` -> `(exclosa/excloses)`
to agree with feminine `transacció(s)`.
- accounts.confirm_unlink.warning_no_sync: added reflexive `es` -
`el compte ja no es sincronitzarà`.
- sophtron_setup_required.heading: `no configurats` -> `sense configurar`
(avoids broken agreement across "ID" masc. + "clau" fem.).
- admin.sso_providers.form.errors_title: split into one/other pluralization
keys (en + ca); singular `ha impedit` was wrong for count > 1.
Brand consistency
- IndexaCapital -> Indexa Capital (37 occurrences across one file).
- Lunchflow -> Lunch Flow in two remaining places.
Anglicisms / domain mistranslations
- kraken_items setup_accounts.instructions: `ompliments d'operacions`
(lit. dental/food fillings) -> `execucions d'operacions`.
- settings kraken_panel.read_only_title: `Sincronització d'intercanvi`
(swap/trade) -> `Sincronització només de lectura amb l'exchange`.
- transactions convert_to_trade.security_custom + security_not_listed_hint:
`cotització` (price quote) -> `ticker` (the EN field IS a ticker symbol).
- loans.form.rate_type: `Tipus d'interès` collided with sibling
interest_rate -> `Modalitat del tipus`.
- brex_items.provider_panel.sandbox_note_html: `L'staging` (broken
contraction) -> `el staging`.
Idiom traps
- coinbase/binance/kraken wait_for_sync: `acabi de sincronitzar` is
ambiguous in CA (`acabar de + inf` reads as "has just done X") ->
`acabi la sincronització`.
- chats.ai_greeting.there: `a tothom` -> `''` (the EN fallback "Hey there"
is singular; literal CA `tothom` is plural and wrong for 1:1 chat).
- transactions.split_parent_row.split_label: `Divideix` (imperative) is
wrong as a status badge -> `Divisió` (noun).
- transactions.keep_both (2 occurrences): infinitive `mantenir ambdues` ->
imperative `mantén-les totes dues` to match the sibling Yes/No buttons.
- rules.clear_ai_cache: `Reinicia` (restart) -> `Buida` (empty/clear),
which matches the success notice (`s'està netejant`).
Moniker gender breakage (cross-file)
%{moniker} is interpolated downcased from family.moniker_label and may
resolve to feminine `família`/`llar` or masculine `grup`. Strings that
hard-code a gendered article ('al teu %{moniker}', 'aquesta %{moniker}',
'aquest/a %{moniker}') broke on at least one branch. Restructured the
affected sentences to drop the gendered determiner:
- account_sharings.show.no_members
- merchants.family_empty / family_title / provider_empty
- registrations.new.join_family_title
- settings.preferences.show.currencies_subtitle / sharing_subtitle
- simplefin_items.select_existing_account.no_accounts_found
- invitations.new.subtitle
- invitation_mailer.invite_email.subject (mailers/) + body (views/)
- snaptrade_items.providers.snaptrade.free_tier_warning
Terminology consistency
- models/account_statement/ca.yml attributes aligned with view-side
forms: `Saldo d'obertura`/`Saldo de tancament` ->
`Saldo inicial`/`Saldo final`; `Suggeriment de...` -> `Pista de...`.
- account_statements.coverage.status.not_expected:
`No s'esperava` -> `No previst` (status label, not past action).
- account_statements.index.empty_unmatched: aligned with the section's
own label `Safata sense aparellar`.
- imports.create.document_provider_not_configured + document_upload_failed:
`arxiu vectorial` -> `magatzem vectorial` (correct TermCat term).
- coinstats_items blockchain gender: `els blockchains` / `un blockchain` ->
`les blockchains` / `una blockchain` (feminine per TermCat).
- accounts.account.remove_default: `Treu el predeterminat` ->
`Treu com a predeterminat` (pairs with sibling `Estableix com a
predeterminat`).
- accounts.tax_treatments.tax_deferred: `Diferit fiscalment` (lit. calque)
-> `Tributació diferida` (standard CA tax-accounting term).
- settings.payments.show.currently_on_plan: `Actualment al` ->
`Actualment al pla:` (was a fragment).
Out of scope (review flagged, not applied here)
- LOW-severity stylistic preferences (Veure vs Mostra, etc).
- `models/category/ca.yml` default category names — seeded at family
creation, not via I18n at runtime, so changes wouldn't affect existing
families.
- `models/period/ca.yml` short labels mixing EN (MTD/YTD) and CA (STD/MA)
— needs a one-convention decision separately.
* fix(i18n,ca): drop gendered article in period_activity + tighten cash-flow terms
- pages.dashboard.investment_summary.period_activity: 'Activitat del
%{period}' contracted 'del' = 'de el' (masc.sg.). %{period} resolves
to mixed forms ('Setmana en curs' fem, 'Últims 30 dies' pl., 'Any en
curs' apostrophe), so hard-coded 'del' was wrong on most labels.
Replaced with 'Activitat — %{period}' (em-dash) to skip the
contraction entirely.
- pages.dashboard.outflows_donut.title / total_outflows: switched from
bare 'Sortides' / 'Total de sortides' to 'Sortides de caixa' /
'Total de sortides de caixa' to match TermCat's precise term
('sortida de caixa' = cash outflow).
* fix(i18n,ca): rephrase transfer source/destination amount labels
'Import d'origen' / 'Import de destinació' were literal calques of
'Source amount' / 'Destination amount'. In a multi-currency transfer
form (sender/receiver in different currencies) the natural CA pair is
'Import enviat' / 'Import rebut'.
* fix(i18n,ca): 'Dades en brut' -> 'Dades sense processar'
The literal calque of 'Raw data' read as too technical for personal-
finance UI. 'Dades sense processar' is the more natural Catalan
equivalent for raw/unprocessed data files.
* fix(i18n): localize Import col_sep label + separator options
The CSV upload form rendered 'Col sep' (the auto-humanized attribute
name) plus hardcoded English 'Comma (,)' / 'Semicolon (;)' options
from Import::SEPARATORS.
- activerecord.attributes.import.col_sep added (en + ca: 'Column
separator' / 'Separador de columnes').
- Import.separator_options class method returns translated tuples;
view switched from Import::SEPARATORS to Import.separator_options.
- activerecord.attributes.import.col_seps.{comma,semicolon} added so
the option labels follow the active locale.
* fix(i18n,ca): drop moniker apposition in sharing/currencies section titles
- sharing_title 'Compartició de %{moniker}' rendered as 'Compartició
de Família' (a noun-noun apposition that's odd in CA) -> 'Compartició
de comptes'.
- sharing_subtitle replaced '%{moniker}' with 'entre els membres' so
the sentence reads naturally and doesn't depend on moniker gender.
- currencies_title 'Divises de %{moniker}' had the same apposition
-> 'Divises'. Subtitle no longer references moniker either.
* fix(i18n,ca): keep 'Self Hosting' untranslated
Reverted 'Autoallotjament' / 'autoallotjada' / 'autoallotjats' usages
to the original English 'Self Hosting' (sidebar label, breadcrumbs,
hostings page title, chat assistant settings hint, redis configuration
subheading, LLM usages cost-estimates description).
The brand-style term reads more naturally in EN for technical users
configuring their own deployment.
* fix(i18n,ca): lowercase 'self hosting' (sentence case in labels)
* fix(i18n): extract budget_categories stepper + allocation_progress strings
Hardcoded English strings on the budget category editor:
- 'Setup' / 'Categories' stepper labels in budgets/_budget_nav.html.erb
- 'X% set' / '> 100% set' / 'left to allocate' / 'Budget exceeded by ...'
in budget_categories/_allocation_progress.erb
- '/m avg' caption + 'Shared' placeholder + 'Leave empty to share
parent's budget' tooltip in budget_categories/_budget_category_form
and _uncategorized_budget_category_form
Extracted to:
- budgets.budget_nav.{setup,categories}
- budget_categories.allocation_progress.{percent_set,over_set,left_to_allocate,budget_exceeded_html}
- budget_categories.budget_category_form.{monthly_average,shared_placeholder,shared_title}
CA translations added; EN keys mirror the prior literals.
* chore(i18n): drop translation tooling from PR
These were dev-only helpers used during the Catalan translation pass:
- script/lt_check_ca.rb: LanguageTool API checker (premium/free
endpoint, picky mode, batching). Useful for ongoing locale QA but
shouldn't ship in this feature PR.
- lib/tasks/i18n_screenshot.rake: rake task that flips user.locale and
role on the demo user for walking the i18n surfaces locally.
Both stay available locally; pulled out of the PR scope.
* fix(i18n): apply PR review feedback (CodeRabbit + Codex)
- balance_reconciliation crypto_items: use :end_balance_crypto tooltip
(was :end_balance_investment). Added new UI.account.balance_reconciliation.tooltips.end_balance_crypto key in en + ca.
- doorkeeper.ca.yml confidentiality.no: was YAML boolean false, now string 'No'.
- views/categories: 'Poor contrast, choose darker color or' continued with hardcoded 'auto-adjust.' button text; extracted to categories.form.auto_adjust key (en + ca).
- imports.create.document_upload_failed: 'a l'magatzem' was broken
contraction -> 'al magatzem'.
- invitation_mailer body + mailer subject: 'unir-se' -> 'unir-te' (was
3rd person, should be 2nd to match the rest of the copy).
- 7 strings across mercury_items / sophtron_items / simplefin_items /
lunchflow_items / brex_items / indexa_capital_items / other_assets:
'se sincronitzaran' -> 'es sincronitzaran', 'se segueixen' ->
'es segueixen' (correct reflexive pronoun before consonants).
- settings.providers.status: key was 'false' (YAML-coerced), now 'off'
to match settings/en.yml status.off used in view lookups.
- sophtron_items.sophtron_setup_required.message: stripped trailing
blank line from the quoted scalar.
- settings/profiles/show.html.erb: switched 'family_moniker ==
"Group"' branch checks to 'Current.family&.moniker == "Group"'.
After Family#moniker_label started returning translated values,
callers using the display label for branching would render the
household copy for group families in ca. Compare the stored sentinel
instead.
- Did not apply CodeRabbit's webauthn 'eliminada' -> 'desada' suggestion:
the key is wired to the destroy action (verified at
settings/webauthn_credentials_controller.rb:55), so 'eliminada' is
correct.
|
||
|
|
4fd460d551 |
Add Actual Budget CSV import flow (#1830)
* Add Actual Budget CSV import flow * Address Actual import review feedback |
||
|
|
0c126b1674 |
feat(i18n): extract hardcoded English strings to locale files (#1806)
* Extract hardcoded strings to i18n
Replace numerous hardcoded English strings with I18n lookups (t / I18n.t) across controllers, views, helpers, and components, and convert model validation error messages to symbol keys. Added multiple locale files under config/locales for models and views. This centralizes user-facing notices/alerts, UI text, import/validation messages, and prepares the app for localization and easier translation maintenance.
* Update en.yml
* Update preview-cleanup.yml
* Revert "Update preview-cleanup.yml"
This reverts commit
|
||
|
|
e59235fdc5 |
feat(statements): add account statement vault (#1753)
* feat(statements): add account statement vault Add web-only statement uploads, account linking, duplicate detection, and per-account coverage/reconciliation checks without mutating transactions. Extend ActiveStorage authorization and targeted tests for family/account scoping. * fix(statements): return deleted account statements to inbox Preserve linked statement records when an account is deleted by moving them back to the unmatched inbox, then expand coverage for upload validation, sanitized parser metadata, unavailable reconciliation, and missing-month coverage. * fix(statements): harden vault upload review flows Address review and security findings in the statement vault by preserving sanitized parser metadata, failing closed on orphaned statement blobs, avoiding account_id mass assignment permits, and adding regression coverage for link/delete edge cases. * fix(statements): harden vault upload and access controls * fix(statements): address vault hardening review * fix(statements): address vault review feedback Prioritize SHA-256 duplicate detection while preserving MD5 fallback for legacy rows. Remove free-form account notes from statement matching, document direct account-destroy unlinking, and add year-selectable historical coverage with muted out-of-range months. * fix(statements): harden vault review follow-ups Clarify legacy MD5 checksum use, whitelist statement balance helper dispatch, and preserve sanitized parser metadata. Hide statement management controls from read-only viewers while keeping server-side authorization unchanged. * fix(statements): repair settings system coverage Allow the changelog provider lookup in the self-hosting settings system test, include Statement Vault in settings navigation coverage, and align the feature title casing. Update the devcontainer so ActiveStorage and parallel system tests can run in the documented environment. * fix(statements): move vault beside accounts Place Statement Vault with account settings instead of between Imports and Exports. Keep settings footer ordering and system navigation coverage aligned, including the non-admin visibility guard. * fix(statements): address vault review cleanup Resolve CodeRabbit review feedback for statement upload validation, duplicate race handling, account statement matching semantics, metadata detection, ActiveStorage authorization tests, and small UI/style cleanups. * fix(statements): address vault cleanup review * fix(statements): deduplicate vault style helpers * fix(statements): close vault review follow-ups * fix(statements): refresh schema after upstream rebase * fix(statements): process vault uploads sequentially * fix(statements): close vault review follow-ups * fix(statements): scope vault index to accessible accounts * fix(statements): harden statement vault readiness Squash the statement vault migration hardening into the feature migration, tighten Active Storage authorization edge cases, bound CSV metadata detection, and add real PDF fixture coverage for stored statements. Validation: targeted statement/auth/controller/provider tests, full Rails suite, system tests, RuboCop, Biome, Brakeman, Zeitwerk, importmap audit, npm audit, ERB lint, CodeRabbit, and Codex Security all passed locally. * fix(statements): close vault review follow-ups Move statement unlinking to after account destroy commit, keep Kraken account creation on the shared crypto helper, and add statement metadata length limits with DB checks. Validation: fresh devcontainer with fresh DB via db:prepare, focused account/statement/Kraken/Binance tests, RuboCop, Brakeman, Zeitwerk, git diff --check, CodeRabbit, and Codex Security passed before commit. * fix(statements): address vault scan follow-ups Move statement tab data setup out of the ERB partial, harden reconciliation labels and coverage initialization, and tighten statement schema constraints. Validation: CodeRabbit and Codex Security reviewed the current PR diff; Rails focused tests, full Rails tests, system tests, RuboCop, Brakeman, Zeitwerk, ERB lint, npm lint, importmap audit, npm audit, and git diff --check passed. * fix(statements): defer vault tab loading --------- Signed-off-by: Juan José Mata <juanjo.mata@gmail.com> Co-authored-by: Juan José Mata <juanjo.mata@gmail.com> |
||
|
|
95f6451b39 |
feat(sync): add Brex provider connections (#1752)
* feat(sync): add Brex provider schema Adds Brex item and account tables with per-family credentials, scoped upstream account uniqueness, encrypted token storage, and sanitized provider payload columns. * feat(sync): add Brex provider core Adds Brex item/account models, provider client and adapter support, family connection helpers, and provider enum registration for read-only Brex cash and card data. * feat(sync): add Brex import pipeline Adds Brex account discovery, linked-account sync, cash/card balance processors, transaction import, sanitized metadata handling, and idempotent provider entry processing. * feat(sync): add Brex connection flows Adds Mercury-style Brex connection management, explicit item-scoped account selection and linking, settings provider UI, account index visibility, localized copy, and per-item cache handling. * test(sync): cover Brex provider workflows Adds targeted coverage for Brex provider requests, adapter config, item/account guards, importer behavior, entry processing, and Mercury-style controller flows. * fix(sync): align Brex API edge cases Tightens Brex account fetching against the official card-account response shape, sends transaction start filters as RFC3339 date-times, and keeps provider error bodies out of user-facing messages while expanding provider client guard coverage. * fix(sync): harden Brex provider integration Restrict Brex API base URLs to official hosts, tighten account-selection UI behavior, and add tests for invalid credentials, cache scoping, and provider setup edge cases. * test(sync): avoid Brex secret-shaped fixtures * refactor(sync): extract Brex account flows * fix(sync): address Brex provider review feedback * fix(sync): address Brex review follow-ups Move remaining Brex review cleanup into focused model behavior, tighten link/setup edge cases, localize summaries, and add regression coverage from CodeRabbit feedback. Also records the security-review pass as no-findings after diff-scoped inspection and Brakeman validation. * refactor(sync): split Brex account flow controllers Route Brex account selection and setup actions through small namespaced controllers while keeping existing URLs and helpers stable. Business flow remains in BrexItem::AccountFlow; the main Brex item controller now only handles connection CRUD, provider-panel rendering, destroy, and sync. * fix(sync): address Brex CodeRabbit review * fix(sync): address Brex follow-up review * fix(sync): address Brex review follow-ups * fix(sync): address Brex sync review findings * fix(sync): polish Brex review copy and errors * fix(sync): register Brex provider health * fix(sync): polish Brex bank sync presentation * fix(sync): address Brex review follow-ups * fix(sync): tighten Brex setup params * test(api): stabilize usage rate-limit window * fix(sync): polish Brex setup flow nits * fix(sync): harden Brex setup params * fix(sync): finalize Brex review cleanup --------- Signed-off-by: Juan José Mata <juanjo.mata@gmail.com> Co-authored-by: Juan José Mata <juanjo.mata@gmail.com> |
||
|
|
ce5d7dd736 |
Add Interactive Brokers Provider (#1722)
* Display multi-currency holdings correctly * Implement IBKR provider * Fix: Use historical exchange rate for historical prices * Add brokerage exchange rate for trades * Sync historical balances from IBKR * Add logos in activity history * Fix privacy mode blur in account view * Improve IBKR XML Flex report parser errors |
||
|
|
be598aecf0 |
feat(providers): add Kraken exchange sync (#1759)
* feat(providers): add Kraken exchange sync Adds family-scoped Kraken API-key connections, read-only balance and trade import, account setup/linking flows, provider status wiring, and focused test coverage. Closes #1758 * test(providers): avoid Kraken sample secret false positive * fix(providers): address Kraken review findings * fix(providers): address Kraken review cleanup * test(imports): stabilize transaction import ordering |
||
|
|
f6f9feba8a |
Bank Sync cleanup (#1710)
* feat(settings/providers): surface connection status in section headers
Lifts the per-panel status indicator up to each collapsed accordion
header so admins can see at a glance which providers are connected
without expanding every section. Connected providers sort first.
- Add optional status: and meta: locals to settings/_section partial;
pill hides via group-open:hidden when the section is expanded
- New settings/providers/_status_pill partial (ok/warn/err/off states)
- Add SettingsHelper#provider_summary to centralise the connected-vs-not
logic already scattered across panel partials
- Refactor show.html.erb to pass status to every section and sort
family_panels by connection state
- Add settings.providers.status.* i18n keys
- Add system tests asserting pill renders and sort order
https://claude.ai/code/session_01KW2HCN9rP1fiyQuw7Cju9D
* feat(settings/providers): group providers into Connected and Available
Partition the provider list in the controller into @connected_providers
and @available_providers based on provider_summary status, and render
each group under its own heading with a count. Auto-open the section
when only one provider is connected. Adds an empty-state line when
nothing is connected yet.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(settings/providers): health strip, action-needed group, and sync error surfacing
- Extend provider_summary to return :err/:warn with meta text by checking
latest sync per item (window function, same pattern as ProviderConnectionStatus)
and Enable Banking session expiry within 7 days
- Partition provider entries into three groups: Connected (:ok), Action needed
(:warn/:err, auto-opened), Available (:off)
- Add Settings::HealthSummary ViewComponent — four-tile grid showing Connected,
Action needed, Errors, and Accounts synced counts
- Render health strip directly under page description; omit Action needed heading
when group is empty
- Add i18n keys for tile labels, group heading, and all meta strings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(settings/providers): card grid for available providers with connect drawer
- Add Provider::Metadata registry with static display data (region, kind,
tier, maturity, logo) for all 11 providers
- Add Settings::ProviderCard ViewComponent rendering logo square, name,
Beta/Alpha pill, meta line (region · type · tier), tagline, and Connect link
- Add connect_form action + route (GET /settings/providers/:key/connect_form)
that opens the existing panel partial or config form in a DS::Dialog drawer
- Replace the Available accordion loop with a 2-column responsive card grid;
empty state when all providers are connected
- Fix layout override: use turbo_rails/frame layout for frame requests so the
drawer response is not wrapped in the full settings layout (was causing
Turbo to pick the empty outer drawer frame instead of the filled one)
- Add SyncAllProvidersJob and last_sync_all_attempted_at migration (sync-all
throttle support)
- Unify Connected + Action needed into a single "Your connections" section;
items with warn/err status auto-open
- Fix Enable Banking grouping: items with expired sessions were returning
:off (Available) instead of :warn (Your connections); gate now checks
any? instead of any?(&:session_valid?)
- Add reconsent_required locale key for fully-expired EB sessions
- Surface Beta/Alpha maturity pills on connected provider accordion rows
via new badge: param on settings_section helper
- Add i18n taglines for all 11 providers; add connect and empty_available keys
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(settings): retire /settings/bank_sync; merge into providers page
- Delete Settings::BankSyncController and its views (the providers page is
now a strict superset of what bank_sync offered)
- Add permanent 301 redirect: GET /settings/bank_sync → /settings/providers
- Collapse nav to a single "Bank Sync" entry pointing at /settings/providers;
remove the duplicate admin-only "Providers" entry from the Advanced section
- Remove "Providers" from SETTINGS_ORDER; point "Bank Sync" at
settings_providers_path for next/prev navigation
- Rename page title to "Bank Sync"; replace admin-credential lede with
user-facing copy ("Connect external accounts…")
- Update breadcrumb: Home → Bank sync
- Add controller test asserting 301 status and Location header
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Migrations are 7.2 here
* Minimize schema noise
* Schema duplication
* Small copy edits
* Fix tests
* Address provider settings review feedback
* refactor(settings/providers): finish design-review cleanup pass
Picks up the remaining items from Claude Design's review of #1710
that the previous review-feedback commit didn't cover.
DS / casing
- Sentence-case the page title ("Bank Sync" -> "Bank sync") and
align the nav label.
- Drop the card hover-lift (shadow-border-sm) in favour of
bg-container-hover; per the DS, card hover is colour-only.
- Whole-tile click target on each provider card — the inner
"Connect ->" link was a hit-target inversion.
- Set Sync all to whitespace-nowrap so the label stops wrapping at
narrow viewport widths.
UX simplifications
- Drop the four health-summary tiles (per-row warn/err pills already
surface the signal at the scale this app sees). Removes
Settings::HealthSummary, the @health_counts controller block, and
the now-unused health.* locale keys.
- Hide "Your connections" heading + empty-state line when no
providers are connected — the lede already invites a connect.
- Drop the redundant "Free" tier from per-card meta lines (printed
10x for one fact); "Paid" still surfaces on Plaid.
Tests updated to drop the obsolete tiles assertion and switch the
provider-card click selector to look up the (now whole-card) anchor
by provider name.
* feat(settings/providers): replace Add another provider CTA with a search + kind filter
Per the design review, the "Add another provider · Browse providers"
card was a redirect to content one scroll-tick away. A search input
plus kind chips lets users self-segment the catalog and is the right
tool once it grows beyond the four to twelve providers we ship today.
- New providers_filter Stimulus controller — case-insensitive free
text search across name/region/kind, plus a chip group with
All / Banks / Crypto / Investment that toggle visibility via
Tailwind's `hidden` class.
- _search_filters partial: search box (count-pluralized placeholder)
+ chip group, ARIA-labelled and aria-pressed for the chips.
- ProviderCard exposes filter_data (target + name/region/kind data
attrs) so the controller can match without re-rendering.
- Lunchflow's `kind` was "Lunch" — switched to "Bank" so it falls
under the Banks chip alongside its actual offering (it aggregates
banks).
- Drops the add_provider_cta partial and its locale entries; adds
search_filters.* and an empty_filter message.
* Private method fix
* refactor(settings/providers): drawer cleanup, header lock-up, trust statement
Per the design review's §07.
- Drop the trailing "Configured / Not configured" footer status from
every provider panel (binance, coinbase, coinstats, indexa_capital,
lunchflow, mercury, simplefin, snaptrade, sophtron, provider_form).
The parent details section's status pill already carries that
signal; the footer was redundant — and the copy/styling was
inconsistent across panels (free-text vs. dot pill, "configured"
vs. "not connected").
- Connect drawer gets a header lock-up: small logo chip + provider
name + maturity badge, mirroring the available-card layout.
Implemented as _drawer_header partial; connect_form passes
custom_header: true to DS::Dialog so we own the row.
- Drawer footer trust statement: "Read-only — Sure can never move
money. Stored encrypted." A single-line reassurance covering all
panels.
- Sentence-case the hardcoded primary buttons that were Title Case:
"Save Configuration" -> "Save and connect"
"Update Configuration" -> "Update connection"
"Connect Bank" -> "Connect bank"
Affects simplefin, lunchflow, enable_banking, provider_form. The
i18n'd panels (binance, coinbase, coinstats, indexa_capital,
mercury, snaptrade, sophtron) keep their existing keys.
* chore(locales): drop unused provider-panel status strings
Footer "Configured / Not configured" status was removed from each
provider panel partial in the prior drawer-cleanup pass; the matching
i18n keys are no longer referenced. Removing them across every
locale to keep the catalogue clean.
Dropped (15 keys × varying locale coverage, 36 line removals across
24 files):
- coinstats_items.new.{status_configured_html, status_not_configured}
- indexa_capital_items.panel.{status_configured_html, status_not_configured}
- mercury_items.provider_panel.{configured_html, not_configured, accounts_link}
- sophtron_items.sophtron_panel.status.{configured_html, not_configured}
(parent `status:` removed where it became empty)
- providers.snaptrade.{status_needs_registration, status_not_configured}
(status_connected stays — still used by the lazy-load summary)
- settings.providers.{binance_panel, coinbase_panel}.{status_connected, status_not_connected}
* feat(settings/providers): connected-state polish per design §05 + Linked institutions rename
Building the next phase of the design review. Pulls forward the
slim health strip, denser connection rows, and "Linked institutions"
heading rename — the small Phase A lift the designer flagged in
§08 of the doc.
- New _health_strip partial: single-line at-a-glance pulse —
connected count + needs-attention count + accounts syncing +
last-synced timestamp. Renders only when at least one provider
is linked or needs action.
- New _connection_row partial replaces the generic settings_section
call for providers. Tighter rows: text-sm title (was text-lg),
px-4 py-3.5 padding, single-line summary (chevron + name +
maturity badge + meta + status pill + sync action). Warn/error
rows get a coloured outline (border-warning/25 or
border-destructive/25) so the at-risk row stands out without
shouting.
- "Sync all" button restyled to match the design's secondary
button: text-primary, alpha-black-100 border, rounded-[10px],
padding 7px 12px (was the broader px-3 py-1.5 ghost).
- "Your connections" → "Linked institutions" heading, lifted from
the designer's Phase-C reconciliation note. Primes users for the
Option-C institution-search wizard six months early; existing
i18n key stays as `groups.your_connections` for now to keep the
rename to a single value flip.
- Controller computes the new @health hash (connected,
needs_attention, accounts_syncing, last_synced_at) feeding the
strip; brings back the single accounts query that was removed
with the four-tile component.
System test updated for the new heading copy.
* fix(settings/providers): align connected state with the final design mock
Tightening the §05 polish to match the user-confirmed final design.
- Revert "Linked institutions" → "Your connections". The §08
designer note about the Phase-A heading rename didn't carry
forward to the final mock; keep the original wording.
- Drop the warn/err auto-open on connection rows. The design shows
Enable Banking collapsed with a warn-outline and a status pill —
no auto-expanded form. Single-connection auto-open kept (handy
when the page is otherwise empty).
- Hide the "accounts syncing" segment in the health strip when the
count is 0 — the design mock assumes a populated number; an
always-visible "0 accounts syncing" reads as a placeholder.
- Strip the leading "about " from `time_ago_in_words` everywhere
the result is shown to the user (health strip "Last synced %{time}
ago" plus per-row "Synced %{time} ago" meta). Matches the design's
shorter copy.
* refactor(settings/providers): tighten paddings, dedupe maturity badge, semantic + a11y fixes
Pixel-level alignment to the design's §05 mock + cleanup from a DS
audit pass.
Paddings, margins, font sizes
- Health strip: my-4 → mt-4 mb-5 to match the design's 16px / 20px
vertical breathing room.
- Search filters bar: gap-2 → gap-2.5; mt-2 → mt-5 mb-3 (was missing
the 12px bottom margin entirely).
- Search box: rounded-lg → rounded-[10px]; px-3 py-2 → px-[14px]
py-[9px]. Search icon downsized w-4 → w-3.5 to match.
- Chip group: p-1 → p-[3px]; rounded-lg → rounded-[10px].
- Chip: py-1 → py-[5px]; rounded-md → rounded-lg.
- Group heading: mt-2 → mt-[18px]; mb-1 → mb-1.5.
- Status pill: text-xs → text-[11px].
- Provider card: gap-3 → gap-2.5 (outer + top); name gets explicit
text-sm; tagline + foot 14px → 13px; arrow icon w-4 → w-3.5.
- Sync icon button: p-1 → fixed w-7 h-7 (28×28) so the row hit
target matches the design's column width.
- Connect drawer header logo glyph: text-[10px] → text-xs (matches
the available card's logo-glyph treatment).
Component / partial cleanup (DS audit follow-ups)
- New _maturity_badge partial replaces the inline span that was
duplicated in 3 places (_connection_row, _drawer_header,
provider_card.html.erb).
- Settings::ProviderCard.maturity_label class method centralizes the
MATURITY_LABELS lookup; callers no longer reach into the constant.
- _connection_row title: <h2> → <h3> (the row sits inside the
"Your connections" h2 group heading; nested h2s flattened the
outline).
- show.html.erb encryption error: <h3> → <h2> for the same reason.
Locale
- Drop orphaned keys: settings.providers.groups.connected and
groups.needs_attention (no view code uses them) plus the leftover
show.coinbase_title block.
- Health strip "needs reconsent" → "needs attention" so the strip
copy lines up with the per-row status pill ("Action needed") and
the original group heading wording.
A11y
- focus-visible:ring-2 on chip buttons, provider-card link, and
focus-within:ring-2 on the search input wrapper. Keyboard users
now get a visible focus state.
- Search input: explicit autocomplete="off" (erb_lint hint).
* fix(settings/providers): icons + search input height
- Icons were rendering at 20px because the application_helper's `icon`
default size (`md` = w-5 h-5) was beating the inline class override
in compiled CSS source order. Pass `size: "sm"` and use the project's
`!w-3.5 !h-3.5` important-prefix pattern (precedent: dashboard.html.erb)
so chevron, refresh-cw, search, check, circle-alert, and arrow-right
all render at the design's 14px.
- Search input was 54px tall because @tailwindcss/forms applies
`padding: 8px 12px` to bare `<input type="search">`. Override with
`!p-0 focus:ring-0 focus:shadow-none` so the wrapping div's padding
alone defines the box (38px total — matches the design).
* refactor(settings/providers): align Sync all + search input with DS, address review feedback
- Sync all: replace the hand-rolled `button_to` with `DS::Link.new(variant: "outline", method: :post)` — same component as the
"Identify Patterns" button on the recurring-transactions page.
- Search input: switch to the icon-overlay pattern used by the
Manage-currencies and transaction filter rows
(relative wrapper + absolutely positioned search icon +
bordered input with `focus:ring-gray-500`). Brings the keyboard
focus state in line with the rest of the app's filterable lists.
- SnapTrade panel: restore the "needs registration" status row that
the drawer-cleanup pass dropped along with the redundant
Configured/Not configured footer. The unregistered case is
meaningful state, not redundant chrome.
- Move the slim health-strip computation out of the controller and
into `SettingsHelper#provider_health_strip` (Convention 2: skinny
controllers).
- Extract `concise_time_ago` helper so the "drop leading 'about '"
trick stops being duplicated 3x.
- `Settings::ProviderCard#maturity_label` (instance) now delegates
to `.maturity_label` (class) instead of duplicating the lookup.
- Drop unused `warn_or_err` local in `_connection_row`.
- Replace the `data-controller` string-injection + html_safe in
`_connection_row` with `tag.details(data: ...)`; safer and more
idiomatic.
- Add a system test for the empty-filter message wiring.
* fix(settings/providers): drawer trust statement uses border-tertiary
`border-secondary/10` was reaching for the text-foreground token at
10% opacity for a divider. The project ships a dedicated divider
token (`border-tertiary`, ~8% black) used by DS::Menu, the holdings
page, and admin/sso forms. Switching to it makes the trust-statement
HR match every other thin divider in Sure and stops misusing the
text token as a border.
* refactor(settings/providers): swap arbitrary Tailwind values for scale tokens
Per the user's directive — DS-compliance over pixel-perfect alignment
with the design mock. Walked the design audit and applied every swap
that lands within ±2px of the original.
Swaps:
- _health_strip: gap-[18px] → gap-5 (+2), px-[14px] → px-3.5 (=),
text-[13px] → text-sm (+1).
- _search_filters: chip group p-[3px] → p-1, rounded-[10px] →
rounded-xl (concentric with rounded-lg inner pills), chip py-[5px]
→ py-1.
- _status_pill: text-[11px] → text-xs.
- _group_heading: mt-[18px] → mt-5.
- _maturity_badge: text-[10px] → text-xs.
- provider_card: tagline + foot text-[13px] → text-sm.
Kept arbitrary: `min-w-[200px]` in _search_filters — nearest scale
tokens are min-w-48 (192px) and min-w-52 (208px); both are noticeable
layout shifts for a one-off responsive guard. Worth keeping the
arbitrary here.
Net: 9 of 10 arbitrary values gone. Visual delta: max +2px on a
single value. Design mock and DS scale now agree.
* revert(settings/providers): drop the slim health strip
Per-row status pills already carry the at-a-glance signal (connected
/ action needed) at the scale this app sees (1–4 connections per
family). The strip was redundant chrome for almost every user; only
worth bringing back if the catalog grows to a point where the row
list itself stops fitting on a single screen.
- Delete _health_strip.html.erb partial.
- Drop @health controller assignment + provider_health_strip helper.
- Drop unused settings.providers.health_strip.* locale keys.
- concise_time_ago helper stays — still used by per-row meta text.
* refactor(settings/providers): align with DS conventions
Two consistency wins from the screenshot/DS audit pass.
Sync icon button now renders DS::Button (variant: icon, size: sm)
instead of a hand-rolled `button_to`. Same component used by other
icon-only actions across the app (settings/profiles, layouts/imports).
Visual delta: 28×28 → 32×32 (DS sm size). Accept the +4px for
consistency. `event.stopPropagation()` still wired via the form opt
so the row's <details> doesn't toggle when the user clicks the
button.
Group heading now follows the established Sure section-label style
(`text-xs font-medium text-secondary uppercase`) used by
`_settings_nav` and the imports/categories surfaces. The previous
sentence-case `text-sm text-primary` was a one-off that didn't
match the rest of the app. Locale strings stay sentence-case;
uppercase comes from CSS `text-transform`. Tests updated to
case-insensitively match the rendered heading text.
* fix(provider/metadata): add plaid_eu entry
`plaid_eu` is registered as a separate Provider::ConfigurationRegistry
entry but had no Provider::Metadata row, so its card in the
Available grid fell through to the gray-500 default and rendered
empty (no region, kind, tier, or tagline). The title also came out
as "Plaid Eu" because `titleize` doesn't know "EU" is an initialism.
- Add a `plaid_eu` row to Provider::Metadata::REGISTRY with the same
shape as `plaid` (US → EU, otherwise identical).
- Introduce an optional `name:` field in metadata; controller falls
back to it before titleizing the provider key. Lets `plaid_eu`
render as "Plaid EU".
- Add the missing `settings.providers.taglines.plaid_eu` translation.
* fix(settings/providers): center-align Sync all next to the lede
`items-start` made the button hug the first line when the lede wrapped;
on a single line the button sat at the top of the text bounding box
which read slightly off. Center matches the dominant convention
across the rest of settings (api_keys, securities, hostings, _section,
_settings_nav_link_large).
* fix(settings/providers): drop colour palette + filter polish + drawer warnings
Round of design-feedback fixes.
Provider chips
- Drop the per-provider raw Tailwind palette (bg-blue-600 etc.) from
Provider::Metadata. All cards + drawer logo lock-up now use
bg-surface-inset + text-primary, matching the design's §04 "drop
colour entirely" recommendation. Solves the long-standing §01
BLOCKER without externalising brand assets. Re-introducing logos
later just means an optional logo_svg: field on metadata.
- ProviderCard component drops the `logo_bg:` parameter; the chip
is now styled in the template.
Filter / search
- "Available · N" count and the empty-filter state now update
client-side as the chip filter and free-text search narrow the
grid (new `count` Stimulus target + dedicated update path).
- Empty-filter state now offers a Clear filters button that resets
both the search input and the active chip in one click.
- Search placeholder drops the drifting "Search 9 providers" count
for plain "Search providers" — the section heading carries the
number.
- Chip labels normalised to plural where natural: "Banks · Crypto ·
Investments" (Crypto stays as the mass noun).
Drawer copy / treatment
- "IP Whitelisting Required" → "IP whitelisting required" (DS
sentence-case).
- Binance "do NOT enable withdrawal permissions" lifted out of
inline red-text into a proper bg-warning-50 border-warning-200
alert block with an alert-triangle icon. Matches the api_keys /
hosting alert pattern.
- SnapTrade free-tier inline alert-triangle now uses `size: "sm"`
so the icon stops rendering at 20px next to 14px body text.
Spacing
- Group-heading margin top bumped 5 → 6 (20→24px) so the eyebrow
has more breathing room above the search bar.
* refactor(settings/providers): drawer alerts use DS::Alert; drop card-in-card
Two consistency fixes from a design-review pass.
DS::Alert adoption
- Replaces 9 hand-rolled error blocks across the provider panels
(`bg-destructive/10 text-destructive ... line-clamp-3`) with
`DS::Alert(variant: :error)` — the project's existing primitive.
- Replaces the just-shipped Binance no-withdraw warning block with
`DS::Alert(variant: :warning)` instead of a hand-rolled
`bg-warning-50 border-warning-200` card.
- Replaces the SnapTrade free-tier inline icon-prefixed warning
paragraph with `DS::Alert(variant: :warning)` — proper alert
treatment for an actual warning, not body copy.
- Replaces the Enable Banking "Configuration locked" inline
`bg-warning/10` two-paragraph block with `DS::Alert(variant: :warning)`
using `safe_join` for the title + body.
- Replaces the encryption-error block at the top of show.html.erb
with `DS::Alert(variant: :error)`, again via `safe_join`.
Mercury card-within-card
- The "Add another Mercury connection" form was wrapped in a
`<details>` `bg-container shadow-border-xs rounded-xl` card. In
the Connect drawer (always 0 existing connections), that wrapping
card-inside-the-drawer-card has no value — the form is the only
thing on the surface. Drop the wrapper when no connections exist;
keep the heading + form inline. When 1+ connections exist (the
section page) the heading hints "+ Add another connection"
without the disclosure indirection.
Trade-off: the error-alert blocks lose their `line-clamp-3` /
`title=` truncation. Acceptable for now — DS::Alert can grow a
truncate option as a follow-up if needed.
Open follow-up: DS::Alert itself uses raw Tailwind palette
(`bg-yellow-50` etc.) instead of semantic tokens, and only accepts
a single string `message:`. A separate issue tracks this.
* fix(settings/providers): hoist warning alerts to top of drawer
DS::Alert convention across the rest of the app: alerts sit at the
top of the form / page / section, not floating between content
blocks. The Binance no-withdraw warning and SnapTrade free-tier
warning were rendering between the setup-instructions list and the
form fields — visually wonky.
Move both to the top of their respective panels so the warning is
the first thing the user sees when the connect drawer opens.
Existing precedents this aligns with:
- accounts/_form.html.erb (error alert above form)
- valuations/new.html.erb (error alert above form)
- other_assets/new.html.erb (info alert above form)
- holdings/show.html.erb (warn alerts above content)
* fix(DS::Alert): align icon to cap-height of first text line
`items-start` on the container made the icon's top edge flush with
the text's top edge, leaving the icon's optical center sitting below
the text's first-line center. The hand-rolled alerts elsewhere in
the codebase (api_keys/new, hostings/_sync_settings, holdings/show)
all add `mt-0.5` to the icon for the same reason — fold that into
the primitive so every caller gets the cap-height alignment.
* copy(settings/providers): tighten alert messaging per voice review
Copy expert pass on the new provider drawer alerts. House style:
sentence case for titles, lead with the action, drop "Warning:" /
"Please" filler (the alert variant icon already signals tone),
prefer one short sentence + optional title-paragraph for emphasis.
- Binance no-withdraw warning: was a single line "Warning: do NOT
enable withdrawal permissions" — alarmist without context. Now
splits into "Read-only key only" (title) + "Don't enable
withdrawal permissions when creating your Binance API key — Sure
only needs read access." (body).
- SnapTrade free-tier note: "Free tier includes 5 brokerage
connections. Additional connections require a paid SnapTrade
plan." → "SnapTrade's free tier covers 5 brokerage connections.
Upgrade on SnapTrade for more."
- SnapTrade connection-limit-info inside the brokerage list: cut
entirely. The drawer already shows the cap; restating it in the
list was noise.
- SnapTrade needs-registration: "Credentials saved — finish
registration to connect a brokerage." → "Credentials saved.
Finish setup to connect a brokerage." ("registration" was
ambiguous — register where, with whom?)
- Enable Banking "Configuration locked" body: "Credentials cannot
be changed while you have active bank connections. Remove all
connections first to update credentials." → "Disconnect all
linked banks before changing these credentials." Same meaning,
half the words.
- Encryption-error block: title-cased "Encryption Configuration
Required" → "Encryption keys missing"; body strips "Please
ensure" filler and the parenthetical credential dump, leaving
the three credential names inline as a clean list. Self-hosters
still get exactly the names they need to set.
* feat(settings/providers): SetupSteps partial for connect-drawer instructions
Per the design's drawer-cleanup follow-up. Replaces the per-panel
"Setup instructions:" + ordered list + "Field descriptions:" block
with a shared boxed-step component.
The new partial — `_setup_steps.html.erb` — takes a `steps:` array
of strings (or html_safe strings for inline links / code) plus an
optional `help:` hash for a docs link below the steps. The eyebrow
label is "Setup" (uppercase, tracking-wider) matching Sure's other
section labels.
Applied across all eleven provider panels:
- _provider_form (Plaid + Plaid EU): field descriptions move to
per-field helper text below the input.
- _binance, _coinbase, _coinstats, _indexa_capital,
_lunchflow, _mercury, _simplefin, _snaptrade, _sophtron,
_enable_banking: ordered list + duplicate "Field descriptions"
block both replaced by the partial.
- Some panels' inline copy tightened in the same pass (Lunch Flow,
SimpleFIN, Enable Banking) — the design copy is shorter than the
current legacy strings; a copy-pass through every panel can
follow as a separate cleanup.
Token notes: uses scale tokens (`rounded-xl`, `text-xs`/`text-sm`,
`tracking-wider`) instead of the design mock's exact arbitrary
values, per the consistency-over-design-specs directive on this
branch.
* fix(settings/providers): tighten panel spacing + relocate per-panel notes
Read-flow audit on each connect drawer. The uniform `space-y-4`
treated every block (alert, steps, info card, fields, button) the
same — visually they were five sibling boxes with no grouping. The
fix is per panel; some notes belong as helper text on a specific
field, others as a tightly-grouped pre-fill primer.
Per panel:
- Binance: IP-whitelisting card now matches the setup_steps box
(`bg-surface-inset rounded-xl`) and is wrapped with setup_steps
in an inner `space-y-2` so they read as a single pre-fill primer
cluster. Same eyebrow treatment ("IP whitelisting required") so
the two boxes look like sister panels, not unrelated chrome.
- SnapTrade: drop the description paragraph above setup_steps. The
available-providers card grid already markets SnapTrade
("Connect brokerage accounts via the SnapTrade aggregation
network."); repeating in the drawer was duplication.
- Mercury: move the sandbox-API note out of its standalone <p>
below setup_steps and into per-field helper text under the
base_url field — the user only cares about the sandbox URL when
they're filling that field. Applied to both the per-item edit
form and the add-new form.
- _setup_steps partial: drop the now-pointless `mb-2` (outer
`space-y-4` already controls the gap; bottom-margin was dead
CSS thanks to margin-collapse rules with the next sibling's
margin-top).
* fix(settings/providers): plaid + indexa drawers join the SetupSteps look
Two unifying fixes after the panel-by-panel screenshots showed
mixed treatments.
Plaid + Plaid EU
- The registry-driven panel (_provider_form) was still rendering
each adapter's markdown `description` block as plain prose
("Setup instructions: 1. Visit the Plaid Dashboard ..."). Other
panels switched to the SetupSteps box; Plaid was the odd one out.
- Drop the markdown `description` block from both plaid_adapter
and plaid_eu_adapter. Render setup_steps in _provider_form for
these two provider keys via inline ERB (link helper handles the
Plaid Dashboard link cleanly; the regional differences fold to
the same dashboard URL with a different account scope).
- Other registry-based providers fall through to the previous
markdown description path — no behavior change for them.
Indexa Capital
- The API token field was wrapped in a `bg-surface border` "card"
that duplicated the field label inside as a heading and put the
description above the input. Same pattern the user flagged as
the "card within input" anti-shape.
- Drop the wrapper. The styled-form input renders its own label;
description moves to per-field helper text below the input,
matching the pattern used by Plaid (provider_form) and Mercury.
* fix(settings/providers): surface configured plaid_eu + dedup show context
provider_summary had no plaid_eu branch — configured plaid_eu was
falling through to status :off and rendering in Available even with
credentials set. Collapse plaid + plaid_eu into a single registry
check.
Drawer title for non-panel configurations was provider_key.titleize,
which produced "Plaid Eu" while the available card grid used
metadata[:name] = "Plaid EU". Read from metadata first.
While here:
- compute_provider_sync_health no longer relies on
instance_variable_get; pass family_panel_items explicitly so the
hash-key/ivar-name coupling is gone.
- drop unused .includes(:syncs, :mercury_accounts) and
.includes(:snaptrade_accounts) from prepare_show_context. The show
view only consults summary[:status]; the eager-loads were carried
over from connect_form (which has its own load_provider_items).
* i18n(settings/providers): localize plaid setup steps + drop dead defaults
The plaid + plaid_eu setup steps in _provider_form.html.erb were
hardcoded English strings. Move them to settings.providers.plaid_panel
(shared) + plaid_eu_panel (EU-specific step 1) so they can be
translated like every other panel.
_setup_steps.html.erb was passing default: "Setup" / "Need help?" to
t(), masking missing translations in non-EN locales. Both keys exist
in en.yml — drop the defaults so missing translations actually
surface.
* test(settings/providers): cover plaid_eu, clear filters, warn outline
Three system test additions:
- Configured plaid_eu surfaces in Your connections (regression guard
for the helper fix; previously fell through to Available).
- Clear filters button resets input + chip state and brings cards
back into view.
- :warn-state connection row carries the border-warning/25 outline
that distinguishes it from an :ok row.
* copy(settings/providers): drop em dashes, naturalize phrasing
Sweep through every string this branch added and replace em-dash
splices with full sentences or simple connectives.
en.yml:
- drawer_trust_statement now reads "Read-only access. Sure can never
move money, and your credentials are stored encrypted." instead
of em-dash splicing.
- sync_all_recently / recently_synced split into two sentences.
- binance_panel.no_withdraw_body, plaid_panel.step_1_html / step_2,
plaid_eu_panel.step_1_html same treatment.
Hardcoded panel steps (enable_banking, lunchflow, simplefin) become
"Go to <link> and …" or "Go to <link> for …" instead of the
"<link> — get …" splice. Same setup_steps comment cleaned up.
* fix(settings/providers): address CodeRabbit pass on PR #1717
Fixed:
- Localize the setup steps in _enable_banking_panel,
_lunchflow_panel, and _simplefin_panel. The em-dash sweep had
rewritten these into hardcoded English; they now route through
settings.providers.{enable_banking,lunchflow,simplefin}_panel
step_1_html / step_2 / step_3 keys, mirroring the plaid_panel
treatment.
- connect_form: silent redirect when provider_key is unknown now
carries an alert (settings.providers.not_found) so misrouted
links don't drop users on the page with no feedback.
- sync action: redirect notice now reflects whether anything was
actually scheduled — adds settings.providers.sync_provider_no_items
for the "all items already syncing or none exist" path.
- Family::Syncer test: count plaid_items via the .syncable scope to
match what Family::Syncer actually schedules (already done for
binance_items in the same test).
Skipped, with reasons:
- focus:ring-gray-500/-gray-900 in coinstats / coinbase / simplefin /
search_filters: tracked under issue #1715 as part of the raw-palette
→ DS-token sweep across the whole codebase.
- Coinbase #0052FF brand-color wrapper: tracked under PR #1710's
follow-up tracking comment as the deferred Provider::Metadata
colour-palette decision (designer §01).
- Sophtron submit-button extraction into DS::Button: same
deferred sweep — every panel hand-rolls this class string;
one-off extraction would just churn.
- Redundant .html_safe on _html keys in coinstats: tracked in #1715.
- _provider_form.html.erb env hint, "Optional" placeholder, "Save and
connect" submit: pre-existing strings not added on this branch.
- Renaming sync_health_for's :stale to :data_stale: pre-existing
shape, refactor scope.
- Plaid_eu using plaid_panel.step_2/step_3 keys: deliberate. Same
English copy across both providers; duplicating keys would just
give translators twice the work for identical strings.
- _enable_banking_panel / _lunchflow_panel / _simplefin_panel
alert + submit + button labels: pre-existing hardcoded strings
from before this branch. Setup steps were the strings actually
touched in the em-dash sweep, so those got localized; the rest
belong in a broader panel-i18n pass.
Verified:
- bundle exec erb_lint on the three panels: clean.
- bin/rubocop on controller + test: clean.
- bin/rails test test/models/family/syncer_test.rb
test/controllers/settings/providers_controller_test.rb:
23 runs, 85 assertions, 0 failures.
- DISABLE_PARALLELIZATION=true bin/rails test
test/system/settings/providers_test.rb:
15 runs, 38 assertions, 0 failures.
* fix(db): rename migration to clear collision with main's 20260508120000
Main's PR #1705 (Sophtron manual sync) shipped a migration with
the same 20260508120000 timestamp as our
add_last_sync_all_attempted_at_to_families migration. The merge
that brought main into this branch left both files at the same
prefix, which trips Rails' "Duplicate migration" guard at
db:schema:load time and broke CI.
Renaming our migration to 20260510120000 keeps the column it adds
intact (already in db/schema.rb) and bumps the schema version to
match. No DB-level change.
* fix(settings/providers): card + strip a11y polish
- Bring back the slim health strip; gate behind 10+ accounts
(HEALTH_STRIP_MIN_ACCOUNTS) so it stays out of the way for
small libraries where per-row pills already carry the signal.
- Status pill: drop the bg-{c}/10 text-{c} pattern (failed AA
on warn / err); switch to bg-surface-inset text-primary with
the dot still carrying semantic colour. Passes AA in both
themes; the dot is the only colourful affordance.
- Maturity badge: bg-alpha-black-50 was invisible against the
hovered card bg in light mode and against bg-container in
dark mode. Move to bg-surface-inset + border-tertiary so it
stays delineated through hover and dark theme.
- Provider card: keep the bg shift on hover (now bg-surface-inset
for a perceptible delta), focus ring promoted alpha-black-100
-> alpha-black-300 (visible to keyboard users), meta line
text-subdued -> text-secondary (text-subdued failed AA at
2.86:1 against bg-container).
- Restore the per-provider logo palette dropped in
|
||
|
|
57d71cd55e |
refactor(design-system): extend DS::Alert and migrate 9 inline alert blocks (#1731)
* feat(design-system): add info semantic color token Mirrors success/warning/destructive: --color-info maps to blue-600 in light mode, blue-500 in dark mode. Unblocks the DS::Alert info variant from carrying a raw 'blue-600' literal in icon_color and lets surface tokens use bg-info/N alpha modifiers like the rest of the system. Refs #1715 * refactor(design-system): adopt semantic tokens and add body slot in DS::Alert Replaces the bg-{blue,green,yellow,red}-50 / text-{...}-700 / border-{...}-200 palette block in DS::Alert with semantic alpha-modifier surfaces (bg-{info,success,warning,destructive}/10 + matching /20 borders). Drops the 'blue-600' literal that icon_color was returning for the info variant; helpers#icon now accepts color: :info backed by the new --color-info token. Adds an optional title: kwarg and an opt-in block-content slot so rich alerts (title + paragraph, lists, embedded actions) can render without callers reaching for a hand-rolled flex layout. The existing message: API stays backward-compatible — nothing in the codebase that already calls DS::Alert.new(message: ..., variant: ...) needs to change. Lookbook gains with_title and with_body_slot examples covering the new shapes. Refs #1715 * refactor(views): migrate api_keys, hostings, lunchflow alerts to DS::Alert Cleans up nine bespoke alert blocks that hand-rolled the same flex + icon + bordered-surface shape DS::Alert already provides: - settings/api_keys/{new,created,created.turbo_stream}.html.erb — three near-identical 'Security Warning' / 'Important Security Note' boxes using the broken bg-warning-50 / text-warning-700 raw-palette pair. - settings/hostings/{_alpha_vantage,_eodhd,_yahoo_finance,_twelve_data,_provider_selection}_settings.html.erb — five amber-50 / amber-200 warning boxes covering rate-limit notes, health-check failure messaging, and the env-configured override banner. The twelve_data plan-restriction block keeps its bullet list and pricing link inside the new DS::Alert body slot. - lunchflow_items/{_api_error,_setup_required}.html.erb — two modal alert headers whose flex+icon scaffolding now collapses onto DS::Alert. The surrounding bg-surface 'Common issues' / 'Setup steps' info cards stay as-is; this PR only touches the alert shape itself. No functional or behavioural changes. Locale keys preserved. amber-* palette uses on the alerts disappear; remaining bg-amber-* hits in the codebase live outside the alert pattern and stay for follow-up sub-PRs of #1715. Refs #1715 |
||
|
|
0b7fa732ae |
feat(splits): add exclusion support for splits and improve rendering (#1661)
* feat(splits): add excluded attribute support for split children and improve rendering of split transactions * address coderabbitai suggestions to improve code quality * Fix split excluded coercion, DRY helpers, and clean up view partials Fix boolean coercion bug where string "false" from form params was truthy in Ruby, causing all split children to be marked excluded. Use ActiveModel::Type::Boolean for explicit casting in Entry#split!. Additional changes addressing code review feedback: - Extract duplicated in_split_group logic from TransactionsController and TransactionCategoriesController into TransactionsHelper - Remove redundant local_assigns.fetch calls in partials that already declare defaults via the Rails 7.1 locals: magic comment - Simplify ternary in _transaction.html.erb to pass grouped directly - Guard hidden_field_tag :grouped to only emit when value is "true" - Add model tests for excluded on split children (boolean and string) - Add controller test for excluded param through full HTTP stack - Add test confirming excluded children are dropped from balance queries * fix(splits): simplify excluded attribute boolean check * refactor(splits): extract truthy values constant for excluded check Extract the array of truthy values used for excluded attribute check into a private constant to improve code maintainability and avoid duplication of the magic array. * refactor: simplify split grouping link generation and add test coverage for excluded split parameters |
||
|
|
8abecf8a8d |
feat(exports): preserve transfer decisions (#1639)
* feat(exports): preserve transfer decisions * fix(api): apply transfer date filters to both sides * fix(api): refine transfer decision handling * fix(api): align transfer decision schemas * fix(api): use current context for transfer filters * fix(api): include either side in transfer date filters * fix(api): deduplicate transfer decision filters * fix(api): guard transfer decision exports |
||
|
|
7e1de420ca |
perf(accounts): kill sidebar/sparkline N+1s and cache the sidebar (#1683)
* perf(accounts): kill sidebar/sparkline N+1s and cache the sidebar The dashboard was issuing hundreds of per-account `SELECT 1` and polymorphic `accountable` lookups on every page load. Sidebar render alone hit the DB ~50–100× and ran twice per request (mobile + desktop). Changes: - AccountableSparklinesController: short-circuit `requires_normalized_aggregation?` to Investment/Crypto only and collapse the per-account `linked?` loop into a single `EXISTS`. Kills the N+1 `AccountProvider Exists?` queries on every sparkline endpoint. - BalanceSheet::AccountTotals#visible_accounts: preload `:accountable`, `:plaid_account`, `:simplefin_account`, and `account_providers: :provider` so the sidebar's `account.subtype` / `account.linked?` / `account.provider` calls don't trigger per-row polymorphic loads. - AccountsController#index: same preloads on `@manual_accounts`. - accounts/index/_account_groups.erb: extend the existing `Preloader` call to batch-load accountable + provider associations so the per-provider-item partials (Plaid, SimpleFIN, Coinbase, etc.) stop re-issuing N+1s when rendering account rows on /accounts. - accounts/_account_sidebar_tabs.html.erb: wrap the partial in a `cache` block keyed on the family's data-version, the current user, shares fingerprint, locale, mobile flag, active tab, and a path-derived "current account" component (`sidebar_active_account_id` helper). The sidebar is rendered on every page in the layout (twice — mobile + desktop drawers), so most navigations now serve the cached fragment instead of re-walking accounts/balances. Local impact (DZG family, 23 accounts, 6.1k transactions): - Dashboard `/`: ~6.5s → ~1.95s - /accounts: ~2.7s → ~0.85s on warm cache - /accountable_sparklines/*: per-request N+1s eliminated; remaining cost is request boilerplate which can be addressed by bumping `RAILS_MAX_THREADS` (the dashboard fans out 5 sparkline turbo frames in parallel and Puma's default 3 threads serialize them). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(perf): address PR review on sidebar/sparkline perf changes - AccountableSparklinesController#requires_normalized_aggregation? also matches legacy plaid_account_id / simplefin_account_id links, not just new-style account_providers, so investment/crypto accounts in the legacy linking state still get LinkedInvestmentSeriesNormalizer applied (Codex P1 / CodeRabbit major). - Sidebar share fingerprint includes both `count` and `max(updated_at)` so deleting a non-most-recent AccountShare invalidates the cached fragment for users who lost access (Codex P1). - Move the sidebar cache-key construction (incl. the AccountShare query) from the ERB into a new `account_sidebar_tabs_cache_key` helper, per the project's "no heavy logic in ERB" rule (CodeRabbit). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(perf): address human review on perf PR - Account.linked: new SQL-level scope mirroring `Account#linked?` so the controller and per-instance method share one definition. Removes the duplicated raw SQL string in `AccountableSparklinesController#requires_normalized_aggregation?`, which now reads `accounts.linked.exists?` (jjmata, sure-design). - AccountsHelper: move `sidebar_active_account_id` and `account_sidebar_tabs_cache_key` out of `ApplicationHelper`. The cache-key helper also collapses the AccountShare `count` + `max(updated_at)` fingerprint into a single `pick` query so we don't pay two round-trips on every render (jjmata, sure-design). - test/models/account/linkable_test.rb: pin the `Account.linked` scope against all three link types (account_providers, legacy plaid_account, legacy simplefin_account) so any future schema change that diverges the SQL definition from `linked?` breaks a test instead of silently serving wrong sparkline aggregations (sure-design). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(perf): correct shares cache fingerprint on raw-SQL pick `pick(Arel.sql("count(*), max(updated_at)"))` passes a single comma- separated fragment, which Rails returns as a String (per the documented behavior of `pluck` with SQL fragments). The previous `max_at&.to_i` silently truncated `"2025-05-06 12:34:56.789 UTC"` to `2025`, so the sidebar cache key would not change for share `updated_at` movements within the same calendar year — including share deletions — leaving revoked users with a stale sidebar until the 12h expiry. Pass the aggregates as two separate `Arel.sql` args and just concatenate the raw String values into the cache key. The values only need to be stable for a given DB state, not numerically meaningful. Caught by CodeRabbit on PR #1683. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
dce2213a98 |
feat: add Hungarian (hu) localization (#1677)
* Add Hungarian (hu) localization Add complete Hungarian translation files and register hu locale * Update hu.yml * Hungarian locale: currency formatting & fixes |
||
|
|
a9661253f4 |
Revert "feat(accounts): Highlight matching activity search text in entry names" (#1682)
Explanation in #1679 |
||
|
|
e535a7ab0b |
Merge pull request #1671 from bugbug11111/feat/search-highlight
feat(accounts): Highlight matching activity search text in entry names |
||
|
|
86d92508cb |
fix(accounts): sanitize activity entry names for highlighting
* Updated the `highlight_activity_entry_name` method to escape HTML in activity entry names before highlighting. This change prevents potential XSS vulnerabilities and ensures safe rendering of user-generated content. |
||
|
|
1646abb938 |
Fix SSO icon rendering for mixed-case provider icons (#1674)
* fix(sso): normalize icon names and fail gracefully * fix(sso): fall back to key icon --------- Co-authored-by: SureBot <sure-bot@we-promise.com> |
||
|
|
bba32a3e61 |
feat(accounts): add activity entry highlighting in summary cards
* Introduced a new helper method `highlight_activity_entry_name` to highlight search terms in activity entry names. * Updated various views to utilize the new highlighting method for improved user experience in displaying relevant entries. |
||
|
|
0fe1e06645 |
refactor(design-system): migrate fg-* utilities to text-* and remove namespace (#1626)
* refactor(design-system): migrate fg-* utilities to text-* and remove namespace
The design system carried two parallel namespaces for foreground colors:
text-* (canonical, ~2,000 uses) and fg-* (32 uses). Most fg-* tokens
were 1:1 duplicates of a text-* counterpart. fg-gray was nearly
identical to text-secondary, with a one-step shade difference in dark
mode.
This PR migrates all 32 usages to their text-* equivalents and removes
the fg-* block from the design tokens. Closes #1606.
Mapping:
- fg-inverse -> text-inverse (20 usages, identical light/dark values)
- fg-gray -> text-secondary (7 usages; light values match, dark is
one step lighter: gray-300 vs gray-400)
- fg-primary -> text-primary (3 usages, identical values)
- fg-subdued -> text-subdued (2 usages, identical values)
The four other fg-* tokens (fg-contrast, fg-primary-variant,
fg-secondary, fg-secondary-variant) had zero usages despite being
defined; they are removed without replacement.
JSON / build:
- design/tokens/sure.tokens.json: $version 1.0.0 -> 2.0.0 (breaking
schema change per the policy added in #1620). 8 fg-* token
definitions removed.
- button-bg-ghost-hover's dark value still references "fg-inverse"
internally; rewritten to "bg-gray-800 text-inverse" so the cleanup
doesn't break that utility.
- _generated.css regenerated. 42 utility blocks now (was 50).
Lookbook tokens preview:
- The Text & foregrounds section dropped its split between text-*
(canonical) and fg-* (legacy). Now a single section listing the
five text-* utilities. The "(legacy)" framing is gone since there's
no legacy left.
README:
- design/tokens/README.md's button-bg-ghost-hover edge-case example
updated to reflect the new "bg-gray-800 text-inverse" dark value.
Visual review needed in dark mode:
- Anywhere icons use the application_helper#icon helper with
color: "default" (most icons in the app). The default class moved
from fg-gray (gray-400 dark) to text-secondary (gray-300 dark), so
default-color icons render slightly lighter in dark mode.
- DS::Buttonish icons in secondary buttons (same shade shift).
- DS::Link icons (same).
- Time series chart axes (same).
- All tooltips, account add flow, settings hostings buttons,
invitations, AI consent, family export, danger-zone buttons --
these used fg-inverse, which is identical to text-inverse, so no
visual change expected.
* fix(design-system): use inverse pair on tooltips for readable dark mode
* fix(lookbook): use semantic tokens in menu preview header text
* fix(lookbook): set text-primary on layout body so previews inherit theme
* fix(design-system): keep shadows dark-toned in dark mode
Inverting shadows to white|8% on dark surfaces produces a halo
effect rather than an elevation cue, and stacks redundantly with
the alpha-white 1px ring already in shadow-border-*.
Switch dark-mode shadows to black at progressively higher alpha
(25%/30%/35%/40%/50% for xs..xl) so they read as actual cast
shadows on near-black surfaces. Surface-tint differences and the
existing alpha-white border ring continue to handle elevation
hierarchy and edge definition.
Approach matches Material 3, Apple HIG, IBM Carbon, Refactoring UI,
and the dark-mode shadows used in Linear/Vercel/Stripe.
* fix(design-system): set text-primary on DS::Dialog element
Browser UA stylesheets apply color: black directly to <dialog>,
which overrides ancestor inheritance even when a body or html
ancestor sets a theme-aware color. Unstyled child content then
renders black regardless of theme.
Setting text-primary on the dialog element itself defeats the UA
override and lets descendants inherit the semantic token.
* fix(lookbook): use shadow css vars in effects preview so dark theme renders
* Revert "fix(design-system): keep shadows dark-toned in dark mode"
This reverts commit
|
||
|
|
0a96bf199d |
SimpleFIN: setup UX + same-provider relink + card-replacement detection (#1493)
* SimpleFIN: setup UX + same-provider relink + card-replacement detection Fixes three bugs and adds auto-detection for credit-card fraud replacement. Bugs: - Importer: per-institution auth errors no longer flip the whole item to requires_update. Partial errors stay on sync_stats so other institutions keep syncing. - Setup page: new activity badges (recent / dormant / empty / likely-closed) via SimplefinAccount::ActivitySummary. Likely-closed (dormant + near-zero balance + prior history) defaults to "skip" in the type picker. - Relink: link_existing_account allows SimpleFIN to SimpleFIN swaps by atomically detaching the old AccountProvider inside a transaction. Adds "Change SimpleFIN account" menu item on linked-account dropdowns. Feature (credit-card scope only): - SimplefinItem::ReplacementDetector runs post-sync. Pairs a linked dormant zero-balance sfa with an unlinked active sfa at the same institution and account type. Persists suggestions on Sync#sync_stats. - Inline banner on the SimpleFIN item card prompts relink via CustomConfirm. Per-pair dismiss button scoped to the current sync (resurfaces on next sync if still applicable). Auto-suppresses once the relink has landed. Dev tooling: - bin/rails simplefin:seed_fraud_scenario[email] creates a realistic broken pair for manual QA; cleanup_fraud_scenario reverses it. * Address review feedback on #1493 - ReplacementDetector: symmetric one-to-one matching. Two dormant cards pointing at the same active card are now both skipped — previously the detector could emit two suggestions that would clobber each other if the user accepted both. - ReplacementDetector: require non-blank institution names on both sides before matching. Blank-vs-blank was accidentally treated as equal, risking cross-provider false matches when SimpleFIN omitted org_data. - ActivitySummary: fall back to "posted" when "transacted_at" is 0 (SimpleFIN's "unknown" sentinel). Integer 0 is truthy in Ruby, so the previous `|| fallback` short-circuited and ignored posted. - Controller: dismiss key is now the (dormant, active) pair so dismissing one candidate for a dormant card doesn't suppress others. - Helper test: freeze time around "6.hours.ago" and "5.days.ago" assertions so they don't flake when the suite runs before 06:00. * Address second review pass on #1493 - ReplacementDetector: canonicalize account_type in one place so filtering (supported_type?) and matching (type_matches?) agree on "credit card" vs "credit_card" variants. - ReplacementDetector: skip candidates with nil current_balance. nil is "unknown," not "zero" — previously fell back to 0 and passed the near- zero gate, allowing suggestions without balance evidence. |
||
|
|
aacbb5ef3b |
Budget page refactor: split into(All - Over Budget - On Track) (#1195)
* Optimize UI in budget * update locales * Optimize UI * optimize suggested_daily_spending * try over_budget and on_track * update locale * optimize * add budgets_helper.rb * fix * hide no buget and no expense sub-catogory * Optimize * Optimize button on phone * Fix Pipelock CI noise * using section to render both overbudget and onTrack * hide last ruler * fix * update test --------- Co-authored-by: Juan José Mata <juanjo.mata@gmail.com> |
||
|
|
60929cdee0 |
feat: add currency management for families with enabled currencies (#1419)
* feat: add currency management for families with enabled currencies * feat: update currency selection logic and improve accessibility * feat: update currency preferences to use group moniker in titles --------- Signed-off-by: Ang Wei Feng (Ted) <hello@tedawf.com> Signed-off-by: Juan José Mata <juanjo.mata@gmail.com> Co-authored-by: Juan José Mata <juanjo.mata@gmail.com> |
||
|
|
2658c36b05 |
feat(select): improve merchant dropdown behavior and placement controls (#1364)
* feat(select): improve merchant dropdown behavior and placement controls - add configurable menu_placement strategy to DS::Select (auto/down/up) with safe normalization forward menu_placement through StyledFormBuilder#collection_select - force Merchant dropdown to open downward in transaction create and editor forms - fix select option/search text contrast by applying text-primary in DS select menu - prevent form jump on open by scrolling only inside dropdown content instead of using scrollIntoView - clamp internal dropdown scroll to valid bounds for stability - refactor select controller placement logic for readability (placementMode, clamp) without changing behavior * set menu_placement=auto for metchant selector |
||
|
|
cebdf1d4f7 |
Polish localization: complete translations, pluralization fixes, and reusable locale audit tooling (#1356)
* Add production-ready Polish localization and reusable locale audit tooling - add and update Polish locale files across models, views, mailers, and shared translations - add runtime rails-i18n dependency and Polish locale support in language helper - add regression coverage for Polish pluralization and locale-aware money formatting - introduce reusable locale audit script for any locale plus backward-compatible PL wrapper - add localization audit docs and generated PL readiness/pluralization reports - resolve one/few/many/other pluralization consistency for Polish locales * Fix Polish locale review feedback * Fix locale compatibility regressions * Polish locale typo pass and wrapper cleanup * Final language improvements and test isolation for Polish locales - Improved partial_success wording in SnapTrade with proper noun inflection - Fixed typos: Pomin → Pomiń in Mercury and LunchFlow items - Isolated I18n backend state in polish_pluralization_test to prevent test coupling * Fix code review comments in locale audit scripts - Use RbConfig.ruby instead of 'ruby' to ensure consistent interpreter - Remove Symbol from permitted_classes and explicitly allow CLDR plural symbols (one, few, many, other) in YAML loading * Simplify i18n flow and align locale interpolation keys * Remove locale audit scripts and localization docs |
||
|
|
b78944ed6f |
Fix selected account and Transaction/Transfer Tabs changes (#1220)
* Fix Dialog selected attribute had no effect * Fix New Transaction/Transfer Dialog * Update transfers_controller.rb --------- Co-authored-by: sokiee <sokysrm@gmail.com> |
||
|
|
fc987ededb |
Add Kosovo to country list (#1066)
Kosovo was missing from the COUNTRY_MAPPING in LanguagesHelper. Added Kosovo with ISO 3166-1 alpha-2 code XK and flag emoji 🇽🇰. The entry is placed in alphabetical order between Kuwait (KW) and Kyrgyzstan (KG) to maintain consistency with the existing list. Fix Investment account subtype not saving on creation (#1039) * Add permitted_accountable_attributes for investments * Include fields_for for accountable subtype selection feat: Add tag badge in filter window (#1038) * feat: Add tag badge in filter window * fix: validate Tag color attribute as hex format and increase transparency mix in border color * fix: use fallback for tag color fix/qol: Add Callback URL the Enable Banking Instructions (#1060) * fix/qol: Add wich Callback URL to use to the Enable Banking Instructions * CodeRabbit suggestion * CodeRabbit suggestion * Skip CI failure on findings --------- fix: Update PWA icons to use current logo (#1052) * fix: Update PWA icons to use current logo (#997) Replace outdated android-chrome-192x192.png and logo-pwa.png with the current logo. The old icons showed the previous branding (cyan border / old logomark) which appeared when creating web shortcuts on smartphones. Also add the 192x192 icon entry to the PWA manifest for better Android home screen icon support. * fix: Replace transparent background with solid #F9F9F9 in 192x192 PWA icon The android-chrome-192x192.png had an RGBA transparent background which can cause display issues on Android home-screen shortcuts. Regenerated with a solid #F9F9F9 background to match theme_color/background_color in the PWA manifest and the 512x512 icon. --------- Bump version to next iteration after v0.6.8-alpha.13 release Use Langfuse client trace upsert API (#1041) Replace direct trace.update calls with client trace upserts so OpenAI provider is compatible with langfuse-ruby 0.1.6 behavior. Add richer warning logs that include full exception details for trace creation, trace upserts, and generation logging failures. Add tests for client-based trace upserts and detailed error logging. Add build ID to Flutter app version display Add MCP server endpoint for external AI assistants (#1051) * Add MCP server endpoint for external AI assistants Expose Sure's Assistant::Function tools via JSON-RPC 2.0 at POST /mcp, enabling external AI clients (Claude, GPT, etc.) to query financial data through the Model Context Protocol. - Bearer token auth via MCP_API_TOKEN / MCP_USER_EMAIL env vars - JSON-RPC 2.0 with proper id threading, notification handling (204) - Transient session (sessions.build) to prevent impersonation leaks - Centralize function_classes in Assistant module - Docker Compose example with Pipelock forward proxy - 18 integration tests with scoped env (ClimateControl) * Update compose for full Pipelock MCP reverse proxy integration Use Pipelock's --mcp-listen/--mcp-upstream flags (PR #127) to run bidirectional MCP scanning in the same container as the forward proxy. External AI clients connect to port 8889, Pipelock scans requests (DLP, injection, tool policy) and responses (injection, tool poisoning) before forwarding to Sure's /mcp endpoint. This supersedes the standalone compose in PR #1050. * Fix compose --preset→--mode, add port 3000 trust comment, notification test Review fixes: - pipelock run uses --mode not --preset (would prevent stack startup) - Document port 3000 exposes /mcp directly (auth still required) - Add version requirement note for Pipelock MCP listener support - Add test: tools/call sent as notification does not execute Feat/Abstract Assistant into module with registry (#1020) * Abstract Assistant into module with registry (fixes #1016) - Add Assistant module with registry/factory (builtin, external) - Assistant.for_chat(chat) routes by family.assistant_type - Assistant.config_for(chat) delegates to Builtin for backward compat - Assistant.available_types returns registered types - Add Assistant::Base (Broadcastable, respond_to contract) - Move current behavior to Assistant::Builtin (Provided + Configurable) - Add Assistant::External stub for future OpenClaw/WebSocket - Migration: add families.assistant_type (default builtin) - Family: validate assistant_type inclusion - Tests: for_chat routing, available_types, External stub, blank chat guard * Fix RuboCop layout: indentation in Assistant module and tests * Move new test methods above private so Minitest discovers them * Clear thinking indicator in External#respond_to to avoid stuck UI * Rebase onto upstream main: fix schema to avoid spurious diffs - Rebase feature/abstract-assistant-1016 onto we-promise/main - Rename migration to 20260218120001 to avoid duplicate version with backfill_crypto_subtype - Regenerate schema from upstream + assistant_type only (keeps vector_store_id, realized_gain, etc.) - PR schema diff now shows only assistant_type addition and version bump --------- Add Pipelock agent security scan to CI (#1049) * Add Pipelock agent security scan to CI Scans PR diffs for leaked secrets and agent security risks. Zero config, runs on every PR to main. * Retrigger CI (v1 action tag now available) * Harden checkout: persist-credentials false Pipelock only reads local git history for diff scanning, no auth token needed in .git/config. Improvements to Flutter client (#1042) * Chat improvements * Delete/reset account via API for Flutter app * Fix tests. * Add "contact us" to settings * Update mobile/lib/screens/chat_conversation_screen.dart * Improve LLM special token detection * Deactivated user shouldn't have API working * Fix tests * API-Key usage * Flutter app launch failure on no network * Handle deletion/reset delays * Local cached data may become stale * Use X-Api-Key correctly! --------- Skip unnecessary sync when account balance unchanged on update (#1040) The update action was calling set_current_balance (which triggers sync_later internally) on every form submission, even when the balance hadn't changed. This caused the account to enter a syncing state, replacing the visible balance with a pulsing skeleton placeholder until the sync completed. Now we compare the submitted balance against the current value and only call set_current_balance when it actually differs. Also removes a redundant sync_later call that duplicated the one already inside set_current_balance. feat: Add merchant logo in filter selection (#1037) * feat: Add merchant logo in filter selection * fix: move external div outside of if statement Sync Helm chart and Rails app versions in CI and release workflows (#1030) * Sync Helm chart and Rails app versions in CI and release workflows - values.yaml: default image.tag to "" so it uses Chart.appVersion (was hardcoded to stale "0.6.6" while app was at 0.6.8-alpha.13) - chart-ci.yml: add version-sync job that fails if version.rb, Chart.yaml version, and Chart.yaml appVersion diverge; trigger on version.rb changes too - chart-release.yml: derive chart version from version.rb (single source of truth) instead of auto-incrementing independent chart-v* tags https://claude.ai/code/session_01Eq3WHBn3Uwjezxb6ctdjMB * Default to `false` AI_DEBUG_MODE * Apply suggestions from CodeRabbit --------- Fix mobile-build using tag name instead of branch for filenames When workflow_dispatch is triggered from a tag (e.g. v0.6.7) instead of a branch, github.ref_name returns the tag name, causing filenames like sure-v0.6.7-{stamp}.apk instead of sure-main-{stamp}.apk. Guard against this by checking github.ref_type and falling back to the repository's default branch when a tag is selected. https://claude.ai/code/session_01TDfNkNxQ6uWxQxLAwJY5Qa Add workflow to build mobile apps from main without tagging (#1028) * Add workflow to build mobile apps from main without tagging Adds a new `mobile-main-build.yml` workflow that can be triggered manually via workflow_dispatch to build Android APK and iOS unsigned builds from the main branch. Uses a `main-YYYYMMDDHHMI` stamp for versioning (e.g. sure-main-202602181259.apk) and updates the gh-pages README.md MOBILE_DOWNLOADS section with direct download links. https://claude.ai/code/session_01TDfNkNxQ6uWxQxLAwJY5Qa * Rename to mobile-build.yml and support any branch Instead of hardcoding "main", derive the branch name from github.ref_name, sanitise it for filenames/tags (slashes → hyphens), and use it throughout: version, tag, release notes, and gh-pages README. The checkout step now explicitly pins ref: ${{ github.ref }} so the tag always matches the dispatched branch. Example artifacts from main: sure-main-202602181259.apk Example from feature/foo: sure-feature-foo-202602181259.apk https://claude.ai/code/session_01TDfNkNxQ6uWxQxLAwJY5Qa * Add continue-on-error to artifact download steps If either the Android or iOS build fails, the download step would hard-fail and abort the release job before the conditional logic in "Prepare release assets" could handle the partial result. Adding continue-on-error lets the workflow proceed so a release can still be created with whichever artifacts succeeded. https://claude.ai/code/session_01TDfNkNxQ6uWxQxLAwJY5Qa * Fix in-place replacement of MOBILE_DOWNLOADS section in README The previous logic stripped the marker block then appended the new section at the end of the file, causing it to drift to the bottom on every run. Now writes the section to a temp file and uses awk to replace the block between the markers in-place, preserving the section's original position in the README. https://claude.ai/code/session_01TDfNkNxQ6uWxQxLAwJY5Qa --------- Bump version to next iteration after v0.6.8-alpha.12 release Enable inclusion of hidden files in helm chart package Remove OPENAI_URI_BASE and OPENAI_MODEL from Helm secret values (#1025) These are optional app configuration values (not secrets), and listing them in rails.secret.values alongside required keys like SECRET_KEY_BASE makes users think they must be specified. Users who need them can set them via rails.extraEnv or rails.settings instead. https://claude.ai/code/session_01BP8Nr2cZWDdu9zGL9vD8Mw fix: Enable Banking DNS issues and provide better UI sync feedback (#1021) * fix(docker): add explicit DNS config to fix enable banking sync * fix(enable-banking): surface sync errors in the UI * fix: add spaces inside array brackets for RuboCop * fix(enable-banking): surface sync errors and partial failures in UI chore(deps): bump nokogiri from 1.18.9 to 1.19.1 (#1024) Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.18.9 to 1.19.1. - [Release notes](https://github.com/sparklemotion/nokogiri/releases) - [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md) - [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.18.9...v1.19.1) --- updated-dependencies: - dependency-name: nokogiri dependency-version: 1.19.1 dependency-type: indirect ... Refactor GitHub Actions workflows (#1023) * Unify release workflows and add chart/mobile wrappers * Update chart CI to kube 1.25 * Fetch tagged commit before pushing release branch * Old `azure/setup-helm` * Base chart dispatch version on existing chart tags * `grep` failure with `pipefail` bypasses the user-friendly error message * `gh-pages` push lacks retry logic * Auto-incremented chart tag collision * `grep -Ev` pipeline will crash * Missed one feat: New tag creation UI (#1014) * feat: Update tag creation UI * fix: remove unused target * fix: remove connect/disconnect functions * fix: remove unnecessary target Fix crypto subtype for trades api (#1022) * fix: crypto subtype not persisted by permitting :subtype in CryptosController * Backfill crypto subtype for existig accounts so Trades API works * fix: backfill only unlinked cryptos; use raw SQL in migration; deterministic redirect in test * Update schema.rb for BackfillcryptoSubtypeForTrades migration --------- fix: add logic to skip future pending transactions and add cleanup ta… (#1011) * fix: add logic to skip future pending transactions and add cleanup task for stuck entries * Update lib/tasks/cleanup_stuck_pending_lunchflow.rake * Update app/models/lunchflow_entry/processor.rb * fix(coderabbit): assertions use entryable instead of transaction for pending state checks * chore(codex): add comments to clarify handling of pending transactions, exclude self in cleanup task * fix(coderabbit): memoize external_id --------- fix: show Edit button in account view bulk selection bar (#1002) Fix foreign currency accounts using wrong exchange rate in balance sheet totals (#1010) Balance sheet totals and accountable type summaries used a SQL JOIN on exchange_rates matching only today's date, which returned NULL (defaulting to 1:1) when no rate existed for that exact date. This caused foreign currency accounts to show incorrect totals. Changes: - Refactor BalanceSheet::AccountTotals to batch-fetch exchange rates via ExchangeRate.rates_for, with provider fallback, instead of a SQL join - Refactor Accountable.balance_money to use the same batch approach - Add ExchangeRate.rates_for helper for deduplicated rate lookups - Fix net worth chart query to fall back to the nearest future rate when no historical rate exists for a given date - Add composite index on accounts (family_id, status, accountable_type) - Reuse nearest cached exchange rate within a 5-day lookback window before calling the provider, preventing redundant API calls on weekends and holidays when providers return prior-day rates https://claude.ai/code/session_01GyssBJxQqdWnuYofQRjUu8 Syntax error in workflow Bump version to next iteration after v0.6.8-alpha.11 release Default tap on URL opens settings No credentials = no TestFlight Upload to TestFlight after `release` / fix version name Fix Android icon color Add demo account to Flutter client also Default login for Flutter client beta Wire TestFlight up to mobile releases iOS build fixes/prep for TestFlight LLC is Sure Finances, keep it the same New icon set and name Safe area around icon Add flutter_export_environment.sh to .gitignore Ignore changes in `mobile/` directory for publish workflow Enhance mobile release workflow with dispatch and script Added workflow_dispatch trigger and updated GitHub Release step to use a script for release notes and asset uploads. New icons New icon Fix version number for Android Version number in Gradle build Version number to bundle files More .gitignore noise chore(deps): bump rack from 3.1.18 to 3.1.20 (#1013) Bumps [rack](https://github.com/rack/rack) from 3.1.18 to 3.1.20. - [Release notes](https://github.com/rack/rack/releases) - [Changelog](https://github.com/rack/rack/blob/main/CHANGELOG.md) - [Commits](https://github.com/rack/rack/compare/v3.1.18...v3.1.20) --- updated-dependencies: - dependency-name: rack dependency-version: 3.1.20 dependency-type: indirect ... Update Flutter iOS run command in `mobile/README.md` fix: Remove additional padding from JS controller and add directly as a class (#1007) PDF ai import (#1006) Add support to review transactions for AI pdf import Bump version to next iteration after v0.6.8-alpha.10 release Improve tests Add new columns and sorting to admin users list (#1004) * Add trial end date to admin users list * Add new columns * Regression feat: Update gh-pages README with latest mobile release links (#1003) Add steps to the mobile-release workflow that checkout the gh-pages branch and update its README.md with direct download links to the latest Flutter mobile clients (Android APK, debug APK, iOS unsigned build). Uses HTML comment markers for idempotent updates on subsequent releases. https://claude.ai/code/session_01GuUjjmMzxvdSwfvhrjvJr1 Fix: Handle missing category in SetTransactionCategory executor (#1001) fix: Transfers were not syncing between accounts (#987) * fix: Include investment_contribution in transfer? check and protect transfer entries from sync Transfer transactions with kind "investment_contribution" were not recognized as transfers by the UI, causing missing +/- indicators, "Transfer" labels, and showing regular transaction forms instead of transfer details. Also adds user_modified: true to entries created via TransferMatchesController and SetAsTransferOrPayment rule action to protect them from provider sync overwrites, matching the existing behavior in Transfer::Creator. https://claude.ai/code/session_019BZ5Z1aqKSK3cRdR81P5Jg * fix: Centralize transfer/budget kind constants for consistent investment_contribution handling Define TRANSFER_KINDS and BUDGET_EXCLUDED_KINDS on Transaction to eliminate hard-coded kind lists scattered across filters, rules, and analytics code. investment_contribution is now consistently treated as a transfer in search filters, rule conditions, and UI display (via TRANSFER_KINDS), while budget analytics correctly continue treating it as an expense (via BUDGET_EXCLUDED_KINDS). https://claude.ai/code/session_019BZ5Z1aqKSK3cRdR81P5Jg * fix: Update tests for consistent investment_contribution as transfer kind - search_test: loan_payment is now in TRANSFER_KINDS, so uncategorized filter correctly excludes it (same as funds_movement/cc_payment) - condition_test: investment_contribution is now a transfer kind, so it matches the transfer filter rather than expense filter https://claude.ai/code/session_019BZ5Z1aqKSK3cRdR81P5Jg * fix: Eliminate SQL injection warnings in Transaction::Search Replace string-interpolated SQL with parameterized queries: - totals: use sanitize_sql_array with ? placeholders - apply_category_filter: pass TRANSFER_KINDS as bind parameter - apply_type_filter: use where(kind:)/where.not(kind:) and parameterized IN (?) for compound OR conditions - Remove unused transfer_kinds_sql helper https://claude.ai/code/session_019BZ5Z1aqKSK3cRdR81P5Jg --------- feat: Display dashboard as a 2-columns grid on big screens (#1000) * feat: display 2 columns grid in dashboard for wide screens * fix: update sortable controller to consider X/Y coordinates * fix: lint issues + missing variable init Flutter title and icon alignment fixes Small Flutter UI tweaks Intro mode in Flutter client fixes Replace text-tertiary with text-subdued in views (#999) * Replace text-tertiary with text-subdued in views Replace usages of the text-tertiary utility with text-subdued across several view partials to standardize subdued text styling because text-tertiary does not exist in the design system (reports, doorkeeper auth, simplefin items). Also adjust the net worth empty-liabilities markup to use a grid layout for consistent spacing, and update the related controller test selector to match the new CSS class. * Standardize empty net worth message markup Replace inconsistent markup and classes for empty asset/liability sections in the net worth partial. Swap text-secondary/p-2/text-center for text-subdued with unified padding (py-3 px-4 lg:px-6), and simplify the liabilities block from a grid/div to a single paragraph for consistent styling and spacing. Fix separators in breakdown table view (#996) * Fix separators in breakdown table view Correct conditional logic for rendering column separators (rulers) in the reports breakdown table. The top-level check now compares idx to groups.size instead of group.size, and the subcategory check compares idx to group[:subcategories].size. This ensures separators are shown between categories and subcategories correctly, avoiding missing or extra rulers. * Fix subcategory index variable name in partial Rename the inner loop index from `idx` to `sub_idx` in app/views/reports/_breakdown_table.html.erb to avoid shadowing the outer `idx`. This ensures the conditional that renders the separator (`shared/ruler`) uses the correct index for subcategories, preventing incorrect rendering of separators between subcategory rows. * Fix conditional block order in breakdown table Reorder ERB tags to properly nest the subcategory conditional and the ruler render in the reports breakdown partial. This ensures the divider is only rendered between subcategories and prevents mismatched ERB/end tags that could break template rendering. fix: Viewport issue in PWA (#995) * fix: move safe-area padding from body/HTML to navbars. Add script to compute app height dynamically. * fix: Initialize sankey tooltip with top-0 to avoid overflow * fix: add fallback to HTML height * fix: properly set bottom spacing and use position fixed for bottom navbar * fix: move viewport controller initialization fix: locale-dependent category duplication bug (#956) * fix: locale-dependent category duplication bug * fix: use family locale for investment contributions category to prevent duplicates and handle legacy data * Remove v* tag trigger from flutter-build to fix double-runs publish.yml already calls flutter-build via workflow_call on v* tags, so the direct push trigger was causing duplicate workflow runs. * Refactor mobile release asset flow * fix: category uniqueness and workflow issues * fix: fix test issue * fix: solve test issue * fix: resolve legacy problem * fix: solve lint test issue * fix: revert unrelated changes --------- fix: prevent New rule modal from closing when removing conditions or actions (#991) fix: prevent value overflow in Assets vs Liabilities card (#992) * fix: prevent value overflow in Assets vs Liabilities card Fixes issue where large asset/liability values overflow the container when AI side panel is open and reduces horizontal space. Changes: - Added flex-wrap to allow values to wrap to next line if needed - Added break-all to both asset and liability values for long numbers - Added shrink-0 to minus sign to prevent it from shrinking Fixes #976 * refactor: use break-words instead of break-all per code review Changed from break-all to break-words for currency values to prevent awkward mid-number breaks (e.g., $1,234,5 / 67.89). break-words only breaks when content overflows and keeps values intact when possible, providing cleaner line breaks while still preventing overflow. --------- fix: inverted Buy/Sell type selection by changing amount.negative to amount.positive in trade edit views (#952) Bump version to next iteration after v0.6.8-alpha.9 release Show disabled import options when no accounts exist (#977) * Show disabled import options before accounts exist Keep account-dependent import choices visible on /imports/new and render them as disabled with guidance when no accounts are available. * Refactor disabled import options: extract partial, fix accessibility (#986) - Extract _import_option partial to eliminate duplicated enabled/disabled markup across TransactionImport, TradeImport, and MintImport (also used by AccountImport, CategoryImport, RuleImport for consistency) - Replace misleading chevron-right with lock icon in disabled state - Add aria-disabled="true" for screen reader accessibility - Remove redundant default: parameter from t() call - Fix locale key ordering (requires_account after import_* keys) - Fix extra blank line in test file - Add assertion for aria-disabled attribute in test https://claude.ai/code/session_016j9tDYEBfWX9Dzd99rAYjX * Tailwind fixes --------- Expose ui_layout and ai_enabled to mobile clients and add enable_ai endpoint (#983) * Wire ui layout and AI flags into mobile auth Include ui_layout and ai_enabled in mobile login/signup/SSO payloads, add an authenticated endpoint to enable AI from Flutter, and gate mobile navigation based on intro layout and AI consent flow. * Linter * Ensure write scope on enable_ai * Make sure AI is available before enabling it * Test improvements * PR comment * Fix review issues: test assertion bug, missing coverage, and Dart defaults (#985) - Fix login test to use ai_enabled? (method) instead of ai_enabled (column) to match what mobile_user_payload actually serializes - Add test for enable_ai when ai_available? returns false (403 path) - Default aiEnabled to false when user is null in AuthProvider to avoid showing AI as available before authentication completes - Remove extra blank lines in auth_provider.dart and auth_service.dart https://claude.ai/code/session_01LEYYmtsDBoqizyihFtkye4 --------- fix: Handle empty compound conditions on rules index (#965) * fix: Handle empty compound conditions on rules index * fix: avoid contradictory rule condition summary on /rules * refactor: move rules condition display logic from view to model * fix: localize rule title fallback and preload conditions in rules index Document merchants API endpoints (#980) Add rswag request specs for merchants index/show and define a MerchantDetail schema used by the docs. Update the generated OpenAPI document with merchants paths and schema. Add family moniker selection and dynamic UI labels (#981) * Add family moniker selection and dynamic UI labels Introduce a Family moniker persisted in the database with allowed values Family/Group, add required onboarding selection for it, and thread moniker-aware copy through key user-facing views and locales. Also add helper methods and tests for onboarding form presence and family moniker behavior. * Small copy edits/change moniker question order * Conditional Group/Family onboarding flow fixes * Fix label * Grouping of fields * Profile Info page Group/Family changes * Only admins can change Group/Family moniker * Repetitive defaults * Moniker in Account model * Moniker in User model * Auth fix * Sure product is also a moniker --------- Disable Turbo prefetch on dashboard outflow category links Hovering over category links in the outflow donut chart triggered Turbo 8's default link prefetching, which made a real request to the transactions controller. The controller's store_params! before_action saved those filter params (category + date range) to the session. When the user later navigated to /transactions via the nav menu, the stored params were restored, showing an unexpected filtered view. Adding data-turbo-prefetch="false" prevents the prefetch on hover while preserving the intended click-to-navigate behavior. https://claude.ai/code/session_01Na7AF1wyidPwFdPq5w8oaw Flutter login polish (#973) * Skip config screen by setting demo site default * Small copy edits * Login page polish Mobile-only GitHub workflow builds (#975) * Mobile build only * Fix copy on debug * PR review comments Bump version to next iteration after v0.6.8-alpha.8 release Bring back `/reports` link Fix flaky expectations in import-related tests (#963) Fix tests Generalize from PDF import to just files Index all PDF imports into vector store with type metadata Update backend table with status and requirements Clarify status of non-OpenAI vector store Add `Family` vector search function call / support for document vault (#961) * Add SearchFamilyImportedFiles assistant function with vector store support Implement per-Family document search using OpenAI vector stores, allowing the AI assistant to search through uploaded financial documents (tax returns, statements, contracts, etc.). The architecture is modular with a provider- agnostic VectorStoreConcept interface so other RAG backends can be added. Key components: - Assistant::Function::SearchFamilyImportedFiles - tool callable from any LLM - Provider::VectorStoreConcept - abstract vector store interface - Provider::Openai vector store methods (create, upload, search, delete) - Family::VectorSearchable concern with document management - FamilyDocument model for tracking uploaded files - Migration adding vector_store_id to families and family_documents table https://claude.ai/code/session_01TSkKc7a9Yu2ugm1RvSf4dh * Extract VectorStore adapter layer for swappable backends Replace the Provider::VectorStoreConcept mixin with a standalone adapter architecture under VectorStore::. This cleanly separates vector store concerns from the LLM provider and makes it trivial to swap backends. Components: - VectorStore::Base — abstract interface (create/delete/upload/remove/search) - VectorStore::Openai — uses ruby-openai gem's native vector_stores.search - VectorStore::Pgvector — skeleton for local pgvector + embedding model - VectorStore::Qdrant — skeleton for Qdrant vector DB - VectorStore::Registry — resolves adapter from VECTOR_STORE_PROVIDER env - VectorStore::Response — success/failure wrapper (like Provider::Response) Consumers updated to go through VectorStore.adapter: - Family::VectorSearchable - Assistant::Function::SearchFamilyImportedFiles - FamilyDocument Removed: Provider::VectorStoreConcept, vector store methods from Provider::Openai https://claude.ai/code/session_01TSkKc7a9Yu2ugm1RvSf4dh * Add Vector Store configuration docs to ai.md Documents how to configure the document search feature, covering all three supported backends (OpenAI, pgvector, Qdrant), environment variables, Docker Compose examples, supported file types, and privacy considerations. https://claude.ai/code/session_01TSkKc7a9Yu2ugm1RvSf4dh * No need to specify `imported` in code * Missed a couple more places * Tiny reordering for the human OCD * Update app/models/assistant/function/search_family_files.rb * PR comments * More PR comments --------- Fix property subtype not persisting on edit (#930) * Fix property subtype not persisting on edit * Add regression test for property subtype persistence This change introduces model specs and factories to cover property subtype persistence on update. FactoryBot setup and test dependencies were adjusted to support the new specs. * Add regression test for property subtype persistence * remove unused FactoryBot factories and test * remove FactoryBot in Gemfile.lock * Fix no-op regression test for property subtype update * Delete no-op property_test * add pimary_residence in properties fixtures * add capybara system test for property subtype persistence * fix spelling and indent * rename test to "can persist property subtype" --------- Bump version to next iteration after v0.6.8-alpha.7 release fix: restore drawer positioning for transaction modals on desktop (#857) (#896) * feat: Add responsive dialog behavior for transaction modals Add responsive option to DS::Dialog component that switches between: - Mobile (< 1024px): Modal style (centered) with inline close button - Desktop (≥ 1024px): Drawer style (right side panel) with header close button Update transaction, transfer, holding, trade, and valuation views to use responsive behavior, maintaining mobile experience while reverting desktop to drawer style like budget categories. Changes: - app/components/DS/dialog.rb: Add responsive parameter and helper methods - app/components/DS/dialog.html.erb: Apply responsive styling - app/views/*/show.html.erb: Add responsive: true and hide close icons on mobile * fix: Enhance close button accessibility in dialog components * fix: Refactor dialog component to improve close button handling and accessibility Include newer providers in automatic family sync (#934) * Include newer providers in automatic family sync Coinbase, CoinStats, Mercury, and SnapTrade all implement Syncable and have Syncer classes but were not listed in child_syncables, meaning their data only refreshed on manual sync button clicks. * refactor(syncer): Open/Closed principle for provider sync - Adding new providers requires modifying child_syncables (violates O/C) - plaid_items missing .active scope (bug: syncs deleted items) - snaptrade_items can exist without user registration → fails on sync - Scattered knowledge about 'ready to sync' logic 1. **Registry pattern**: SYNCABLE_ITEM_ASSOCIATIONS constant lists all provider associations that participate in family sync 2. **Encapsulated sync-readiness**: Each item model defines its own `syncable` scope that knows when it's ready for auto-sync: - Most providers: `syncable = active` (not scheduled for deletion) - SnapTrade: `syncable = active + user_registered` (has API creds) 3. **Single loop**: child_syncables iterates the registry, calling `.syncable` on each association - Adding a provider = add to registry + define syncable scope - Each model owns its 'ready to sync' business logic - Fixes plaid_items bug (now uses .active via .syncable) - Fixes snaptrade auto-sync failures (filters unregistered items) - Easy to extend with new conditions per provider - family/syncer.rb: Registry + dynamic collection - *_item.rb (7 files): Add `scope :syncable, -> { active }` - snaptrade_item.rb: Add syncable with user_registered filter * Fix rubocop bracket spacing in SnaptradeItem syncable scope fix: keep nav bar sticky at top (#943) * fix: keep nav bar sticky at top * fix: sticky on settings page * fix: keep padding in settings page * fix: make all settings page title sticky * fix: make buttons sticky with title * fix: set header bar min height * fix: mobile responsive * fix: reduce header bar feat: Add LLM prompt for API endpoint consistency (#949) * Add LLM prompt for API endpoint consistency (fixes #944) - Add .cursor/rules/api-endpoint-consistency.mdc: checklist that applies when editing app/controllers/api/v1, spec/requests/api/v1, or test/controllers/api/v1. Enforces (1) Minitest-only behavioral coverage for new endpoints, (2) rswag docs-only (no expect/assert), (3) same API key auth pattern in all rswag specs. - Reference the rule in AGENTS.md under API Development Guidelines. * Add tests for API endpoint consistency implementation - Minitest: test/api_endpoint_consistency_rule_test.rb checks rule file exists, globs, and all three sections (Minitest, rswag docs-only, API key auth) plus AGENTS.md reference. - Standalone: test/support/verify_api_endpoint_consistency.rb runs same checks without loading Rails (use when app fails to boot). - Rule: add mdc: links for Cursor, note valuations_spec OAuth outlier. * Address review: add --compliance check, CLAUDE.md section - Verification script: --compliance scans current APIs and reports rswag OAuth vs API key, missing Minitest for controllers, expect/assert. - CLAUDE.md: add Post-commit API consistency subsection under API Development Guidelines (links to rule, documents script + --compliance). --------- Remove Flipper and replace with ENV-driven FeatureFlags (#957) * Presence of valid DEFAULT_UI_LAYOUT is sufficient * Linter Normalize legacy SSO icon values before validation (#955) chore(deps): bump faraday from 2.13.2 to 2.14.1 (#953) Bumps [faraday](https://github.com/lostisland/faraday) from 2.13.2 to 2.14.1. - [Release notes](https://github.com/lostisland/faraday/releases) - [Changelog](https://github.com/lostisland/faraday/blob/main/CHANGELOG.md) - [Commits](https://github.com/lostisland/faraday/compare/v2.13.2...v2.14.1) --- updated-dependencies: - dependency-name: faraday dependency-version: 2.14.1 dependency-type: direct:production ... Bump version to next iteration after v0.6.8-alpha.6 release Remove reference to nonexistent Sidekiq::Throttled gem (#950) The sidekiq-throttled gem is not in the Gemfile, so including Sidekiq::Throttled::Job causes an uninitialized constant NameError at boot time, breaking production builds. https://claude.ai/code/session_01Bj7xgndJt28BcUHW1v1M9S Bump version to next iteration after v0.6.8-alpha.5 release Forgot to make test Flipper-conditional Add Intro UI feature flag First cut of a simplified "intro" UI layout (#265) * First cut of a simplified "intro" UI layout * Linter * Add guest role and intro-only access * Fix guest role UI defaults (#940) Use enum predicate to avoid missing role helper. * Remove legacy user role mapping (#941) Drop the unused user role references in role normalization and SSO role mapping forms to avoid implying a role that never existed. Refs: #0 * Remove role normalization (#942) Remove role normalization Roles are now stored directly without legacy mappings. * Revert role mapping logic * Remove `normalize_role_settings` * Remove unnecessary migration * Make `member` the default * Broken `.erb` --------- Implement Indexa Capital provider with real API integration (#933) * Add Indexa Capital provider scaffold Generate Indexa Capital provider scaffolding and align credential fields with the API authentication requirements. * Fix PR 926 lint and schema CI failures * Implement Indexa Capital provider with real API integration - Rewrite all broken view templates (were meta-ERB from code generator) - Create missing select_accounts.html.erb template - Implement real API calls: list_accounts via /users/me, get_holdings via /accounts/{number}/fiscal-results, get_account_balance via /accounts/{number}/performance - Add API token auth support (stored token > env token > credentials) - Add api_token column with encryption support - Redesign settings panel: API token prominent, credentials collapsible - Fix account balances display using performance endpoint portfolios - Fix accounts index empty-state guard missing indexa_capital_items - Simplify activities fetch job (no activities API endpoint exists) - Fix i18n interpolation (%%{ -> %{) throughout locale file * Add tests for Indexa Capital provider integration - IndexaCapitalItemTest: validations, credentials, scopes, sync status - IndexaCapitalAccountTest: upsert, holdings, account provider linking - Provider::IndexaCapitalTest: auth modes, API stubs, error handling - IndexaCapitalItemsControllerTest: CRUD, setup, linking, authorization - Fixtures for items (token + credentials) and accounts (mutual + pension) 52 tests, 98 assertions, 0 failures * Address code review feedback from PR #933 - Fix zero balance bug: use `nil?` instead of `present?` so 0 is stored - Fix has_indexa_capital_credentials? to check api_token (was ignored) - Fix build_provider to delegate to Provided concern (was ignoring token) - Fix IndexaCapital section outside encryption_error guard in settings - Add account_number sanitization to prevent path traversal in API URLs - Replace all skipped processor tests with real working tests - Add zero-balance and path-traversal test coverage 61 tests, 107 assertions, 0 failures * Address code review round 2: credentials validation, RuboCop, test quality - Fix RuboCop SpaceInsideArrayLiteralBrackets in credentials check - Chain where.not calls so all three username/document/password must be present - Require all three credentials (||) instead of any one (&&) in validate_configuration! - Move attr_reader to private to avoid exposing credentials publicly - Parse dates with Date.parse in extract_balance for robustness - Remove stale TODO and Crypto from supported_account_types - Order build_provider query deterministically by created_at - Replace no-op holdings assertion with meaningful assert_difference * Address code review round 3: JSON parse safety and test precision - Rescue JSON::ParserError on 2xx responses for clearer error messages - Fix weak balance assertion: set balance to 0 before processing, assert expected value (27093.01 = sum of holdings amounts) * Include Indexa Capital in automatic family sync Add indexa_capital_items to Family::Syncer#child_syncables so balances and holdings refresh on daily auto-sync and login sync, not only on manual sync button clicks. --------- Add REST API for holdings and trades (Discussion #905) (#918) * Add REST API for holdings and trades (Discussion #905) - Trades: GET index (filter by account_id, account_ids, start_date, end_date), GET show, POST create (buy/sell with security_id or ticker), PATCH update, DELETE destroy. Create restricted to accounts that support trades (investment or crypto exchange). Uses existing Trade::CreateForm for creation. - Holdings: GET index (filter by account_id, account_ids, date, start_date, end_date, security_id), GET show. Read-only; scoped to family. - Auth: read scope for index/show; write scope for create/update/destroy. - Responses: JSON via jbuilder (trade: id, date, amount, qty, price, account, security, category; holding: id, date, qty, price, amount, account, security, avg_cost). Pagination for index endpoints (page, per_page). * API v1 holdings & trades: validation, docs, specs - Holdings: validate date params, return 400 for invalid dates (parse_date!) - Trades: validate start_date/end_date, return 422 for invalid dates - Trades: accept buy/sell and inflow/outflow in update (trade_sell_from_type_or_nature?) - Trades view: nil guard for trade.security - Trades apply_filters: single join(:entry) when filtering - OpenAPI: add Trade/TradeCollection schemas, ErrorResponse.errors - Add spec/requests/api/v1/holdings_spec.rb and trades_spec.rb (rswag) - Regenerate docs/api/openapi.yaml * CI: fix Brakeman and test rate-limit failures - Disable Rack::Attack in test (use existing enabled flag) so parallel API tests no longer hit 429 from shared api_ip throttle - Add Brakeman ignore for trades_controller trade_params mass-assignment (account_id/security_id validated in create/update) - Trades/holdings API and OpenAPI spec updates * Trades: partial qty/price update fallback; fix PATCH OpenAPI schema - Fall back to existing trade qty/price when only one is supplied so sign normalisation and amount recalculation always run - OpenAPI: remove top-level qty, price, investment_activity_label, category_id from PATCH body; document entryable_attributes only * Trades: fix update/DELETE OpenAPI and avoid sell-trade corruption - Only run qty/price normalisation when client sends qty or price; preserve existing trade direction when type/nature omitted - OpenAPI: remove duplicate PATCH path param; add 422 for PATCH; document DELETE 200 body (DeleteResponse) * API: flat trade update params, align holdings errors, spec/OpenAPI fixes - Trades update: accept flat params (qty, price, type, etc.), build entryable_attributes in build_entry_params_for_update (match transactions) - Holdings: ArgumentError → 422 validation_failed; parse_date!(value, name) with safe message; extract render_validation_error, log_and_render_error - Specs: path id required (trades, holdings); trades delete 200 DeleteResponse; remove holdings 500; trades update body flat; holdings 422 invalid date - OpenAPI: PATCH trade request body flat * OpenAPI: add 422 invalid date filter to holdings index * API consistency and RSwag doc-only fixes - Trades: use render_validation_error in all 4 validation paths; safe_per_page_param case/when - Holdings: set_holding to family.holdings.find; price as Money.format in API; safe_per_page_param case/when - Swagger: Holding qty/price descriptions (Quantity of shares held, Formatted price per share) - RSwag: trades delete and valuations 201 use bare run_test! (documentation only, no expect) * Fix index-vs-show visibility inconsistencies and preserve custom activity labels - Add account status filter to set_holding to match index behavior - Add visible scope to set_trade to match index behavior - Preserve existing investment_activity_label when updating qty/price * Trades: clearer validation for non-numeric qty/price Return 'must be valid numbers' when qty or price is non-numeric (e.g. abc) instead of misleading 'must be present and positive'. --------- Add "Link to existing" option in SnapTrade Setup Accounts modal (#935) * Add account linking functionality for SnapTrade items - Introduced UI to link existing accounts when setting up SnapTrade items, preventing duplicate account creation. - Updated controller to fetch linkable accounts. - Added tests to verify proper filtering of accounts and linking behavior. * Add `snaptrade_item_id` to account linking flow for SnapTrade items - Updated controller to allow specifying `snaptrade_item_id` when linking accounts. - Adjusted form and views to include `snaptrade_item_id` as a hidden field. - Enhanced tests to validate behavior with the new parameter. Add tests for SnapTrade error handling and refine unlink behavior (#931) - Introduced new tests to cover SnapTrade decryption and connection errors in `SnaptradeItemsControllerTest`. - Updated error messages for improved user clarity. - Modified `unlink` functionality to preserve `SnaptradeAccount` records while ensuring proper detachment of associated holdings. Auto-categorize investment contributions across all transfer paths (#924) * Ensure investment contributions are auto-categorized with proper kind and category creation. * Retrigger CI Fix SSO provider warning timing (#927) Warn after providers are registered to avoid false empty state. fix: loan transfer kind assignment to use destination account (#874) * fix: loan transfer kind assignment to use destination account * fix: update system test to use depository account instead of investment account Bump version to next iteration after v0.6.8-alpha.4 release Protect demo API key from deletion (#919) * feat: Protect demo monitoring API key from deletion - Add DEMO_MONITORING_KEY constant to ApiKey model - Add `demo_monitoring_key?` method to identify the monitoring key - Add `visible` scope to exclude monitoring key from UI queries - Update controller to use `visible` scope, hiding the monitoring key - Prevent revocation of the monitoring key with explicit error handling - Update Demo::Generator to use the shared constant Users on the demo instance can still create their own API keys, but cannot see or delete the monitoring key used for uptime checks. https://claude.ai/code/session_01RQFsw39K7PB5kztboVdBdB * Linter * Protect demo monitoring API key from deletion * Use monitoring source for demo API key * Add test for demo monitoring revoke guard * Disable Rack::Attack in test and development --------- feat: Allow to create rules to mark transactions as transfers or payments (#920) * feat: Allow to create rules to define transfer or payments * fix: lint issues * Update app/models/rule/action_executor/set_as_transfer_or_payment.rb * fix: indentation issue * fix: add explicit return * fix: add guard on target_account * fix: use local variable for transfer and revert explicit return as it doesn't work * fix: Adjust transaction naming --------- fix: allow refreshes from the same source for cost basis updates (#917) * fix: allow refreshes from the same source for cost basis updates * test: update cost basis priority expectations Fix mobile login "Record not found" for unseeded instances (#916) * Auto-create mobile OAuth application when missing (#912) Self-hosted users who set up their instance without running `db:seed` (or reset their database) got "Record not found" on mobile login because `MobileDevice.shared_oauth_application` used `find_by!` which raises when the "Sure Mobile" Doorkeeper application does not exist. Switch to `find_or_create_by!` so the record is created transparently on first use, matching the attributes from the seed file. * Nice Claude Code suggestion --------- feat: add SSL_CA_FILE and SSL_VERIFY environment variables to support… (#894) * feat: add SSL_CA_FILE and SSL_VERIFY environment variables to support self-signed certificates in self-hosted environments * fix: NoMethodError by defining SSL helper methods before configure block executes * refactor: Refactor SessionsController to use shared SslConfigurable module and simplify SSL initializer redundant checks * refactor: improve SSL configuration robustness and error detection accuracy * fix:HTTParty SSL options, add file validation guards, prevent Tempfile GC, and redact URLs in error logs * fix: Fix SSL concern indentation and stub Simplefin POST correctly in tests * fix: normalize ssl_verify to always return boolean instead of nil * fix: solve failing SimpleFin test * refactor: trim unused error-handling code from SslConfigurable, replace Tempfile with fixed-path CA bundle, fix namespace pollution in initializers, and add unit tests for core SSL configuration and Langfuse CRL callback. * fix: added require ileutils in the initializer and require ostruct in the test file. * fix: solve autoload conflict that broke provider loading, validate all certs in PEM bundles, and add missing requires. Fix OIDC household invitation (issue #900) (#904) * Fix OIDC household invitation (issue #900) - Auto-add existing user when inviting by email (no invite email sent) - Accept page: choose 'Create account' or 'Sign in' (supports OIDC) - Store invitation token in session on sign-in; accept after login (password, OIDC, OIDC link, OIDC JIT, MFA) - Invitation#accept_for!(user): add user to household and mark accepted - Defensive guards: nil/blank user, token normalization, accept_for! return check * Address PR review: rename accept_for! to accept_for, i18n OIDC notice, test fixes, stub Rails.application.config * Fix flaky system test: assert only configure step, not flash message --------- fix: Preserve tags on bulk edits (take 3) (#889) * fix: handle tags separately from entryable_attributes in bulk updates Tags use a join table (taggings) rather than a direct column, which means empty tag_ids clears all tags rather than meaning "no change". This caused bulk category-only edits to accidentally clear existing tags. This fix: - Removes tag_ids from entryable_attributes in Entry.bulk_update! - Adds update_tags parameter to explicitly control tag updates - Uses params.key?(:tag_ids) in controller to detect explicit tag changes - Preserves existing tags when tag_ids is not provided in the request This is a cleaner architectural solution compared to tracking "touched" state in the frontend, as it properly acknowledges the semantic difference between column attributes and join table associations. https://claude.ai/code/session_014CsmTwjteP4qJs6YZqCKnY * fix: handle tags separately in API transaction updates Apply the same pattern to the API endpoint: tags are now handled separately from entryable_attributes to distinguish between "not provided" (preserve existing tags) and "explicitly set to empty" (clear all tags). This allows API consumers to: - Update other fields without affecting tags (omit tag_ids) - Clear all tags (send tag_ids: []) - Set specific tags (send tag_ids: [id1, id2]) https://claude.ai/code/session_014CsmTwjteP4qJs6YZqCKnY * Proposed fix * fix: improve tag handling in bulk updates for transactions * fix: allow bulk edit to clear/preserve tags by omitting hidden multi-select field * PR comments * Dumb copy/paste error * Linter --------- Fix flash message persistence in drag-and-drop CSV import (#915) * Initial plan * Disable Turbo on drag-and-drop import form to fix flash message display --------- Bump version to next iteration after v0.6.8-alpha.3 release Add Google Sign-In (SSO) support to Flutter mobile app (#860) * Add mobile SSO support to sessions controller Add /auth/mobile/:provider route and mobile_sso_start action that captures device params in session and renders an auto-submitting POST form to OmniAuth (required by omniauth-rails_csrf_protection). Modify openid_connect callback to detect mobile_sso session, issue Doorkeeper tokens via MobileDevice, and redirect to sureapp://oauth/callback with tokens. Handles MFA users and unlinked accounts with error redirects. Validates provider name against configured SSO providers and device info before proceeding. * Add SSO auth flow to Flutter service and provider Add buildSsoUrl() and handleSsoCallback() to AuthService for constructing the mobile SSO URL and parsing tokens from the deep link callback. Add startSsoLogin() and handleSsoCallback() to AuthProvider for launching browser-based SSO and processing the redirect. * Register deep link listener for SSO callback Listen for sureapp://oauth/* deep links via app_links package, handling both cold start (getInitialLink) and warm (uriLinkStream) scenarios. Routes callbacks to AuthProvider.handleSsoCallback(). * Add Google Sign-In button to Flutter login screen Add "or" divider and outlined Google Sign-In button that triggers browser-based SSO via startSsoLogin('google_oauth2'). Add app_links and url_launcher dependencies to pubspec.yaml. * Fix mobile SSO failure handling to redirect back to app When OmniAuth fails during mobile SSO flow, redirect to sureapp://oauth/callback with the error instead of the web login page. Cleans up mobile_sso session data on failure. * Address PR review feedback for mobile SSO flow - Use strong params for device info in mobile_sso_start - Guard against nil session data in handle_mobile_sso_callback - Add error handling for AppLinks initialization and stream - Handle launchUrl false return value in SSO login - Use user-friendly error messages instead of exposing exceptions - Reject empty token strings in SSO callback validation * Consolidate mobile device token logic into MobileDevice model Extract duplicated device upsert and token issuance code from AuthController and SessionsController into MobileDevice. Add CALLBACK_URL constant and URL builder helpers to eliminate repeated deep-link strings. Add mobile SSO integration tests covering the full flow, MFA rejection, unlinked accounts, and failure handling. * Fix CI: resolve Brakeman redirect warnings and rubocop empty line Move mobile SSO redirect into a private controller method with an inline string literal so Brakeman can statically verify the target. Remove unused URL builder helpers from MobileDevice. Fix extra empty line at end of AuthController class body. * Use authorization code exchange for mobile SSO and add signup error handling Replace passing plaintext tokens in mobile SSO redirect URLs with a one-time authorization code pattern. Tokens are now stored server-side in Rails.cache (5min TTL) and exchanged via a secure POST to /api/v1/auth/sso_exchange. Also wraps device/token creation in the signup action with error handling and sanitizes device error messages. * Add error handling for login device registration and blank SSO code guard * Address PR #860 review: fix SSO race condition, add OpenAPI spec, and cleanup - Fix race condition in sso_exchange by checking Rails.cache.delete return value to ensure only one request can consume an authorization code - Use strong parameters (params.require) for sso_exchange code param - Move inline HTML from mobile_sso_start to a proper view template - Clear stale session[:mobile_sso] flag on web login paths to prevent abandoned mobile flows from hijacking subsequent web SSO logins - Add OpenAPI/rswag spec for all auth API endpoints * Fix mobile SSO test to match authorization code exchange pattern The test was asserting tokens directly in the callback URL, but the code uses an authorization code exchange pattern. Updated to exchange the code via the sso_exchange API endpoint. Also swaps in a MemoryStore for this test since the test environment uses null_store which discards writes. * Refactor mobile OAuth to use single shared application Replace per-device Doorkeeper::Application creation with a shared "Sure Mobile" OAuth app. Device tracking uses mobile_device_id on access tokens instead of oauth_application_id on mobile_devices. --------- Normalize whitespace in text rule matching (#890) Normalize whitespace in text-based rule filters so transaction names with irregular spacing still match. Refs #886 Add mailer subject tests and refine i18n keys (#910) * Add mailer subject tests and refine i18n keys * Linter * Fix test * More fixes * More fixes --------- 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 --------- Add encryption support to provider account models (#815) * Enable encryption for raw payloads in account models. * Add backfill support for Snaptrade, Coinbase, Coinstats, and Mercury accounts. API v1: add amount_cents + signed_amount_cents to transactions (#899) * feat(api): add amount_cents + signed_amount_cents to transactions * fix: use currency.minor_unit_conversion for amount_cents - Replace hardcoded *100 with currency.minor_unit_conversion - Handles JPY (0 decimals), KWD/BHD (3 decimals), etc. correctly - Add assert_amount_cents_fields helper to validate sign/scale invariants Update Romanian localization for profile subtitle placeholder (#885) Use `dependent: :purge_later` for ActiveRecord attachments (#882) * Use dependent: :purge_later for user profile_image cleanup This is a simpler alternative to PR #787's callback-based approach. Instead of adding a custom callback and method, we use Rails' built-in `dependent: :purge_later` option which is already used by FamilyExport and other models in the codebase. This single-line change ensures orphaned ActiveStorage attac… Signed-off-by: Josh Pigford <josh@joshpigford.com> |
||
|
|
2595885eb7 |
Full .ndjson import / reorganize UI with Financial Tools / Raw Data tabs (#1208)
* Reorganize import UI with Financial Tools / Raw Data tabs Split the flat list of import sources into two tabbed sections using DS::Tabs: "Financial Tools" (Mint, Quicken/QIF, YNAB coming soon) and "Raw Data" (transactions, investments, accounts, categories, rules, documents). This prepares for adding more tool-specific importers without cluttering the list. https://claude.ai/code/session_01BM4SBWNhATqoKTEvy3qTS3 * Fix import controller test to account for YNAB coming soon entry The new YNAB "coming soon" disabled entry adds a 5th aria-disabled element to the import dialog. https://claude.ai/code/session_01BM4SBWNhATqoKTEvy3qTS3 * Fix system tests to click Raw Data tab before selecting import type Transaction, trade, and account imports are now under the Raw Data tab and need an explicit tab click before the buttons are visible. https://claude.ai/code/session_01BM4SBWNhATqoKTEvy3qTS3 * feat: Add bulk import for NDJSON export files Implements an import flow that accepts the full all.ndjson file from data exports, allowing users to restore their complete data including: - Accounts with accountable types - Categories with parent relationships - Tags and merchants - Transactions with category, merchant, and tag references - Trades with securities - Valuations - Budgets and budget categories - Rules with conditions and actions (including compound conditions) Key changes: - Add BulkImport model extending Import base class - Add Family::DataImporter to handle NDJSON parsing and import logic - Update imports controller and views to support NDJSON workflow - Skip configuration/mapping steps for structured NDJSON imports - Add i18n translations for bulk import UI - Add tests for BulkImport and DataImporter * fix: Fix category import and test query issues - Add default lucide_icon ("shapes") for categories when not provided - Fix valuation test to use proper ActiveRecord joins syntax * Linter errors * fix: Add default color for tags when not provided in import * fix: Add default kind for transactions when not provided in import * Fix test * Fix tests * Fix remaining merge conflicts from PR 766 cherry-pick Resolve conflict markers in test fixtures and clean up BulkImport entry in new.html.erb to use the _import_option partial consistently. https://claude.ai/code/session_01BM4SBWNhATqoKTEvy3qTS3 * Import Sure `.ndjson` * Remove `.ndjson` import from raw data * Fix support for Sure "bulk" import from old branch * Linter * Fix CI test * Fix more CI tests * Fix tests * Fix tests / move PDF import to first tab * Remove redundant title --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
0cda69ebb0 |
Split UI (#1245)
* Initial split transaction support
* Add support to unsplit and edit split
* Update show.html.erb
* FIX address reviews
* Improve UX
* Update show.html.erb
* Reviews
* Update edit.html.erb
* Add parent category to dialog
* Update en.yml
* Add UI indication to totals
* FIX ui update
* Add category select like rest of app
* Add split ui
* Add settings configuration for split transactions
- Adds a new settings section for appearance changes
- Also adds extra checks for delete and API calls
- Also adds checks for parent/child changes
* fixes
- split transactions dark mode fix
- add split transactions to context menu
* Update entry.rb
1. New validation split_child_date_matches_parent — prevents saving a split child with a date different from its parent. This is the root-cause fix that
protects all flows at once.
2. Bulk update guard — bulk_update! now strips :date from attributes when processing split children, preventing the validation from raising and silently
skipping the date change instead.
* N+1 fix for split_parent?
* Update entry.rb
Problem: In bulk_update!, when a split child has :date removed from attrs (line 432) and the remaining attrs is empty (e.g., the bulk update only
changed the date), entry.update! {} still ran as a no-op. But lock_saved_attributes! and mark_user_modified! at lines 443-444 executed unconditionally,
incorrectly marking untouched split children as user-modified and opting them out of future syncs.
Fix:
1. Added a changed flag to track whether any actual modification happened
2. Wrapped entry.update! in an if attrs.present? check so no-op updates are skipped
3. Gated lock_saved_attributes! and mark_user_modified! behind if changed, so they only run when the entry was actually modified (either via attribute
update or tag update)
* fixes
1. Indentation in show.html.erb Settings section — The split button block and delete block had extra indentation making them appear nested inside guard
blocks they weren't part of. Fixed to match actual nesting.
2. Skip @split_parents query when grouping is off — The controller now only loads split parent entries when show_split_grouped? is true, saving a query
with joins when the feature is disabled.
|
||
|
|
e1ff6d46ee |
Make categories global (#1160)
* Make categories global This solves us A LOT of cash flow and budgeting problems. * Update schema.rb * Update auto_categorizer.rb * Update income_statement.rb * FIX budget sub-categories * FIX sub-categories and tests * Add 2 step migration |
||
|
|
0f78f54f90 |
New select component (#1071)
* feat: add new UI component to display dropdown select with filter * feat: use new dropdown componet for category selection in transactions * feat: improve dropdown controller * feat: Add checkbox indicator to highlight selected element in list * feat: add possibility to define dropdown without search * feat: initial implementation of variants * feat: Add default color for dropdown menu * feat: add "icon" variant for dropdown * refactor: component + controller refactoring * refactor: view + component * fix: adjust min width in selection for mobile * feat: refactor collection_select method to use new filter dropdown component * fix: compute fixed position for dropdown * feat: controller improvements * lint issues * feat: add dot color if no icon is available * refactor: controller refactor + update naming for variant from icon to logo * fix: set width to 100% for select dropdown * feat: add variant to collection_select in new transaction form * fix: typo in placeholder value * fix: add back include_blank property * refactor: rename component from FilterDropdown to Select * fix: translate placeholder and keep value_method and text_method * fix: remove duplicate variable assignment * fix: translate placeholder * fix: verify color format * fix: use right autocomplete value * fix: selection issue + controller adjustments * fix: move calls to startAutoUpdate and stopAutoUpdate * Update app/javascript/controllers/select_controller.js Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Alessio Cappa <104093777+alessiocappa@users.noreply.github.com> * fix: add aria-labels * fix: pass html_options to DS::Select * fix: unnecessary closing tag * fix: use offsetvalue for position checks * fix: use right classes for dropdown transitions * include options[:prompt] in placeholder init * fix: remove unused locale key * fix: Emit a native change event after updating the input value. * fix: Guard against negative maxHeight in constrained layouts. * fix: Update test * fix: lint issues * Update test/system/transfers_test.rb Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Alessio Cappa <104093777+alessiocappa@users.noreply.github.com> * Update test/system/transfers_test.rb Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Alessio Cappa <104093777+alessiocappa@users.noreply.github.com> * refactor: move CSS class for button select form in maybe-design-system.css --------- Signed-off-by: Alessio Cappa <104093777+alessiocappa@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> |
||
|
|
0057458963 |
Add dynamic assistant icon: OpenClaw lobster SVG for external assistant (#1122)
* Add dynamic assistant icon: OpenClaw lobster SVG for external assistant When a family (or installation via ASSISTANT_TYPE env var) uses the "external" assistant, the AI avatar now shows a lobster/claw icon (claw.svg / claw-dark.svg) instead of the default builtin AI icon. The icon switches dynamically based on the current configuration. https://claude.ai/code/session_01Wt7HiFypk3Nbs8z2hAkmkG * Update app/views/chats/_ai_avatar.html.erb Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Juan José Mata <jjmata@jjmata.com> --------- Signed-off-by: Juan José Mata <jjmata@jjmata.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> |
||
|
|
98df0d301a |
fix/qol: Add Callback URL the Enable Banking Instructions (#1060)
* fix/qol: Add wich Callback URL to use to the Enable Banking Instructions * CodeRabbit suggestion * CodeRabbit suggestion * Skip CI failure on findings --------- Co-authored-by: Juan José Mata <jjmata@jjmata.com> |
||
|
|
e0ae71f33a |
fix: Viewport issue in PWA (#995)
* fix: move safe-area padding from body/HTML to navbars. Add script to compute app height dynamically. * fix: Initialize sankey tooltip with top-0 to avoid overflow * fix: add fallback to HTML height * fix: properly set bottom spacing and use position fixed for bottom navbar * fix: move viewport controller initialization |
||
|
|
868a0ae4d8 |
Add family moniker selection and dynamic UI labels (#981)
* Add family moniker selection and dynamic UI labels Introduce a Family moniker persisted in the database with allowed values Family/Group, add required onboarding selection for it, and thread moniker-aware copy through key user-facing views and locales. Also add helper methods and tests for onboarding form presence and family moniker behavior. * Small copy edits/change moniker question order * Conditional Group/Family onboarding flow fixes * Fix label * Grouping of fields * Profile Info page Group/Family changes * Only admins can change Group/Family moniker * Repetitive defaults * Moniker in Account model * Moniker in User model * Auth fix * Sure product is also a moniker --------- Signed-off-by: Juan José Mata <juanjo.mata@gmail.com> |
||
|
|
ea80e92b6b |
Add Palestine to country options in preferences (#845)
Co-authored-by: Abdallatif Sulaiman <asulaiman@restaurant365.com> |
||
|
|
8c9764f1ad |
Unify provider and account card UI and move setup actions to menus (#755)
* feat: add auto-open functionality for collapsible sections and streamline unlinked account handling - Introduce `auto-open` Stimulus controller to auto-expand <details> elements based on URL params. - Update all settings sections and panels to support the new `auto_open_param` for seamless navigation. - Improve unlinked account logic for Coinbase, SimpleFIN, and SnapTrade, ensuring consistent and optimized handling. - Refactor sync warnings and badges for better readability and user experience. - Extend localization for additional menu items, warnings, and setup prompts. * fix: improve error handling and safe HTML usage in Coinbase and settings components - Log warning for unhandled exceptions in Coinbase unlinked account count fallback. - Escape `auto_open_param` in settings section for safe HTML injection. - Clean up URL params in `auto-open` controller after auto-expansion. --------- Co-authored-by: luckyPipewrench <luckypipewrench@proton.me> |
||
|
|
a83f70425f |
Add SnapTrade brokerage integration with full trade history support (#737)
* Introduce SnapTrade integration with models, migrations, views, and activity processing logic. * Refactor SnapTrade activities processing: improve activity fetching flow, handle pending states, and update UI elements for enhanced user feedback. * Update Brakeman ignore file to include intentional redirect for SnapTrade OAuth portal. * Refactor SnapTrade models, views, and processing logic: add currency extraction helper, improve pending state handling, optimize migration checks, and enhance user feedback in UI. * Remove encryption for SnapTrade `snaptrade_user_id`, as it is an identifier, not a secret. * Introduce `SnaptradeConnectionCleanupJob` to asynchronously handle SnapTrade connection cleanup and improve i18n for SnapTrade item status messages. * Update SnapTrade encryption: make `snaptrade_user_secret` non-deterministic to enhance security. --------- Signed-off-by: Juan José Mata <juanjo.mata@gmail.com> Co-authored-by: luckyPipewrench <luckypipewrench@proton.me> Co-authored-by: Juan José Mata <juanjo.mata@gmail.com> |
||
|
|
8e36c8e736 |
Rename billing to payment throughout the codebase (#726)
* Rename billing to payment throughout the codebase This change updates terminology from "billing" to "payment" to better reflect that these are contributions/payments rather than bills. Changes include: - Rename BillingsController to PaymentsController - Rename billing_email to payment_email - Rename next_billing_date to next_payment_date - Rename create_billing_portal_session_url to create_payment_portal_session_url - Update routes from billing to payment - Update all 12 locale files with new terminology - Update views, helpers, and tests * Update app/views/subscriptions/upgrade.html.erb Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Juan José Mata <jjmata@jjmata.com> --------- Signed-off-by: Juan José Mata <jjmata@jjmata.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> |
||
|
|
777fbdc4ca |
feat(settings): add pagination to imports and exports pages (#598)
* feat(settings): split imports and exports * feat(security): sanitize pagination params to prevent abuse * fix(settings): fix syntax in settings nav * feat(settings): internationalize family_exports and imports UI strings * fix(settings): fix coderabbit review * fix(settings): fix coderabbit review * fix(settings): fix coderabbit review * Change default per_page value from 20 to 10 Signed-off-by: Juan José Mata <jjmata@jjmata.com> * Add `/family_export` to navigation * Consistency with old defaults * Align `safe_per_page` even if not DRY --------- Signed-off-by: Julien Orain <julien.orain@gmail.com> Signed-off-by: Juan José Mata <jjmata@jjmata.com> Signed-off-by: Juan José Mata <juanjo.mata@gmail.com> Co-authored-by: JulienOrain <your-github-email@example.com> Co-authored-by: Juan José Mata <jjmata@jjmata.com> Co-authored-by: Juan José Mata <juanjo.mata@gmail.com> |
||
|
|
bf9bcae600 |
Add gains by tax treatment to investment report with grouped subtype dropdown (#701)
* Add tax treatment metrics to reports, forms, and models - Implement `build_gains_by_tax_treatment` for grouping gains by tax treatment - Update investment performance view with tax treatment breakdown - Add tax treatment field to crypto and investments forms - Introduce `realized_gain_loss` calculation in the Trade model - Group investment subtypes by region for improved dropdown organization * Optimize investment performance report by reducing N+1 queries - Eager-load associations in `build_gains_by_tax_treatment` to minimize database queries - Preload holdings for realized gain/loss calculations in trades - Refactor views to standardize "no data" placeholder using translations - Adjust styling in tax treatment breakdown for improved layout * Enhance investment performance translations and optimize holdings lookup logic - Update `holdings_count` and `sells_count` translations to handle pluralization - Refactor views to use pluralized translation keys with count interpolation - Optimize preloaded holdings lookup in `Trade` to ensure deterministic selection using `select` and `max_by` * Refine preloaded holdings logic in `Trade` model - Treat empty preloaded holdings as authoritative to prevent unnecessary DB queries - Add explicit fallback behavior for database query when holdings are not preloaded --------- Co-authored-by: luckyPipewrench <luckypipewrench@proton.me> |
||
|
|
7f0781179c |
Add Dutch (nl) translations for UI and models (#702)
* Add Dutch (nl) translations for UI and models
Added comprehensive Dutch translation files for models, views, mailers, and Doorkeeper, covering accounts, categories, admin, and more. Updated languages_helper.rb to include 'nl' as a supported language. Improved capitalization and consistency in existing Dutch date and number formats.
* ai sugestions
* nitpick fix
* Fix Dutch translations and improve consistency
Corrected minor issues and improved consistency in Dutch locale files, including fixing typos, updating terminology (e.g., 'Self-Hosting' to 'Zelfhosting', 'Provider selectie' to 'Providerselectie'), and ensuring proper formatting. No functional changes were made; this commit only affects translation files.
* Update Dutch translations for product name and pluralization
Replaced hardcoded product names with %{product_name} in password reset and API key views for improved reusability. Updated wallet setup message in CoinStats item model to support correct Dutch pluralization.
* Update nl.yml
|
||
|
|
3fc3e20c36 |
i18n: Add French translations (#658)
* i18n: Add French translations Add complete French translation files for the application. Co-Authored-By: Norman Alié <mail@normanalie.fr> Co-Authored-By: Xurron <corentin.boeglin2005@gmail.com> * fix: Correct YAML syntax in French securities translations Co-Authored-By: Norman Alié <mail@normanalie.fr> Co-Authored-By: Xurron <corentin.boeglin2005@gmail.com> --------- Co-authored-by: Norman Alié <mail@normanalie.fr> Co-authored-by: Xurron <corentin.boeglin2005@gmail.com> |
||
|
|
25ac822308 |
Reports print functionality (#622)
* Print initial impl * Try to keep the bigger section together * /* Tufte-inspired Print Report Styles */ * styling * I8n * Move print styling out. * FIX unrelated test ordering on line 53 - import.rows.first doesn't guarantee ordering. Without an explicit ORDER BY, the database may return rows in any order. * Update print-report.css * Update print.html.erb * pass data to view * Update index.html.erb * Fix ERB helpers * Update reports_helper.rb |
||
|
|
dddc2182a4 |
feat(zh-TW): add Traditional Chinese localization support (#503)
* feat(zh-TW): add Traditional Chinese localization support Integrates comprehensive zh-TW locale files across UI, models, emails, and helpers. Updates language mapping for Chinese (Traditional) and adds translations for various modules. Establishes full Traditional Chinese support in the app. * feat(locales): add zh-TW translations Add comprehensive Traditional Chinese (zh-TW) translations for UI, defaults, Doorkeeper, mailers, models, and views to provide full Taiwanese localization and improve wording consistency. Replace and update several existing zh-TW entries for clarity and consistency. Also expose the Postgres port in the example compose for easier local development and apply minor locale/typo/whitespace fixes. * feat(locales): add zh-TW translations Add Traditional Chinese (zh-TW) locale files across many views and settings to provide Taiwanese localization. Introduce updated translations for authentication, onboarding, settings, integrations (Plaid, SimpleFin, Lunch Flow), accounts, reports, and various resource pages. Remove or replace legacy locale files to align with the revamped i18n structure and copy organization. This enables full zh-TW support for the UI. * chore(docker): remove published Postgres port Remove the published Postgres port mapping (5432) from the example docker-compose file to avoid exposing the database to the host and to prevent accidental port conflicts. Keeps the example more secure and focused on internal service networking. * docs(i18n): 統一 SimpleFIN 在繁體中文翻譯的大小寫 將 zh-TW 翻譯中所有出現的 "SimpleFin" 更新為品牌正確的 "SimpleFIN", 包含標題、提示文字、成功/錯誤訊息及表單標籤,以維持品牌名稱一致性 並提升使用者介面的翻譯準確性。 |
||
|
|
3658e812a8 |
Add pending transaction handling and duplicate reconciliation logic (#602)
* Add pending transaction handling and duplicate reconciliation logic - Implemented logic to exclude pending transactions from budgets and analytics calculations. - Introduced mechanisms for reconciling pending transactions with posted versions. - Added duplicate detection with support for merging or dismissing matches. - Updated transaction search filters to include a `status_filter` for pending/confirmed transactions. - Introduced UI elements for reviewing and resolving duplicates. - Enhanced `ProviderSyncSummary` with stats for reconciled and stale pending transactions. * Refactor translation handling and enhance transaction and sync logic - Moved hardcoded strings to locale files for improved translation support. - Refined styling for duplicate transaction indicators and sync summaries. - Improved logic for excluding stale pending transactions and updating timestamps on batch exclusion. - Added unique IDs to status filters for better element targeting in UI. - Optimized database queries to avoid N+1 issues in stale pending calculations. * Add sync settings and enhance pending transaction handling - Introduced a new "Sync Settings" section in hosting settings with UI to toggle inclusion of pending transactions. - Updated handling of pending transactions with improved inference logic for `posted=0` and `transacted_at` in processors. - Added priority order for pending transaction inclusion: explicit argument > environment variable > runtime configurable setting. - Refactored settings and controllers to store updated sync preferences. * Refactor sync settings and pending transaction reconciliation - Extracted logic for pending transaction reconciliation, stale exclusion, and unmatched tracking into dedicated methods for better maintainability. - Updated sync settings to infer defaults from multiple provider environment variables (`SIMPLEFIN_INCLUDE_PENDING`, `PLAID_INCLUDE_PENDING`). - Refined UI and messaging to handle multi-provider configurations in sync settings. # Conflicts: # app/models/simplefin_item/importer.rb * Debounce transaction reconciliation during imports - Added per-run reconciliation debouncing to prevent repeated scans for the same account during chunked history imports. - Trimmed size of reconciliation stats to retain recent details only. - Introduced error tracking for reconciliation steps to improve UI visibility of issues. * Apply ABS() in pending transaction queries and improve error handling - Updated pending transaction logic to use ABS() for consistent handling of negative amounts. - Adjusted amount bounds calculations to ensure accuracy for both positive and negative values. - Refined exception handling in `merge_duplicate` to log failures and update user alert. - Replaced `Date.today` with `Date.current` in tests to ensure timezone consistency. - Minor optimization to avoid COUNT queries by loading limited records directly. * Improve error handling in duplicate suggestion and dismissal logic - Added exception handling for `store_duplicate_suggestion` to log failures and prevent crashes during fuzzy/low-confidence matches. - Enhanced `dismiss_duplicate` action to handle `ActiveRecord::RecordInvalid` and display appropriate user alerts. --------- Co-authored-by: Josh Waldrep <joshua.waldrep5+github@gmail.com> |
||
|
|
f76f541c05 |
Settings page UI improvements (#495)
* fix: UI fixes for "Settings" page on mobile * Critical to the Rabbit! --------- Co-authored-by: Juan José Mata <juanjo.mata@gmail.com> |
||
|
|
3b8888c8de |
Add Chinese localization (#471)
* add zh-CN.yml for chinese * The files appear to use CRLF line endings instead of LF (Unix-style). * Add the missing entries to the zh-CN.yml file and include the Simplified Chinese option. * Fix grammatical errors Signed-off-by: jiang123574 <jiang123574@163.com> * Update languages_helper.rb Signed-off-by: jiang123574 <jiang123574@163.com> * Update 'SimpleFin' to 'SimpleFIN' in translations Signed-off-by: jiang123574 <jiang123574@163.com> * update zh-CN.yml * update zh-CN.yml * add new zh-CN.yml * CodeRabbit comments * Enable Banking i18n --------- Signed-off-by: jiang123574 <jiang123574@163.com> Co-authored-by: Juan José Mata <juanjo.mata@gmail.com> |
||
|
|
70b050e4a4 |
Rules: Fix no action conditions (#447)
* Fix Rules page when no action on rule * Reject new rules without actions * Rule with no action translation * Easy one to keep translations going * Fix tests * Learn something new every day --------- Co-authored-by: Juan José Mata <juanjo.mata@gmail.com> |
||
|
|
ea35296def |
feat(lang): add all brazilian portuguese translations (#416)
* feat(lang): add all brazilian portuguese translations * feat: update pt-BR errors on translation * fix: atualizar fix base * feat: add reports translations * feat: finish translation to brazilian portuguese * fix: add to supported locales * fix: number of translations * fix: errors on translations * fix: error on rubocop lint --------- Co-authored-by: Leonardo Ralph <theleoralph@gmail.com> |
||
|
|
e5ed946959 |
Add rules import/export support (#424)
* Add full import/export support for rules with versioned JSON schema This commit implements comprehensive import/export functionality for rules, allowing users to back up and restore their rule definitions. Key features: - Export rules to both CSV and NDJSON formats with versioned schema (v1) - Import rules from CSV with full support for nested conditions and actions - UUID to name mapping for categories and merchants for portability - Support for compound conditions with sub-conditions - Comprehensive test coverage for export and import functionality - UI integration for rules import in the imports interface Technical details: - Extended Family::DataExporter to generate rules.csv and include rules in all.ndjson - Created RuleImport model following the existing Import STI pattern - Added migration for rule-specific columns in import_rows table - Implemented serialization helpers to map UUIDs to human-readable names - Added i18n support for the new import option - Included versioning in NDJSON export to support future schema evolution The implementation ensures rules can be safely exported from one family and imported into another, even when category/merchant IDs differ, by mapping between names and IDs during export/import. * Fix AR migration version * Mention support for rules export * Rabbit suggestion * Fix tests * Missed schema.rb * Fix sample CSV download for rule import * Fix parsing in Rules import * Fix tests * Rule import message i18n * Export tag names, not UUIDs * Make sure tags are created if needed at import * Avoid test errors when running in parallel --------- Co-authored-by: Claude <noreply@anthropic.com> |