Skip to content

chore: back-merge main → dev (pre Sprint 148 release)#2273

Merged
Hugo0 merged 15 commits into
devfrom
chore/backmerge-main-into-dev-20260623
Jun 23, 2026
Merged

chore: back-merge main → dev (pre Sprint 148 release)#2273
Hugo0 merged 15 commits into
devfrom
chore/backmerge-main-into-dev-20260623

Conversation

@Hugo0

@Hugo0 Hugo0 commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Back-merge maindev

Brings the 14 hotfix commits that landed directly on main since the last release back into dev, so the upcoming Prod Release Sprint 148 PR (dev → main, #2245) is a clean superset of main with no surprise reverts/conflicts.

Merge was conflict-free.

Hotfixes pulled back from main

  • fix(claim) show the backend's bank-account error verbatim
  • fix(security) pass localStorage key as a console arg, not a format string
  • fix(claim) preserve send-link password across the auth/KYC redirect (+ test stub)
  • feat(add-money) flag BRL-via-PIX onramp as under maintenance (warn-only)
  • fix(card) hasActiveCard tri-state + region-picker redirect fixes (3 commits)
  • feat(card-details) render Rain dispute status in the receipt drawer (+ CR follow-up)

Why a PR (not a direct push)

Matches the established back-merge pattern (cf. #2256) and lets CI/Vercel-preview validate before it lands on the shared dev branch that staging tracks.

Automated as part of the Sprint 148 release prep.

jjramirezn and others added 15 commits June 19, 2026 05:55
Companion to peanut-api-ts#1037 (Rain dispute webhook handling).

Plumb extraData.dispute through the history transformer into the
TransactionDetails.cardPayment block, render a "Disputed — <label>" row
under the existing decline / cancellation / pending notes, and append an
"Evidence requested" row when Rain has prompted the user for docs.

Pill override: while the dispute is pending or inReview, flip the status
pill to pending — a settled spend that's actively contested isn't really
COMPLETED from the user's POV. Terminal dispute states (accepted /
resolvedByMerchant / rejected / canceled) restore the underlying status.
Suppress the existing "Authorized, awaiting settlement or reversal"
sub-row when an active dispute is the reason for the pending pill — the
dispute row IS the status story there.
feat(card-details): render Rain dispute status in receipt drawer
Card-eligible users (skip badge or admin grant — both collapse into
cardInfo.hasCardAccess on the BE) were funneled to the region picker first:
the home activation funnel only surfaced the card step after KYC + funding,
and the carousel KYC prompt always pointed at /profile/identity-verification.

For EU/NA users that picker maps the region intent to bridge-requirements and
auto-enrolls Bridge bank rails, whose DB checks fail on many EU addresses —
surfacing as a "blocked by Bridge / proof of address" detour for people who
only ever wanted a card.

Surface the card step first whenever the user is eligible and cardless,
routing them to /card (KYC runs on rain-requirements — no regionIntent, no
Bridge rails), and stop showing the region-picker carousel CTA to eligible
users.
Extends the card-priority behavior to the region picker. When a user has
card access (skip badge / admin grant -> cardInfo.hasCardAccess) but no
active card, handleStartKyc now sends them to /card — whose KYC runs on the
rain-requirements Sumsub level (no regionIntent, no Bridge/Manteca rail
enrollment) — instead of starting region KYC, regardless of which region
they selected. Card-holders fall through and can still unlock bank regions.
Address CodeRabbit (Major): while useRainCardOverview is still loading,
treating `overview` as "no active card" could bounce a card-holder to /card
before it resolves. Make hasActiveCard tri-state (undefined while loading)
and only redirect on an explicit `false`.
fix(card): prioritize card CTA for eligible users over region-picker KYC
…nly)

PIX deposits via Manteca (Brazil) are currently unstable/intermittent. We want
users to still see and use the option, but to know it may be delayed or fail —
so this surfaces a warning without removing the path.

- new config lever underMaintenanceConfig.pixBrazilOnrampMaintenance (warn-only,
  Brazil-scoped — does not touch the Argentina/ARS Manteca onramp or QR pay,
  which the existing coarse disabledPaymentProviders:['MANTECA'] lever would)
- "Maintenance" tag on the Pix option in /add-money/brazil (stays clickable)
- warning banner inside the deposit flow (/add-money/brazil/manteca)

Flip the flag to false when PIX deposits are stable again. Frontend-only: the
API still accepts PIX deposits by design (warn-only).
…ce-warning

