Skip to content

✨ app: payments screen revamp#810

Open
dieguezguille wants to merge 20 commits intohomefrom
payments
Open

✨ app: payments screen revamp#810
dieguezguille wants to merge 20 commits intohomefrom
payments

Conversation

@dieguezguille
Copy link
Member

@dieguezguille dieguezguille commented Feb 19, 2026

Summary by CodeRabbit

  • New Features

    • Installments calculator for estimating payment options and APRs
    • New Payments screen with total outstanding, counts, detailed payment cards, and an integrated payment flow
    • In-app info sheets for early repayment discounts, fees, and statements
  • UI/UX Improvements

    • Renamed tab to "Payments" with updated icon and haptic feedback on tab press
    • Redesigned overdue and upcoming payments list with clearer dates, amounts, and processing states
    • Added direct navigation to the installments calculator
  • Refactor

    • Centralized card-mode handling for more consistent behavior
  • Documentation

    • Added English and Spanish translations for payment flows and messages

Open with Devin

Greptile Summary

This PR revamps the Payments screen with per-payment detail cards, early-repayment discount/late-fee info sheets, and a redesigned payment list with clearer dates and amounts. The core payment logic (Repay.tsx, RepayAmountSelector.tsx) is solid, and the cardModeMutationOptions refactor (server.ts) cleanly centralizes optimistic-update logic.

However, three issues require attention:

  1. React anti-pattern in RolloverIntroSheet.tsx: setState is called directly during render (lines 43–46) instead of inside useEffect, inconsistent with PaymentSheet.tsx and risky under concurrent rendering.
  2. Missing isLatestPlugin guard for returning users: The plugin-version check was moved into RolloverIntroSheet but is skipped when rolloverIntroShown=true. Returning users navigate to /roll-debt without the guard, creating a gap if /roll-debt lacks its own check.
  3. Missing Spanish translations: Aria-label keys "Payment due {{date}}, {{amount}}" and "Overdue payment {{date}}, {{amount}}" are used in code but not translated in es.json, affecting screen-reader users.

Confidence Score: 2/5

  • RolloverIntroSheet uses a React anti-pattern (setState during render) that risks correctness issues under concurrent rendering; PaymentSheet/Pay.tsx bypass isLatestPlugin validation for returning users; and critical translations are missing.
  • Three verified issues prevent safe merging: (1) RolloverIntroSheet violates React hooks rules by calling setState during render instead of in useEffect, inconsistent with PaymentSheet; (2) isLatestPlugin guard was removed for returning users, creating a potential security/correctness gap if /roll-debt lacks its own check; (3) aria-label translations for accessibility are missing from es.json. The first two carry real risk; all three should be addressed.
  • src/components/pay/RolloverIntroSheet.tsx (move setState into useEffect), src/components/pay/PaymentSheet.tsx and src/components/pay/Pay.tsx (restore isLatestPlugin check for all users), src/i18n/es.json (add missing aria-label keys)

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    TAB[Payments Tab] --> PAY[Pay.tsx]

    PAY --> EMPTY{Has payments?}
    EMPTY -- No --> EMPTYVIEW[Empty.tsx]
    EMPTY -- Yes --> HEADER[TotalOutstandingCard]

    HEADER --> FIRST[FirstMaturityCard earliest maturity]
    FIRST --> OVERDUE[OverduePayments excludeMaturity=first]
    OVERDUE --> UPCOMING[UpcomingPayments excludeMaturity=first]

    FIRST -- Pay --> REPAY[Repay.tsx]
    FIRST -- Rollover introShown=false --> ROLLINTRO[RolloverIntroSheet]
    FIRST -- Rollover introShown=true --> ROLLDEBT[roll-debt screen]

    ROLLINTRO -- isLatestPlugin true --> ROLLDEBT
    ROLLINTRO -- isLatestPlugin false --> TOAST[Upgrade toast]

    OVERDUE -- onSelect --> SHEET[PaymentSheet maturity param]
    UPCOMING -- onSelect --> SHEET

    SHEET -- Pay --> REPAY
    SHEET -- Rollover introShown=false --> PAY_ROLLINTRO[onRolloverIntro callback]
    PAY_ROLLINTRO --> ROLLINTRO
    SHEET -- Rollover introShown=true --> ROLLDEBT

    CALC[Calculator.tsx] -.->|entry from InstallmentsSheet| CALC
    PAY --> INFOSHEET[InfoSheet total/discount/fees]
Loading

Comments Outside Diff (3)

  1. src/components/pay/RolloverIntroSheet.tsx, line 43-46 (link)

    setState is called directly during render (lines 43–46) instead of inside useEffect. This is a React anti-pattern that is inconsistent with how the identical pattern is correctly handled in PaymentSheet.tsx (lines 46–51).

    Calling setState during render can cause subtle bugs under React 18's concurrent rendering — the render can be interrupted and retried, potentially triggering the state update more than once.

  2. src/components/pay/PaymentSheet.tsx, line 106-114 (link)

    isLatestPlugin guard was removed from the pre-navigation check for returning users. In the new code, isLatestPlugin is only checked inside RolloverIntroSheet (which is only shown when !rolloverIntroShown). This means returning users (where rolloverIntroShown === true) bypass the plugin guard entirely and navigate directly to /roll-debt without the version check.

    The same gap exists in Pay.tsx's onRollover handler (line 161–167). If /roll-debt does not independently check isLatestPlugin, users with outdated account plugins may encounter runtime errors instead of receiving the "Upgrade account to rollover" message.

    Consider re-adding the isLatestPlugin check to the navigateToRollover callback for all users, not just first-time users:

  3. src/components/pay/UpcomingPayments.tsx, line 115 (link)

    Accessibility aria-label key "Payment due {{date}}, {{amount}}" (used here) is missing from es.json. The same issue exists for "Overdue payment {{date}}, {{amount}}" in OverduePayments.tsx (line 120). Spanish-speaking users relying on screen readers will hear the raw English key string instead of a localized description.

    Add the following entries to es.json:

    "Payment due {{date}}, {{amount}}": "Pago por vencer {{date}}, {{amount}}",
    "Overdue payment {{date}}, {{amount}}": "Pago vencido {{date}}, {{amount}}"

Last reviewed commit: 08feec0

@dieguezguille dieguezguille self-assigned this Feb 19, 2026
@changeset-bot
Copy link

changeset-bot bot commented Feb 19, 2026

🦋 Changeset detected

Latest commit: 985b78c

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link

coderabbitai bot commented Feb 19, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Restructures payment UI: removes legacy pay-mode components, adds a new pay folder (Pay, Repay, Calculator, Overdue/UpcomingPayments, PaymentSheet updates), centralizes card-mode mutation options, updates navigation (tab icons/titles and haptics), adds i18n keys, and updates Maestro subflows and changesets.

Changes

