mirror of
https://github.com/we-promise/sure.git
synced 2026-07-12 04:45:19 +00:00
* Add russian localization * Update localization files from upstream * Add russian to supported languages * Add CHANGELOG.ru.md * Changes in CHANGELOG.ru.md * Fix some errors after PR * Remove duplicated 'merge_duplicate' key in the same mapping in locales/views/transactions/ru.yml * Fixed a translation error into Russian about creating a new account by invitation. * Clarification of financial terms in Russian translation * Add russian localization * Update localization files from upstream * Add russian to supported languages * Add CHANGELOG.ru.md * Changes in CHANGELOG.ru.md * Fix some errors after PR * Remove duplicated 'merge_duplicate' key in the same mapping in locales/views/transactions/ru.yml * Fixed a translation error into Russian about creating a new account by invitation. * Clarification of financial terms in Russian translation * Improve Russian i18n translations * Improve Russian i18n translations * Improve Russian i18n translations * Improve Russian i18n translations * Improve Russian i18n translations * Improve tests for Russian i18n translations * Improve Russian i18n translations * Improve Russian i18n translations * Minimalistic SECURITY.md Signed-off-by: Juan José Mata <juanjo.mata@gmail.com> * ci(preview): render Cloudflare config from trusted template (#2207) * Fix SSO provider settings updates (#2210) * fix(goals): UI polish — submit validation, container-responsive cards, picker & filter fixes (#2160) * fix(ds): disabled buttons use not-allowed cursor Tailwind v4 preflight sets cursor:pointer on every <button>, including disabled ones. Add disabled:cursor-not-allowed to the DS button base so disabled buttons read as non-interactive. * fix(goals): disable new-goal submit until required fields valid The submit button was always enabled, and the funding-accounts checkbox group has no native 'required', so a goal with no linked account could be submitted and only fail server-side (422). Disable the submit until name, a positive target amount, and >=1 account are present, and add a submit-time guard that surfaces inline errors and focuses the first offending field as a backstop. * fix(goals): keep color/icon picker from shifting the form DS::Disclosure wraps its body in an mt-2 div that sits in normal flow, so opening the absolutely-positioned picker popover nudged the form down ~8px. Use a raw <details> for the picker so the popover overlays without reflowing the content beneath it. * fix(goals): container-responsive cards, meta-line status, scrollable tabs - Card grids + KPI strip switch from viewport breakpoints to container queries (@container), so columns track the actual content width when the account/AI sidebars are open instead of crushing three columns into a narrow center. - Move the status pill from the title row to the meta line; the title now gets the full header width (no more 'Hou...' truncation at 3-up). Drop the secondary line when the pill already states it (completed / open). - Card internals harden for narrow widths (ring shrink-0, footer wrap, shorter 'N days left' secondary). - Filter tabs scroll horizontally instead of wrapping 'On track' mid-label or forcing the row wider than the viewport. * fix(goals): only require a funding account on the create form GoalsController#edit renders account checkboxes from visible accounts only, so a goal backed solely by a now-hidden (disabled / pending_deletion) account renders none checked. The submit-validation then wedged the edit form — a name/notes change couldn't be saved even though #update preserves existing links when account_ids is omitted. Gate the account requirement on a require-account Stimulus value that is true only for the create form. * fix(goals): give the color/icon picker trigger an accessible name The hand-rolled <summary> is icon-only (pen), so screen readers announced an unlabeled control. Add a localized aria-label. * fix(goals): render form validation errors inline on server re-render The client-side controller already surfaces name / amount / account errors inline and disables submit, but a server 422 (JS disabled, or a race that lets an invalid submit through) fell back to the top run-on banner ("X must be filled and Y and Z."). Make the existing inline error hints server-aware so a failed submit shows the same per-field red text the client path does, and drop the base-error banner — base only ever carries the account requirement, which now renders beside the funding-accounts list. * fix(goals): blur projection chart under privacy mode The projection chart's SVG axis labels and annotations (target, "$X short", $200K/$400K ticks) rendered in cleartext while privacy mode blurred every other number on the page. Tag the chart container `privacy-sensitive`, the same wrapper-level treatment the net-worth and cashflow-sankey charts already use, so it blurs with the rest. * fix(goals): stop target-date input stretching when amount error shows The target-amount / target-date row is a two-column grid. The amount column carries its inline error <p> underneath, so when that error appears the column grows and the default `items-stretch` makes the sibling date input stretch to match — the date box visibly grew taller than the amount box. Anchor the row with `items-start` so each input keeps its natural height and the error just extends below its own column. * fix(goals): make invalid submit announce errors instead of dead-ending Review follow-ups: - Swap the submit gate from the disabled attribute to aria-disabled: a truly disabled default submit also blocks Enter-key implicit submission, so an invalid form was a dead button with every inline error still hidden. The button now stays clickable and lets validateOnSubmit surface the errors; DS buttonish styles the aria-disabled state (cursor-not-allowed + opacity-50). Side effect: loading_button_controller submits also dim while busy, which reads as an upgrade. - Focus the first funding-account checkbox when accounts are the only missing field (focus previously went nowhere). - Drop the now-orphaned goals.goal_card.days_left_by locale key. * fix(ds): canonical transaction-row — stop category pills truncating (#2147) * fix(ds): canonical transaction-row — stop category pills truncating (#2137) The desktop transaction grid gave the name column col-span-8 (67%, usually half-empty) and the category column only col-span-2 (17%), so the category pill's name area was clamped to ~44px and ellipsized nearly every value ("Misc...", "Shop...", "Rest...") despite the empty space the audit flagged. - Rebalance the lg:grid-cols-12 row: name col-span-8 -> 7, category 2 -> 3 (amount unchanged; still 12). Category gains 50% width from the over-wide name column. - categories/_badge: the pill was `flex w-full` (stretched to fill the column); make it `inline-flex max-w-full` so it hugs its content and short names render fully, capping + truncating only when genuinely too long. Verified on /transactions: visible-row category truncation dropped 7/7 -> 2/7 even in the compressed (AI-panel-open) view; Payment / Shopping / Restaurants now render in full. Fixes both the interactive categories/menu and the static transfer categories/badge (menu reuses the badge partial). * feat(ds): lift categories/_badge onto DS::Pill Addresses the Drift Patrol finding (and jjmata's request) properly instead of patching the bespoke span: the badge becomes a DS::Pill in badge mode. The pill primitive grows the three capabilities the badge needed and the bot's one-liner glossed over: - truncate: pills that may shrink inside min-w-0 columns drop their shrink-0/whitespace-nowrap and ellipsize the label instead of overflowing (the transaction-row category cell this PR fixes). - label_testid: stamps data-testid on the label span; five test files target [data-testid='category-name']. - icon_size: passthrough to the icon helper (badge keeps its established sm glyph; default stays xs). custom_color was already the sanctioned escape hatch for user-chosen hues. Owned visual deltas from pill standardization: px-1.5/py-1 -> px-2/py-0.5, gap-1 -> gap-1.5, border mix 10% -> 20%; bg mix, hex text, radius, text scale, icon size, truncation and testid are parity. Label wrapper only renders when truncate/label_testid ask for it, so existing pill DOM (and the find("span", text:) assertions on it) is unchanged. Four new pill tests + a Lookbook case cover the recipe. * feat(merchants): add raw data import (csv) for merchants (#1992) * feat(merchants): add csv import endpoint for merchants * docs: update endpoint docs * fix(merchant): recommended ai fixes * chore(ci): finish Node 24 GitHub Actions migration (#2221) * chore(ci): finish Node 24 GitHub Actions migration * chore(ci): update preview security check for github-script v8 * fix(mobile): redact sensitive diagnostic logs (#2199) * fix(mobile): redact sensitive diagnostic logs * fix(mobile): tighten diagnostic log redaction * fix(mobile): harden sanitized diagnostics * fix(mobile): clarify offline fallback diagnostics * fix(mobile): refine diagnostic log redaction * fix(mobile): tighten diagnostic log redaction * fix(mobile): harden auth diagnostic failures * feat(ds): DS::SegmentedControl — fix invisible dark selected pill (#2145) * feat(ds): DS::SegmentedControl; fix invisible dark selected pill (#2137) Ships DS::SegmentedControl — a single-select pill group (filters, mode switches) — plus a Lookbook preview, tests, and an exemplar migration of the budget filter tabs. The audit flagged the dark selected pill as invisible: the bespoke controls paired a container-inset track (gray-800) with a gray-700 active pill, barely a step apart. The primitive uses the DS tab-token values (tab-bg-group -> a near-black alpha-black-700 track in dark), against which the gray-700 active pill clearly reads. Values are inlined with @variant theme-dark because @apply-ing the custom tab utilities drops their dark override. - app/components/DS/segmented_control.rb: slot-based with_segment(label, active:, href:, **opts); link or button; full_width: for equal footprint; selected style isolated in .segmented-control__segment--active so a controller can toggle it as one class. - .segmented-control recipe in components.css. - Migrate budgets/_budget_tabs; budget_filter_controller now toggles the single --active class instead of five raw utility classes. Verified in-browser: dark active pill reads (gray-700 on near-black track); filter toggle still works. Tests + rubocop clean. Deferred (follow-up): auth sign-in/up switch, transaction-type tabs, and other bespoke segmented controls — same primitive, one migration each. * fix(a11y): expose segmented-control selection + derive active from filter param - DS::SegmentedControl: set aria-current (links) / aria-pressed (buttons) from the segment's active state so screen readers announce the selection. - budget_filter_controller: mirror aria-pressed when it toggles the active class. - _budget_tabs: compute each segment's initial active: from params[:filter] so a ?filter=over_budget request server-renders the correct pill (no flash before Stimulus runs). Addresses CodeRabbit reviews on #2145. * fix(ds): close unterminated segmented-control CSS rule The --active rule swallowed the table-scroll block's comment opener and never closed, so the Tailwind build died with 'Missing closing } at @layer components' and both test jobs failed at boot. Restore the closing brace and the /* opener. Also add the budgets.show.filter.aria_label locale key the budget tabs view referenced only through its inline default. --------- Signed-off-by: Juan José Mata <juanjo.mata@gmail.com> Co-authored-by: Juan José Mata <juanjo.mata@gmail.com> * fix(ds): one height rail for icon and text buttons (#2202) * fix(ds): put icon buttons on the text-button height rail Icon-only DS::Button containers were 32/44/48px squares while text buttons of the same nominal size render ~28/36/48px tall, so every mixed header row (icon menu trigger next to text buttons — the transactions index, account pages, the app header) sat misaligned. - buttonish SIZES: icon containers now share the text rail (sm w-7, md w-9, lg w-12 unchanged). - DS::Popover's hand-rolled w-11 trigger joins the rail at w-9. - The layout's hand-rolled privacy toggle (mobile + desktop) matches the md icon-button chrome: w-9, rounded-lg, container-inset hover — it sat at w-8 with a different hover next to a DS icon button. - DS::Select's panel adopts shadow-border-lg, the elevation Menu and Popover already use, replacing the weaker shadow-lg+border-xs combo. Measured on the transactions header after the change: 36/38/36px. * fix(ds): keep the 44px touch target on coarse pointers The height rail trades icon-button size for row alignment, which is a pointer-precision tradeoff: WCAG 2.5.5's 44x44 minimum is about fingers, not mice. sm/md icon containers (and the two off-rail consumers: the popover trigger and the layout privacy toggles) gain pointer-coarse:w-11/h-11, so touch devices keep the full target while fine-pointer layouts get the aligned 36px row. Measured via Playwright: desktop 36x36, iPhone emulation 44x44 on both the menu trigger and the privacy toggle. * fix(sync-toast): morph refresh, defer behind modals, DS conformance (#2105) * fix(sync-toast): morph refresh, defer behind modals, DS conformance Follow-up to #1964 (addresses #2071). - Refresh via Turbo morph visit instead of window.location.reload, so scroll position and data-turbo-permanent elements (the AI chat panel) survive and there is no white flash. - Defer the toast while a <dialog> is open and reveal it on close. A refresh mid-modal closes the dialog and discards its in-progress input, which is the exact data loss this toast exists to prevent. Handles stacked modals. - Refresh CTA and close button now use DS::Button (secondary / icon). The close is always visible, inside the card, focusable, and has an aria-label; the old hover-only corner chip was unreachable on touch and not keyboard-focusable. - Add role="status" / aria-live="polite" to the toast. - Fix icon color: "inverse" is not a key in the icon helper color map, so it silently rendered no color class (dark icon on bg-info). Use "white", which maps to the functional text-inverse token. - Tighten copy: "New data available" / "Refresh". - Sync the broadcast comment with the actual replace/morph behavior. * fix(sync-toast): detach deferred dialog listener on disconnect A toast replaced by a newer broadcast_replace_to while a <dialog> was open kept its 'close' listener attached, so the detached controller fired #reveal()/#arm() when the dialog closed — a spurious auto-refresh from a stale toast (and repeated syncs could queue several). Store the dialog + handler refs and remove the listener in disconnect(). Flagged by codex + coderabbit on #2105. * fix(sync-toast): re-check interaction and dialogs at refresh-fire time The interaction check ran once at arm time but the refresh fired two seconds later. The post-dialog reveal made that window matter: the user closes a dialog sitting on a form, resumes typing, and the timer morphs the page — wiping non-turbo-permanent input, the exact data-loss class this toast exists to prevent. A dialog opened during the window had the mirror problem (the refresh would close it). Bail inside the callback instead, leaving the toast visible for a manual refresh, matching the mid-form behavior. Also documents the dialog-removed-without-close edge on the deferred listener. * perf(transactions): preload new form options (#2189) * perf(transactions): preload new form options * refactor(transactions): reuse new form account scope * perf(reports): collapse investment flow aggregates (#2190) * perf(reports): collapse investment flow aggregates * fix(reports): avoid shared-account flow duplication * fix: Savings Goal Marked Reached While Still Short (#2180) * fix: saving goal is marke while several states * feat(enable_banking): support MFA/decoupled banks and harden session handling (#2174) Decoupled/MFA banks (e.g. VR Bank in Holstein) were hard-blocked because the authorize flow aborted whenever auth_methods[0] was DECOUPLED. Enable Banking's hosted /auth page actually coordinates decoupled SCA and redirects back with a code, so route these banks through it instead: - Provider#start_authorization accepts and forwards an auth_method param - EnableBankingItem#select_auth_method picks the best method (REDIRECT > DECOUPLED > EMBEDDED), filtering by psu_type and skipping hidden methods - Shared begin_authorization! re-fetches ASPSP metadata on each authorize and reauthorize, so the method is always re-derived (no persistence required) - Remove the DECOUPLED block in the controller Also stop the integration from constantly reporting "session expired": - Only a session-level GET /sessions 401/404 flips the connection to requires_update; per-account 401/404 are retried and no longer kill the whole connection - Reconcile session_expires_at from the API's access.valid_until on every sync - Treat an expired session as a graceful requires_update state instead of raising a bare error No schema changes. Adds covering tests. * fix: request change reach --------- Co-authored-by: Tobias Rahloff <rahloff@gmail.com> * Improve Russian i18n translations * Improve Russian i18n translations * Improve Russian i18n translations * Improve Russian i18n translations * No need for language-specific CHANGELOG.md file --------- Signed-off-by: Juan José Mata <juanjo.mata@gmail.com> Co-authored-by: tea <john.fx@gmail.com> Co-authored-by: Juan José Mata <juanjo.mata@gmail.com> Co-authored-by: ghost <49853598+JSONbored@users.noreply.github.com> Co-authored-by: Guillem Arias Fauste <accounts@gariasf.com> Co-authored-by: Blaž Dular <22869613+xBlaz3kx@users.noreply.github.com> Co-authored-by: Sure Admin (bot) <sure-admin@splashblot.com> Co-authored-by: Jonathan Chang <55106972+jonathanchang31@users.noreply.github.com> Co-authored-by: Tobias Rahloff <rahloff@gmail.com> Co-authored-by: Juan José Mata <jjmata@jjmata.com>