feat(add-money): flag BRL-via-PIX onramp as under maintenance (warn-only)
A new recipient who opens a send link, registers/KYCs, and is redirected
back to /claim hit a 404 'Send link not found' and could not claim
(TASK-20193). The claim pubKey is derived from the link's #p= password
fragment; the guest -> /setup -> KYC -> /claim flow fully remounts the page
and the fragment comes back mangled (the c/v/i query survives), so the
remounted page derives the wrong pubKey and 404s.

Proven in prod: the failing GET used a pubKey absent from the DB while the
real link at the same deposit slot (c=42161,v=v4.4,i=181) exists and is
completed.

Fix: resolveClaimLink keys the pristine link by its deposit slot (which the
redirect preserves) in localStorage on first open; first write wins, so the
correct password stays authoritative even if a later remount arrives with a
broken fragment. Slot-local + client-only, so nothing new is exposed.
claim-states mounts <Claim/>, which now calls resolveClaimLink; the module
mock must expose it or the suite throws 'resolveClaimLink is not a function'.
…tring

CodeQL flagged externally-controlled-format-string (high): resolveClaimLink
builds the localStorage key from user-controlled URL params, which then reached
saveToLocalStorage's console.log as the format string. Pass key as a trailing
arg so a %s/%d in a link can't be interpreted as a format specifier.
When adding a bank account for a send-link payout fails, the backend now
returns a curated, user-facing message (e.g. 'we couldn't verify your bank's
billing address — check city/state/ZIP'). Routing it through ErrorHandler
collapsed it into the generic 'contact support' fallback, hiding the
actionable detail. Surface response.error verbatim for this path; keep FE
Sentry capture. (TASK-20194, companion to peanut-api-ts external-account fix)
fix(claim): surface backend bank-account error verbatim [hotfix]
@vercel

vercel Bot commented Jun 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
peanut-wallet Ready Ready Preview, Comment Jun 23, 2026 8:11pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR bundles four independent changes: a warn-only Brazil PIX onramp maintenance banner (flag, countries-list badge, in-flow InfoCard); a card-activation funnel priority that unconditionally redirects card-eligible users without an active card to /card; a resolveClaimLink utility that preserves claim-link password fragments across multi-hop redirects; and Rain dispute lifecycle display in the card-payment transaction drawer.

Changes

PIX Brazil Onramp Maintenance Banner

Layer / File(s) Summary
Maintenance config flag and copy
src/config/underMaintenance.config.ts
Adds pixBrazilOnrampMaintenance: true to MaintenanceConfig and exports PIX_BRAZIL_ONRAMP_MAINTENANCE badge/title/description copy object.
Countries list maintenance badge
src/components/AddWithdraw/AddWithdrawCountriesList.tsx, src/components/AddWithdraw/__tests__/AddWithdrawCountriesList.test.tsx
Computes isPixOnrampUnderMaintenance in renderPaymentMethods and renders a pending StatusBadge on the pix-add card while keeping it clickable; tests assert tag presence/absence and navigation.
InputAmountStep maintenance banner
src/components/AddMoney/components/InputAmountStep.tsx, src/components/AddMoney/components/MantecaAddMoney.tsx, src/app/(mobile-ui)/add-money/__tests__/add-money-states.test.tsx
Adds optional maintenanceBanner: React.ReactNode prop to InputAmountStep; MantecaAddMoney passes a warning InfoCard when country is BR and flag is set; test verifies banner renders and Continue is not disabled.

Card Activation Funnel Priority

Layer / File(s) Summary
useActivationStatus card-step override
src/hooks/useActivationStatus.ts
Removes the activationStep === 'outbound' gate so eligible card users without an active card see the card step regardless of funnel milestone.
Carousel KYC prompt card-access gate
src/hooks/useHomeCarouselCTAs.tsx
Adds hasCardAccessGranted === false guard to suppress the kyc-prompt CTA for card-eligible users.
UnlockedRegions redirect to /card
src/components/Profile/views/UnlockedRegions.view.tsx
Reads hasCardAccess and findActiveCard(overview), and redirects users with card access but no active card to /card on KYC initiation.

Claim Link Fragment Preservation

Layer / File(s) Summary
resolveClaimLink utility
src/services/sendLinks.ts, src/utils/general.utils.ts, src/services/__tests__/resolveClaimLink.test.ts
Exports resolveClaimLink with first-write-wins localStorage keyed on deposit slot to restore mangled #p= fragments; fixes saveToLocalStorage console format for CodeQL; tests cover pristine restoration, first-write semantics, and pass-through cases.
Claim.tsx and BankFlowManager integration
src/components/Claim/Claim.tsx, src/components/Claim/Link/views/BankFlowManager.view.tsx, src/components/Claim/__tests__/claim-states.test.tsx
Applies resolveClaimLink on mount and on receipt close in Claim.tsx; returns backend error messages verbatim in BankFlowManager guest-claim path instead of wrapping in a generic Error; adds resolveClaimLink stub to test mock.

Rain Card Dispute Lifecycle Display

Layer / File(s) Summary
DisputeStatus type, HistoryEntryExtraData, and view model
src/utils/history.utils.ts, src/components/TransactionDetails/transactionTransformer.ts
Exports DisputeStatus union, DISPUTE_STATUSES set, and isDisputeStatus guard; extends HistoryEntryExtraData with optional dispute; adds typed dispute block to TransactionDetails.cardPayment.
Transformer mapping and uiStatus override
src/components/TransactionDetails/transactionTransformer.ts
Overrides uiStatus to pending for active disputes (pending/inReview); populates cardPayment.dispute with runtime-validated fields or null.
CardPaymentRows dispute rendering
src/components/TransactionDetails/provider-rows/CardPaymentRows.tsx, src/components/TransactionDetails/provider-rows/__tests__/dispute-label.test.ts
Exports disputeStatusLabel mapping all DisputeStatus values to "Disputed — …" strings; extends content guard for dispute presence; renders dispute status and evidence-request rows; suppresses pending-auth row during active disputes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • peanutprotocol/peanut-ui#2262: Implements the same card-eligibility routing changes — useActivationStatus card-step priority, useHomeCarouselCTAs KYC-prompt suppression, and UnlockedRegions redirect to /card.
  • peanutprotocol/peanut-ui#2270: Introduces the same resolveClaimLink integration in Claim.tsx and the matching sendLinks mock update in claim tests.
  • peanutprotocol/peanut-ui#2268: Adds the same maintenanceBanner prop to InputAmountStep and the BRL-via-PIX warn-only maintenance flow in MantecaAddMoney.

Suggested labels

enhancement

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: a back-merge of hotfix commits from main into dev for Sprint 148 release preparation.
Description check ✅ Passed The description is directly related to the changeset, detailing the 14 hotfix commits being back-merged and their specific purposes and impacts.
Docstring Coverage ✅ Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

@github-actions

Copy link
Copy Markdown
Contributor

Code-analysis diff

Painscore total: 5807.46 → 5811.68 (+4.22)
Findings: +2 net (+100 new, -98 resolved)

🆕 New findings (100)

  • critical complexity — src/components/AddWithdraw/AddWithdrawCountriesList.tsx — CC 118, MI 56.72, SLOC 333
  • critical complexity — src/components/Claim/Claim.tsx — CC 100, MI 53.93, SLOC 389
  • critical complexity — src/components/Claim/Link/views/BankFlowManager.view.tsx — CC 98, MI 47.64, SLOC 407
  • critical complexity — src/components/TransactionDetails/transactionTransformer.ts — CC 92, MI 44.6, SLOC 268
  • critical complexity — src/components/Profile/views/UnlockedRegions.view.tsx — CC 66, MI 61.73, SLOC 198
  • critical complexity — src/hooks/useHomeCarouselCTAs.tsx — CC 54, MI 59.49, SLOC 235
  • critical complexity — src/components/TransactionDetails/provider-rows/CardPaymentRows.tsx — CC 52, MI 51.55, SLOC 128
  • critical complexity — src/components/AddMoney/components/MantecaAddMoney.tsx — CC 50, MI 56.79, SLOC 191
  • high complexity — src/utils/history.utils.ts — CC 49, MI 45.09, SLOC 170
  • high hotspot — src/components/AddWithdraw/AddWithdrawCountriesList.tsx — 41 commits, +692/-488 lines since 6 months ago
  • high hotspot — src/components/AddMoney/components/MantecaAddMoney.tsx — 37 commits, +378/-228 lines since 6 months ago
  • high complexity — src/services/sendLinks.ts — CC 34, MI 52.38, SLOC 133
  • high method-complexity — src/components/Claim/Link/views/BankFlowManager.view.tsx:290 — CC 32 SLOC 108
  • high method-complexity — src/components/TransactionDetails/transactionTransformer.ts:167 — mapEntryStatusToUiStatus CC 32 SLOC 51
  • high hotspot — src/hooks/useHomeCarouselCTAs.tsx — 32 commits, +367/-225 lines since 6 months ago
  • high hotspot — src/components/TransactionDetails/transactionTransformer.ts — 31 commits, +852/-759 lines since 6 months ago
  • high method-complexity — src/utils/history.utils.ts:321 — completeHistoryEntry CC 31 SLOC 114
  • high method-complexity — src/components/TransactionDetails/transactionTransformer.ts:450 — mapTransactionDataForDrawer CC 30 SLOC 131
  • medium high-mdd — src/hooks/useHomeCarouselCTAs.tsx:72 — useHomeCarouselCTAs: MDD 145.5 (uses across many lines from declarations)
  • medium high-mdd — src/components/AddWithdraw/AddWithdrawCountriesList.tsx:44 — AddWithdrawCountriesList: MDD 137.1 (uses across many lines from declarations)

…and 80 more.

✅ Resolved (98)

  • src/components/AddWithdraw/AddWithdrawCountriesList.tsx — CC 115, MI 56.78, SLOC 332
  • src/components/Claim/Claim.tsx — CC 100, MI 53.99, SLOC 387
  • src/components/Claim/Link/views/BankFlowManager.view.tsx — CC 98, MI 47.73, SLOC 404
  • src/components/TransactionDetails/transactionTransformer.ts — CC 84, MI 42.82, SLOC 245
  • src/components/Profile/views/UnlockedRegions.view.tsx — CC 62, MI 62.4, SLOC 179
  • src/hooks/useHomeCarouselCTAs.tsx — CC 53, MI 59.52, SLOC 235
  • src/utils/history.utils.ts — CC 49, MI 45.1, SLOC 170
  • src/components/AddMoney/components/MantecaAddMoney.tsx — CC 48, MI 56.89, SLOC 190
  • src/components/AddWithdraw/AddWithdrawCountriesList.tsx — 40 commits, +622/-437 lines since 6 months ago
  • src/components/TransactionDetails/provider-rows/CardPaymentRows.tsx — CC 40, MI 53.03, SLOC 98
  • src/components/AddMoney/components/MantecaAddMoney.tsx — 36 commits, +363/-228 lines since 6 months ago
  • src/components/Claim/Link/views/BankFlowManager.view.tsx:290 — CC 32 SLOC 105
  • src/components/TransactionDetails/transactionTransformer.ts:151 — mapEntryStatusToUiStatus CC 32 SLOC 51
  • src/hooks/useHomeCarouselCTAs.tsx — 31 commits, +360/-224 lines since 6 months ago
  • src/utils/history.utils.ts:306 — completeHistoryEntry CC 31 SLOC 114
  • src/hooks/useHomeCarouselCTAs.tsx:72 — useHomeCarouselCTAs: MDD 143.0 (uses across many lines from declarations)
  • src/components/AddWithdraw/AddWithdrawCountriesList.tsx:43 — AddWithdrawCountriesList: MDD 135.6 (uses across many lines from declarations)
  • src/components/AddMoney/components/MantecaAddMoney.tsx:35 — MantecaAddMoney: MDD 110.5 (uses across many lines from declarations)
  • src/components/Claim/Link/views/BankFlowManager.view.tsx:57 — BankFlowManager: MDD 106.3 (uses across many lines from declarations)
  • src/hooks/useHomeCarouselCTAs.tsx:132 — : MDD 91.9 (uses across many lines from declarations)

…and 78 more.

📈 Painscore deltas (top movers)

File Before After Δ
src/components/TransactionDetails/provider-rows/CardPaymentRows.tsx 11.3 12.0 +0.7
src/config/underMaintenance.config.ts 6.2 6.9 +0.7
src/components/Profile/views/UnlockedRegions.view.tsx 9.0 9.6 +0.6

@github-actions

Copy link
Copy Markdown
Contributor

🧪 UI test report — ✅ all green

Suites

  • unit: 1517 ran, 0 failed, 0 skipped, 21.0s

📊 Coverage (unit)

metric %
statements 53.2%
branches 36.1%
functions 40.7%
lines 53.1%
⏱ 10 slowest test cases
time test
0.3s src/app/actions/__tests__/api-headers-extended.test.ts › should not include apiKey in updateUserById body
0.2s src/app/actions/__tests__/api-headers.test.ts › should include Content-Type in updateUserById
0.2s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › every stamp stays within canvas at any count
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle valid 9-digit US account
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle too long for US account
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle valid Italian IBAN
0.1s src/hooks/query/__tests__/user.test.tsx › does NOT call setAuthToken when the response has no token field
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle valid ETH address in lowercase
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle valid ETH address
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle valid US account with spaces
📍 Inline annotations are in the **Unit test report** check above. Coverage artifact: `coverage-unit`. Generated by `.github/workflows/tests.yml`.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/hooks/useActivationStatus.ts (1)

119-122: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider tri-state handling for hasCard to prevent flash during loading.

Currently hasCard is computed as !!findActiveCard(overview), which evaluates to false while overview is loading. If cardInfo loads before overview, a user who has an active card would briefly match the condition and see the card CTA. Since the CTA is dismissable (line 24), they might accidentally dismiss it during the loading window, persisting the dismissal even after overview loads and reveals they have a card.

UnlockedRegions.view.tsx (lines 75-78) uses explicit tri-state handling for the same field to prevent similar issues:

const hasActiveCard = useMemo<boolean | undefined>(() => {
    if (!overview) return undefined
    return !!findActiveCard(overview)
}, [overview])

Then the guard checks hasCard === false instead of !hasCard.

♻️ Proposed refactor
-        const hasCardAccess = cardInfo?.hasCardAccess ?? false
-        const hasCard = !!findActiveCard(overview)
-        if (hasCardAccess && !hasCard && !cardDismissed) {
+        const hasCardAccess = cardInfo?.hasCardAccess ?? false
+        const hasCard = overview === undefined ? undefined : !!findActiveCard(overview)
+        if (hasCardAccess && hasCard === false && !cardDismissed) {
             activationStep = 'card'
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/hooks/useActivationStatus.ts` around lines 119 - 122, The hasCard
variable needs to use tri-state handling to prevent false positives during
loading. Wrap the hasCard computation in a useMemo hook that returns undefined
when overview is not yet loaded, otherwise returns the boolean result of
findActiveCard(overview). Update the condition check from !hasCard to hasCard
=== false to properly handle the three states (undefined for loading, false when
no card, true when card exists). This prevents the card CTA from briefly
appearing and being accidentally dismissed when cardInfo loads before overview.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/hooks/useActivationStatus.ts`:
- Around line 119-122: The hasCard variable needs to use tri-state handling to
prevent false positives during loading. Wrap the hasCard computation in a
useMemo hook that returns undefined when overview is not yet loaded, otherwise
returns the boolean result of findActiveCard(overview). Update the condition
check from !hasCard to hasCard === false to properly handle the three states
(undefined for loading, false when no card, true when card exists). This
prevents the card CTA from briefly appearing and being accidentally dismissed
when cardInfo loads before overview.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b99d14dd-87b2-41f5-9ca7-6555d3b35a50

📥 Commits

Reviewing files that changed from the base of the PR and between d3eb9a3 and a94ea7e.

📒 Files selected for processing (19)
  • src/app/(mobile-ui)/add-money/__tests__/add-money-states.test.tsx
  • src/components/AddMoney/components/InputAmountStep.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
  • src/components/AddWithdraw/AddWithdrawCountriesList.tsx
  • src/components/AddWithdraw/__tests__/AddWithdrawCountriesList.test.tsx
  • src/components/Claim/Claim.tsx
  • src/components/Claim/Link/views/BankFlowManager.view.tsx
  • src/components/Claim/__tests__/claim-states.test.tsx
  • src/components/Profile/views/UnlockedRegions.view.tsx
  • src/components/TransactionDetails/provider-rows/CardPaymentRows.tsx
  • src/components/TransactionDetails/provider-rows/__tests__/dispute-label.test.ts
  • src/components/TransactionDetails/transactionTransformer.ts
  • src/config/underMaintenance.config.ts
  • src/hooks/useActivationStatus.ts
  • src/hooks/useHomeCarouselCTAs.tsx
  • src/services/__tests__/resolveClaimLink.test.ts
  • src/services/sendLinks.ts
  • src/utils/general.utils.ts
  • src/utils/history.utils.ts

@Hugo0 Hugo0 enabled auto-merge June 23, 2026 20:28
@Hugo0 Hugo0 merged commit 68c1412 into dev Jun 23, 2026
18 of 20 checks passed
@Hugo0 Hugo0 deleted the chore/backmerge-main-into-dev-20260623 branch June 23, 2026 21:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants