mirror of
https://github.com/we-promise/sure.git
synced 2026-09-04 06:11:12 +00:00
75aa16e4e2879003b4aee3268ea8a6782eed2e4a
18
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
176bb508e4 |
feat(i18n): complete the German locale (#2847)
* fix(i18n): replace hardcoded UI strings with translation keys
Fourteen views printed English text directly instead of going through
t(). Those strings could not be translated in any locale, so French and
Italian users saw English there as well, even though both locales are
otherwise complete.
The Mint import form was the worst of them with thirteen hardcoded
strings, including the intro text and the submit button. The valuation
confirmation dialog passed "set" and "update" into an interpolation as
bare English words, so the verb stayed English whatever the locale.
Adds 35 keys to en and de.
* feat(i18n): complete the German locale
German covered 55 percent of the English keys, against 97 for French and
90 for Italian. The gaps ran through settings, imports, goals, insights
and every provider integration, so the interface kept switching language
in the middle of a page.
This fills 3170 keys. Terminology follows what the existing German files
already used: Konto for account, Händler for merchant, Familie for
family. Provider names, ticker symbols and the IBKR flex query field
names stay in English, because that is what users see in those services
themselves.
Interpolation variables were checked against the English source for every
translated key.
* fix(i18n): use one form of address across the German locale
The German files mixed the informal du and the formal Sie, sometimes
within the same dialog. Most of the existing strings already used du, so
the remaining 39 now follow suit.
Several of those sentences also read like form letters. "Bitte versuchen
Sie es erneut" is now "Versuch es noch einmal".
* fix(i18n): correct German names for account types
Depository accounts were called Bargeld, meaning cash, in the views,
while the model called them Bankkonto. The icon is a bank building and
the type covers checking, savings, CDs and money market accounts, so
Bankkonto fits in both places.
Investment accounts were called Investition. In German that word means
the act of investing rather than the account that holds it, so the label
read as if the app were tracking transactions instead of a portfolio.
Investment is the term people use for the account itself.
* fix(i18n): pass the account name to the SimpleFIN dialog title
The key simplefin_items.select_existing_account.title already existed and
interpolates %{account_name}. Calling t(".title") without it raised
I18n::MissingInterpolationArgument before the dialog rendered.
The controller sets @account for this action, so the title can use it and
name the account being linked.
* fix(i18n): finish the German consistency pass
Follow-up to review feedback on this branch.
Quotation marks: 32 strings opened with the German „ but closed with the
straight ASCII quote, which renders as broken punctuation. They now close
with “.
Address form: 311 strings still used the formal Sie, mostly in the
provider integrations that the earlier commit left alone. Mixing both
registers inside one screen was worse than either choice on its own, so
they now use du like the rest.
Wording: the securities breadcrumb said Sicherheit, which means safety
rather than the financial instruments, and now reads Wertpapiere. The
Redbark setup failure had a typo in the imperative. An invitation message
broke off mid-sentence without naming the household. SimpleFin is spelled
SimpleFIN throughout, matching the provider.
* fix(i18n): repair verb forms left by the address change
The previous commit swapped Sie for du with a rule set, which changed the
pronoun but left the verb in its formal form. That produced sentences like
"die du importieren möchten" instead of "möchtest", and "bevor du Brex-Konten
verknüpfen können" instead of "kannst".
Also fixes lowercase deine at the start of a sentence, which came from
replacing Ihre without looking at position, and the last Sie forms in the
Sophtron block scalar and the chat demo banner.
41 strings in total.
* fix(i18n): keep straight quotes inside the HTML attribute
My quote sweep replaced the closing ASCII quote after an opening „ with “,
and in treat_as_html that ASCII quote was the delimiter of class="font-medium".
A curly quote cannot delimit an HTML attribute, so the span rendered with a
broken class attribute rather than merely looking odd.
The attribute now uses straight quotes and only the surrounding citation marks
are typographic. Checked every German value containing markup for the same
mistake; this was the only one.
* fix(i18n): address findings from an independent review pass
Three passes went over this branch: the two bots on the PR, plus a
separate audit of the German values, the t() calls and the YAML structure.
This closes what they found.
Two defects were invisible until then.
settings.providers.status carried the key "false" instead of "off". The
English file writes `off:` unquoted, which YAML reads as the boolean false,
and I had copied the parsed name rather than the intended one. The code
looks up :off, so the status pill for unconfigured providers found nothing
at all. Both files now quote the key, which repairs English too.
The valuation confirmation dialog built a sentence that only works in
English. There, Set and Update open the sentence as imperatives; my German
values were participles, and the partial capitalises them at the start, so
it read "Gesetzt Kontostand am ...". The values are now "Neu:" and
"Geändert:", which carry a sentence opening. The date runs through l()
instead of strftime with a US format, so the German dialog no longer says
"July 30, 2026".
Grammar left behind by the earlier rule-based Sie/du change: 33 strings had
lost the verb particle ("Richte X." instead of "Richte X ein."), six kept an
infinitive after "bevor du", and one turned a pronoun referring to merchants
into a form of address, which reversed the meaning.
Placeholders that had been hardcoded: seven %{moniker}, where users can
choose Gruppe but the German text said Familie regardless, one
%{product_name}, and %{message} in an authorization error that swallowed
the cause.
Structure: entries.selection_bar.edit gave way to the long-standing
transactions.selection_bar.edit, which exists in 15 languages and would
otherwise have fallen back to English for all of them. simplefin_items
gained the missing check_provider_health in en and de, and two buttons
there now use keys that already existed instead of English literals.
Two notes on scope and wording, since this branch has grown well past its
original shape.
The PR began as pure gap-filling and deliberately left existing
translations untouched. The address change and these grammar fixes reach
into them because a dialog that switches register mid-screen is worse than
one that stays formal throughout. Existing values are roughly nine percent
of the diff and sit in separate commits, so they can be dropped on their
own if you would rather keep them out.
German terms stay close to the length of the English source. The buttons
size to their content, but DS::Buttonish sets whitespace-nowrap, so a
longer word widens the button instead of wrapping and pushes its
neighbours around in button rows, table cells and the sidebar. That is why
this locale says Sync rather than Synchronisieren, Setup rather than
Einrichtung, API-Key rather than API-Schlüssel. Where surrounding files use
the longer form, the difference is deliberate.
* fix(i18n): keep flash messages within the notification clamp
The notification partial renders flash messages at md:max-w-80 with
line-clamp-3, so anything past roughly 130 characters is cut off and
survives only in the title attribute. Ten German messages had crossed that
line where the English stayed under it, the longest at 205 characters.
They now say the same thing with fewer detours. "Dieser Vorgang lässt sich
gerade nicht ändern, sein Job steht möglicherweise noch in der Warteschlange
oder läuft" became "Der Job läuft oder wartet noch".
Two of them carried the same text under different keys in the hosting
settings, the older wording at 175 characters. Both read the same now.
Also from review: the region message mixed grammatical cases ("Für diese
Region/Land"), one invitation string still used Sie, and four Sophtron
messages pointed users at an API key configuration when that provider uses
a user ID and an access key.
* test(i18n): cover German locale completeness
* feat(i18n): translate the keys that landed while this PR was open
Upstream added the Plan page, insight acknowledgements and the SnapTrade
account type picker after this branch was cut. The coverage test from
|
||
|
|
c45b786e86 |
fix(ds): migrate SSO amber warning surfaces to DS::Alert / DS::Pill (#2246)
Apply the resolved warning-hue decision (use --color-warning; no separate amber token) to the admin SSO surfaces flagged in #2198: - admin/sso_providers: the "legacy providers" notice → DS::Alert(:warning); the "ENV configured" badge → DS::Pill(tone: :warning). - settings/securities: the single-OIDC password warning → DS::Alert(:warning). Replaces hand-rolled bg-amber-50 / border-amber-200 / text-amber-800 boxes and an amber badge with the functional warning token via DS components. The DS::Alert warning recipe (warning tint + neutral text + alert-triangle) matches the prior look while clearing the literal-token drift. Part of #2198. |
||
|
|
4e0da0c237 |
fix(ds): dark-parity for bespoke green status badges (#2142)
* fix(ds): dark-parity for bespoke green status badges (#2134) The status/type badges in recurring transactions, admin SSO providers, and the categorize group title used solid light-green tints (bg-green-50/100 + text-green-700/800) with no dark variant, so they rendered as pale "stickers" on dark. Add theme-dark variants mirroring DS::Pill's soft recipe (bg-green-tint-10 + text-green-200); dark text contrast 9.5-14.2:1. Their neutral siblings were already token-based. Audit-named _status_pill / _maturity_badge already render via DS::Pill; remaining solid-tint spots are alpha icon-containers (bg-*-500/5,/10) that composite fine on dark. Full DS::Pill consolidation tracked separately. * fix(ds): dark-parity for bespoke amber/blue notices + SSO badge (#2134) Continues the bespoke-badge tail: the raw-amber warning notices (securities SSO warning, admin SSO legacy-providers notice) + the env-configured badge, and the AI tool-call info box, were solid light tints (bg-amber-50/100 / bg-blue-50) with no dark variant -> pale "stickers" on dark. Add theme-dark variants mirroring the soft recipe: alpha-tint bg (amber/blue-500/10-15), lightened border, light text/icon (amber-200/400). Verified legible on dark. Pre-auth (sessions/new) + infra (redis error) amber notices render light-only, left as-is. Broader: these warning boxes want a shared DS notice/callout component (#2137). |
||
|
|
172301f875 | Fix SSO provider settings updates (#2210) | ||
|
|
e15ae5b23a | fix: replace hand-rolled badges with DS::Pill in admin users view (#1988) | ||
|
|
adea16f694 |
fix(views): clear Rule 2 + Rule 5 findings from weekly DS drift (#1951) (#1955)
* fix(views): clear Rule 2 + Rule 5 findings from weekly DS drift (#1951) Token swaps + i18n cleanup across the three files flagged in the weekly merged-commit drift scan. **`app/views/admin/users/index.html.erb`** - `bg-green-100 text-green-800` → `bg-success/10 text-success` (2 callsites — active-subscription badge + super_admin role legend) - `bg-surface-default` → `bg-surface` (`--color-surface-default` isn't defined; canonical token is `--color-surface`) - `bg-red-50/30 dark:bg-red-950/20` → `bg-destructive/5` (pending-invitation row highlight; functional token resolves correctly in both themes via `--color-destructive`) - Hand-rolled destructive button classes (`text-red-600`, `border-red-300`, `hover:bg-red-50`) → functional tokens (`text-destructive`, `border-destructive`, `hover:bg-destructive/10`) - Drop redundant `default:` args from `t(".roles.member", default: "Member")` and `t(".role_descriptions.member", default: "Basic user access…")` — the locale keys exist in `config/locales/views/admin/users/en.yml` **`app/views/imports/new.html.erb`** - `icon_bg_class: "bg-gray-tint-5"` → `"bg-surface-inset"` (`gray-tint-5` isn't a defined utility; `bg-surface-inset` carries the same muted-background intent and theme-swaps correctly) **`app/views/settings/profiles/show.html.erb`** - Drop redundant `default:` args from `t(".group_title", default: "Group")`, `t(".group_form_label", default: "Group name")`, and `t(".group_form_input_placeholder", default: "Enter group name")` — all three keys exist in `config/locales/views/settings/en.yml` **Deferred** to a separate PR (Rule 1 findings on admin/users): - `<details>` block (lines 54–180) → `DS::Disclosure(:card)` — bigger refactor with custom summary content + Stimulus controller attributes; warrants its own diff. - Destructive button shell → `DS::Button(:destructive)` — same reason; the class-token swap in this PR clears the immediate violation without changing the form-with structure or visual. Refs #1951. * fix(profiles): restore i18n default: args for group_* keys @jjmata + @codex correctly flagged: `settings.profiles.show.group_title`, `group_form_label`, and `group_form_input_placeholder` are defined in en.yml + 4 other locales (de, es, pl, pt-BR), but missing from 8 locales (ca, fr, nb, nl, ro, tr, zh-CN, zh-TW). With `config.i18n.fallbacks = true` those locales currently fall back to en values, so end-users see English copy rather than a translation-missing marker. The `default:` arg makes the fallback explicit at the call site without depending on the Rails fallback chain being configured a particular way — restores the original defensive behavior from before #1955. Admin/users role keys keep their `default:` removal — verified that `roles.member` and `role_descriptions.member` exist in all 8 admin/users locales (`grep -c "^\s*member:"` returns 2 for every locale file). |
||
|
|
9182346c6c |
fix(admin/users): use parent space-y-6 for sibling section spacing (#1934)
The admin users page wraps four top-level sibling sections inside a single `bg-container rounded-xl shadow-border-xs p-4` card: 1. description paragraph 2. filter form 3. trials-expiring summary grid 4. families/groups list 5. role descriptions (`settings_section` collapsible → DS::Disclosure :card) The first three carried their own `mb-6`; the families list and the role descriptions section had no margin at all, so the families card sat flush against the role-descriptions card with zero gap — clearly broken next to the well-spaced upper sections. Apply spacing at the **layout** level: hoist `space-y-6` onto the outer container and drop the per-child `mb-6`. All five siblings now get a consistent 24px gap. No other admin or settings pages match this exact pattern (single outer card + multiple sibling sections without parent space-y) — the settings layout already wraps `<%= yield %>` in `space-y-4`, and other pages with outer cards (`api_keys/show`, `llm_usages/show`, etc.) either rely on that layout or carry their own internal `space-y-N`. |
||
|
|
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
|
||
|
|
2bcdf6c554 |
fix(design-system): replace undefined utility classes and broken /N modifiers (#1660)
* fix(design-system): replace undefined utility classes and broken /N modifiers
Audit of class-name resolution in views surfaced two related silent
failures across ~17 files:
1. Class names that don't exist anywhere in the design system. Tailwind
silently drops them and the element renders with no CSS for that
property.
- bg-primary (and bg-primary/5, /10, /90): never defined as a
custom utility, no --color-primary in @theme. Used as a CTA bg
in 8 places, all rendered transparent.
- text-inverted: typo of text-inverse.
- text-primary-foreground: shadcn/Radix vocabulary, not in our
token system.
- bg-accent / border-accent / text-accent: same shadcn vocabulary;
not defined.
2. Slash modifier (/N) used on custom @utility blocks. Modifiers only
resolve on Tailwind theme colors (anything in tokens.json color.*).
Custom @utility blocks compile to static @apply statements and
silently drop the /N variant. Affected uses:
- border-surface-inset/50 across provider account selectors.
- border-secondary/30, /40 in admin SSO form and simplefin setup.
- bg-surface-inset/30, /40 in settings preferences and simplefin.
Fixes:
| From | To |
|---------------------------------------------------|------------------------------------------------------|
| bg-primary text-white (and similar primary CTAs) | button-bg-primary text-inverse |
| bg-primary text-primary-foreground (badges) | button-bg-primary text-inverse |
| bg-primary text-inverted (typo) | button-bg-primary text-inverse |
| bg-primary text-primary (broken active pill) | bg-inverse text-inverse |
| bg-primary (status dot) | bg-inverse |
| bg-primary/5, bg-primary/10 (subtle accent bg) | bg-gray-tint-5, bg-gray-tint-10 |
| hover:bg-primary/90 | hover:button-bg-primary-hover |
| border-accent bg-accent/10 text-accent (badges) | border-secondary bg-surface-inset text-secondary |
| border-surface-inset/50 | border-secondary |
| border-secondary/30, /40 | border-tertiary |
| bg-surface-inset/30 | bg-surface-inset (full strength) |
| bg-surface-inset/40 | bg-container-inset |
Also documents the alpha-modifier limitation in design/tokens/README.md
under a new "Alpha modifiers in views (/N syntax)" section, with the
opacity-N convention for custom utilities and a note that the
gray-tint-5 / gray-tint-10 family (and similar pre-resolved tints) are
theme colors and accept /N modifiers natively.
The accent-badge mapping uses neutral semantics for now. A dedicated
brand-accent token (text-link-tint-10 etc.) is worth considering as a
follow-up if the "highlighted metadata badge" pattern recurs.
* fix(design-system): replace undefined divide-primary / divide-secondary with alpha tokens
Same class of bug as the rest of this PR: divide-{name} requires the
name to be a theme color (i.e. expose --color-{name}), and our custom
@utility utilities (primary, secondary, etc.) do not. Tailwind silently
drops the unrecognized class and rows render with no separator.
Spotted six instances during the visual audit:
- admin/users/index.html.erb (×2): users table + pending invitations
- admin/sso_providers/index.html.erb (×2): configured + legacy lists
- transactions/categorizes/_transaction_list.html.erb: categorize sidebar
- settings/preferences/show.html.erb: divide-secondary/60 (also broken)
Swapped to the alpha-black/white pattern already used elsewhere in the
codebase (imports/cleans/show, transactions/_summary, etc.):
divide-y divide-primary
-> divide-y divide-alpha-black-200 theme-dark:divide-alpha-white-200
divide-y divide-secondary/60
-> divide-y divide-alpha-black-100 theme-dark:divide-alpha-white-100
The lighter (-100) variant on the preferences list matches the original
intent of /60 (more subtle).
|
||
|
|
71c0735824 | Linter | ||
|
|
02af8463f6 |
Administer invitations in /admin/users (#1185)
* Add invited users with delete button to admin users page Shows pending invitations per family below active users in /admin/users/. Each invitation row has a red Delete button aligned with the role column. Alt/option-clicking any Delete button changes all invitation button labels to "Delete All" and destroys all pending invitations for that family. - Add admin routes: DELETE /admin/invitations/:id and DELETE /admin/families/:id/invitations - Add Admin::InvitationsController with destroy and destroy_all actions - Load pending invitations grouped by family in users controller index - Render invitation rows in a dashed-border tbody below active user rows - Add admin-invitation-delete Stimulus controller for alt-click behavior - Add i18n strings for invitation UI and flash messages https://claude.ai/code/session_01F8WaH5TmtdUWwhHnVoQ6Gm * Fix destroy_all using params[:id] from member route The member route /admin/families/:id/invitations sets params[:id], not params[:family_id], so Family.find was always receiving nil. https://claude.ai/code/session_01F8WaH5TmtdUWwhHnVoQ6Gm * Fix translation key in destroy_all to match locale t(".success_all") looked up a nonexistent key; the locale defines admin.invitations.destroy_all.success, so t(".success") is correct. https://claude.ai/code/session_01F8WaH5TmtdUWwhHnVoQ6Gm * Scope bulk delete to pending invitations and allow re-inviting emails - destroy_all now uses family.invitations.pending.destroy_all so accepted and expired invitation history is preserved - Replace blanket email uniqueness validation with a custom check scoped to pending invitations only, so the same email can be invited again after an invitation is deleted or expires https://claude.ai/code/session_01F8WaH5TmtdUWwhHnVoQ6Gm * Drop unconditional unique DB index on invitations(email, family_id) The model-level uniqueness check was already scoped to pending invitations, but the blanket unique index on (email, family_id) still caused ActiveRecord::RecordNotUnique when re-inviting an email that had any historical invitation record in the same family (e.g. after an accepted invite or after an account deletion). Replace it with no DB-level unique constraint — the no_duplicate_pending_invitation_in_family model validation is the sole enforcer and correctly scopes uniqueness to pending rows only. https://claude.ai/code/session_01F8WaH5TmtdUWwhHnVoQ6Gm * Replace blanket unique index with partial unique index on pending invitations Instead of dropping the DB-level uniqueness constraint entirely, replace the unconditional unique index on (email, family_id) with a partial unique index scoped to WHERE accepted_at IS NULL. This enforces the invariant at the DB layer (no two non-accepted invitations for the same email in a family) while allowing re-invites once a prior invitation has been accepted. https://claude.ai/code/session_01F8WaH5TmtdUWwhHnVoQ6Gm * Fix migration version and make remove_index reversible - Change Migration[8.0] to Migration[7.2] to match the rest of the codebase - Pass column names to remove_index so Rails can reconstruct the old index on rollback https://claude.ai/code/session_01F8WaH5TmtdUWwhHnVoQ6Gm --------- Signed-off-by: Juan José Mata <juanjo.mata@gmail.com> Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
7fce804c89 |
Group users by family in /admin/users (#1139)
* Display user admins grouped * Start family/groups collapsed * Sort by number of transactions * Display subscription status * Fix tests * Use Stimulus |
||
|
|
06fedb34f3 |
Add new columns and sorting to admin users list (#1004)
* Add trial end date to admin users list * Add new columns * Regression |
||
|
|
4b0986220f |
Remove Flipper and replace with ENV-driven FeatureFlags (#957)
* Presence of valid DEFAULT_UI_LAYOUT is sufficient * Linter |
||
|
|
705b5a8b26 |
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` --------- Signed-off-by: Juan José Mata <juanjo.mata@gmail.com> |
||
|
|
320e087a22 |
Add support for displaying and managing legacy SSO providers (#628)
* feat: add support for displaying and managing legacy SSO providers - Introduced UI section for environment/YAML-configured SSO providers. - Added warnings and guidance on migrating legacy providers to database-backed configuration. - Enhanced localization with new keys for legacy provider management. - Updated form and toggle components for improved usability. * Expand SSO documentation: add SAML 2.0 support, JIT provisioning settings, super-admin setup steps, audit logging, and user administration details. * Update JIT provisioning docs: clarify role mapping behavior and add examples; note new `logout_idp` audit log event. --------- Co-authored-by: Josh Waldrep <joshua.waldrep5+github@gmail.com> |
||
|
|
b2ecc6bc67 |
refactor: improve SSO provider management and logging
- Simplified `name_id_format` selection logic in SSO provider form. - Switched raw database query to sanitized SQL in client secret tests. - Added condition to log JIT account creation only when identity persists. - Sanitized failure reasons in SSO login failure handling. - Added SSO provider connection test policy tests for super admin and regular users. |
||
|
|
14993d871c |
feat: comprehensive SSO/OIDC upgrade with enterprise features
Multi-provider SSO support: - Database-backed SSO provider management with admin UI - Support for OpenID Connect, Google OAuth2, GitHub, and SAML 2.0 - Flipper feature flag (db_sso_providers) for dynamic provider loading - ProviderLoader service for YAML or database configuration Admin functionality: - Admin::SsoProvidersController for CRUD operations - Admin::UsersController for super_admin role management - Pundit policies for authorization - Test connection endpoint for validating provider config User provisioning improvements: - JIT (just-in-time) account creation with configurable default role - Changed default JIT role from admin to member (security) - User attribute sync on each SSO login - Group/role mapping from IdP claims SSO identity management: - Settings::SsoIdentitiesController for users to manage connected accounts - Issuer validation for OIDC identities - Unlink protection when no password set Audit logging: - SsoAuditLog model tracking login, logout, link, unlink, JIT creation - Captures IP address, user agent, and metadata Advanced OIDC features: - Custom scopes per provider - Configurable prompt parameter (login, consent, select_account, none) - RP-initiated logout (federated logout to IdP) - id_token storage for logout SAML 2.0 support: - omniauth-saml gem integration - IdP metadata URL or manual configuration - Certificate and fingerprint validation - NameID format configuration |