Cohort / File(s) Summary
Changesets
/.changeset/cold-poems-end.md, /.changeset/green-weeks-care.md, /.changeset/jolly-radios-poke.md, /.changeset/purple-lizards-run.md, /.changeset/shiny-badgers-sleep.md
Added five patch changesets for @exactly/mobile (tab bar UI, new pay screen, card mode mutation refactor, installments calculator, overdue payment styling).
Navigation / Layout
src/app/(main)/(home)/_layout.tsx
Tab icon/title updates (Coins→CalendarCheck, FileText→History; "Pay Mode"→"Payments"), added selectionAsync haptic on tab press with error reporting, adjusted tab label emphasis and caption2 styling.
Route re-exports
src/app/(main)/(home)/pay-mode.tsx, src/app/(main)/calculator.tsx, src/app/(main)/pay/index.tsx
Repointed route exports: pay-mode → new Pay, added calculator export, changed pay index to re-export Repay.
Removed pay-mode components
src/components/pay-mode/PayMode.tsx, src/components/pay-mode/PaySelector.tsx, src/components/pay-mode/OverduePayments.tsx, src/components/pay-mode/PaymentsActions.tsx
Deleted legacy pay-mode screen and related selector/actions/overdue components; functionality migrated to new pay/* modules.
New pay components
src/components/pay/Pay.tsx, src/components/pay/Repay.tsx, src/components/pay/Calculator.tsx, src/components/pay/OverduePayments.tsx, src/components/pay/UpcomingPayments.tsx, src/components/pay/Empty.tsx
Added Pay (aggregates maturities, total/first-maturity cards), Repay rename, Calculator (installment rates, BEST APR), rebuilt Overdue/UpcomingPayments with maturity aggregation and processing state, adjusted Empty state layout/text.
PaymentSheet & InfoSheet
src/components/pay/PaymentSheet.tsx, src/components/shared/InfoSheet.tsx
Refactored PaymentSheet to add Frame/NotAvailableView/RolloverIntroView/DetailsView (signature changed to accept onInfoPress), integrated InfoSheet modal component.
Home integration
src/components/home/Home.tsx, src/components/home/InstallmentsSheet.tsx
Home: switched to cardModeMutationOptions via useMutation, added handleModeChange with error reporting. InstallmentsSheet: added router navigation to /calculator.
Server mutation defaults
src/utils/server.ts
Made setCardMode internal, exported cardModeMutationOptions, registered mutation defaults with optimistic update, onError rollback, onSettled invalidation and settings update.
i18n
src/i18n/en.json, src/i18n/es.json
Added English plural keys for "in {{count}} payments" and added/updated many Spanish translations for calculator, payments, discounts, fees, due labels, and empty state text.
Maestro subflows
.maestro/subflows/activateCard.yaml, .maestro/subflows/repay.yaml, .maestro/subflows/rollover.yaml
Expanded activateCard flow to multi-step installments path; sanitized and repositioned debt parsing/taps in repay flow; simplified rollover flow to rely on copiedText-based taps and assertions.
Minor imports
src/components/loans/Loans.tsx
Updated imports to use new pay/* components instead of pay-mode/*.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Home
    participant Calculator
    participant PaymentSheet
    participant Server

    User->>Home: Open Payments screen
    Home->>Server: Fetch market data & maturities
    Server-->>Home: Market data + maturities
    Home->>Home: Aggregate & sort maturities
    User->>Home: Tap "Installments calculator"
    Home->>Calculator: Navigate to /calculator
    User->>Calculator: Enter amount
    Calculator->>Server: Fetch installment rates
    Server-->>Calculator: Rates
    Calculator-->>User: Show installment options (BEST APR)
    User->>Home: Tap payment item
    Home->>PaymentSheet: Open details
    PaymentSheet->>Server: Submit repay/rollover mutation
    Server-->>PaymentSheet: Updated card details
    PaymentSheet->>Home: Refresh & close
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • franm91
  • cruzdanilo
  • itofarina
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title '✨ app: payments screen revamp' directly and clearly summarizes the main objective of the pull request, which is a comprehensive redesign of the payments screen component.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch payments
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link

Summary of Changes

Hello @dieguezguille, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a comprehensive overhaul of the application's payment management features. It focuses on enhancing the user experience by providing a more intuitive and feature-rich payments screen, including a dedicated calculator for installment planning. The changes also streamline underlying logic for card mode management and refresh the application's navigation aesthetics.

Highlights

  • Payments Screen Revamp: The entire payments screen has been revamped, introducing a new UI and improved functionality for managing card payments and installments.
  • Installments Calculator Added: A new installments calculator feature has been implemented, allowing users to estimate costs for purchases.
  • Tab Bar UI Update: The tab bar user interface has been updated with new icons and labels, changing 'Pay Mode' to 'Payments' and adding haptic feedback on tab presses.
  • Unified Card Mode Mutation: The logic for handling card mode mutations has been unified and centralized, improving consistency and maintainability.
  • Restyled Overdue and Upcoming Payments: The display and styling of overdue and upcoming payments have been restyled for better clarity and user experience.
Changelog
  • .changeset/cold-poems-end.md
    • Updated tab bar UI
  • .changeset/green-weeks-care.md
    • Implemented new pay screen
  • .changeset/jolly-radios-poke.md
    • Unified card mode mutation
  • .changeset/purple-lizards-run.md
    • Added installments calculator
  • .changeset/shiny-badgers-sleep.md
    • Restyled overdue and upcoming payments
  • .maestro/subflows/activateCard.yaml
    • Updated Maestro flow to include tests for installment calculator and info sheets
  • src/app/(main)/(home)/_layout.tsx
    • Updated tab bar icons, labels, and added haptic feedback
  • src/app/(main)/(home)/pay-mode.tsx
    • Updated the default export path for the pay mode screen
  • src/app/(main)/calculator.tsx
    • Added a new route for the installments calculator
  • src/app/(main)/pay/index.tsx
    • Updated the default export path for the pay index screen
  • src/components/home/Home.tsx
    • Updated payment component imports and refactored card mode mutation handling
  • src/components/home/InstallmentsSheet.tsx
    • Integrated router for calculator navigation and updated installment rate data handling
  • src/components/loans/Loans.tsx
    • Updated payment component imports
  • src/components/pay-mode/AssetSelectionSheet.tsx
    • Renamed to src/components/pay/AssetSelectionSheet.tsx
  • src/components/pay-mode/Empty.tsx
    • Renamed to src/components/pay/Empty.tsx and updated content
  • src/components/pay-mode/ManualRepaymentSheet.tsx
    • Renamed to src/components/pay/ManualRepaymentSheet.tsx
  • src/components/pay-mode/OverduePayments.tsx
    • Removed the overdue payments component
  • src/components/pay-mode/Pay.tsx
    • Renamed to src/components/pay/Repay.tsx
  • src/components/pay-mode/PayMode.tsx
    • Removed the main pay mode component
  • src/components/pay-mode/PaySelector.tsx
    • Removed the pay selector component
  • src/components/pay-mode/PaymentSheet.tsx
    • Renamed to src/components/pay/PaymentSheet.tsx and refactored its internal views
  • src/components/pay-mode/PaymentsActions.tsx
    • Removed the payments actions component
  • src/components/pay-mode/RepayAmountSelector.tsx
    • Renamed to src/components/pay/RepayAmountSelector.tsx
  • src/components/pay-mode/UpcomingPayments.tsx
    • Renamed to src/components/pay/UpcomingPayments.tsx and updated its display logic
  • src/components/pay/Calculator.tsx
    • Added a new component for calculating installment costs
  • src/components/pay/OverduePayments.tsx
    • Added a new component to display overdue payments with enhanced UI
  • src/components/pay/Pay.tsx
    • Added a new main payments screen component
  • src/components/shared/InfoSheet.tsx
    • Added a new reusable info sheet component
  • src/i18n/en.json
    • Added new payment-related translation keys
  • src/i18n/es.json
    • Added and updated payment-related translation keys
  • src/utils/server.ts
    • Refactored card mode mutation logic into a reusable option object
Activity
  • New changeset files were added, indicating planned releases for UI updates, new features (pay screen, installments calculator), and code unification.
  • Maestro tests were updated to cover the new installments calculator and various info sheets related to spending limits and credit limits.
  • The tab bar navigation was updated, including new icons and haptic feedback on tab presses.
  • Payment-related components were reorganized into a new 'src/components/pay' directory for better modularity.
  • The 'setCardMode' mutation logic was centralized in 'src/utils/server.ts' for unified handling.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

gemini-code-assist[bot]

This comment was marked as resolved.

@sentry
Copy link

sentry bot commented Feb 19, 2026

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
602 1 601 1
View the top 1 failed test(s) by shortest run time
local::local
Stack Traces | 415s run time
No visible element found: "Credit limit info"

To view more test analytics, go to the Prevent Tests Dashboard

coderabbitai[bot]

This comment was marked as resolved.

@cruzdanilo cruzdanilo changed the title ✨ payments screen revamp ✨ app: payments screen revamp Feb 19, 2026
@dieguezguille dieguezguille force-pushed the home branch 4 times, most recently from 84b9ea9 to 943fa36 Compare February 20, 2026 21:54
@dieguezguille dieguezguille force-pushed the home branch 2 times, most recently from 6e34c64 to 89412ce Compare February 23, 2026 12:50
coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

sentry[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

sentry[bot]

This comment was marked as resolved.

sentry[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 2 new potential issues.

View 4 additional findings in Devin Review.

Open in Devin Review

Comment on lines +43 to +46
if (maturity && !open) {
setDisplayMaturity(maturity);
setOpen(true);
}

Choose a reason for hiding this comment

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

🚩 RolloverIntroSheet uses setState-during-render instead of useEffect

At src/components/pay/RolloverIntroSheet.tsx:43-46, the component calls setDisplayMaturity and setOpen directly during the render phase. This is an officially supported React pattern (similar to getDerivedStateFromProps), and it works correctly — the condition maturity && !open prevents infinite loops. However, the sibling component src/components/pay/PaymentSheet.tsx:60-65 handles the identical scenario using useEffect. This inconsistency may confuse future maintainers. The render-phase pattern is also slightly more fragile: if any code path sets open = false without simultaneously clearing the parent's maturity prop, the sheet would immediately reopen on the next render.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +106 to +107
const hasPayments = allMaturities.length > 0;
const firstMaturity = allMaturities[0];

Choose a reason for hiding this comment

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

🚩 Empty state shown during initial data load on Pay screen

In src/components/pay/Pay.tsx:106-107, hasPayments is false when allMaturities is empty, which includes the initial loading state (before markets is fetched). This means the Empty component (src/components/pay/Empty.tsx) is displayed briefly while the previewer data loads, before payments appear. The RefreshControl spinner overlays this. Consider gating on markets being defined to distinguish 'loading' from 'no payments' — e.g., rendering a skeleton or nothing while loading.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants