Skip to content

Release 2026-06-17: dev → main#2236

Merged
jjramirezn merged 23 commits into
mainfrom
dev
Jun 17, 2026
Merged

Release 2026-06-17: dev → main#2236
jjramirezn merged 23 commits into
mainfrom
dev

Conversation

@Hugo0

@Hugo0 Hugo0 commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Release — dev → main (2026-06-17, peanut-ui)

21 commits ahead of main. Coordinated FE+BE release (pairs with peanut-api-ts release).

Badges & EVENT_ALUMNI reactivation

  • feat(badges) add TOUCHED_GRASS + EVENT_ALUMNI badge art + mappings
  • fix(badges) rename → Seedling, second-person copy
  • fix(invites) EVENT_ALUMNI auto-claims for returning logged-in users
  • feat(email) EVENT_ALUMNI hero image for the event-reactivation mailer

Brazil PIX sends

  • feat(withdraw) route Brazil PIX sends through the QR-payment endpoint
  • refactor(pix) extract shared normalizePixInput to prevent input drift

Send-links

  • fix(send-links) thread preparationId so the backend can adopt the prepare intent (pairs with API adopt fix)

Reliability / UX hardening

  • fix(ui) harden 4 dead-button / silent-no-op handlers
  • fix(kyc) don't offer futile retry for terminal unsupported-region
  • fix(analytics) make persons searchable by username in PostHog
  • fix release-audit follow-ups — badge-icon fallback + reachable restart-identity modal

Notes

0xkkonrad and others added 22 commits June 3, 2026 19:42
Sod-tile 'Touched Grass' sticker for the met-us-IRL mailer. Adds the SVG,
the BADGES icon/copy entry, and code/utm campaign mappings so
?code=touched_grass, ?campaignTag=TOUCHED_GRASS, and utm_campaign=touched-grass
all award it.
Mortarboard 'Event Alumni' sticker (badge-draft). Adds the SVG, BADGES
icon/copy entry, and code/utm mappings (alumni) so ?code=, ?campaignTag=,
and ?utm_campaign= all award it.
Badge descriptions are user-facing; switch the Seedling line from third
person ('They shill...') to 'You shill...'. Pairs with the BE rename +
reactivation of the Seedling badge.
…e restart-identity modal

getBadgeIcon fell back to PEANUTMAN_LOGO (StaticImageData, typed `any` by the
svg shim) while shareAssetLayout types iconUrl as string and ShareAssetD3 feeds
it to a raw <img src> — an unknown badge code (the recurring FE-BADGES-drop
incident) would render a broken stamp. Unwrap .src and pin the contract with a
test.

UnlockedRegions' provider-rejection override only promoted fixable/blocked, so
the restart-identity copy + handleRestartIdentity CTA already wired in the
modal were unreachable from this predicate — those users fell back to the
generic start flow. Include restart-identity.

Plus two lint nits from the #2218 review: unused useRouter in the payment error
boundary, missing alt on the careers mascot.
…st asset stub

/code-review pass on this PR converged on two structural fixes:

The predicate now reads state !== 'happy' instead of enumerating the three
non-happy members — the enumeration pattern is how restart-identity got missed
in the first place, and a future fourth state would have repeated it.

The jest asset stub (jest-transform-stub) flattened image imports to a bare
string while Next yields StaticImageData — tests of .src-reading code ran
against fiction, which is exactly how the original object-into-string fallback
shipped unnoticed. Point the mapper at a {src,width,height} stub and drop both
per-file mascot mocks this PR had added. jest-transform-stub stays in
devDependencies for now: removing it forces a pnpm lockfile re-resolve that
drags Sentry onto different otel peers — that cleanup belongs in a deps PR.
fix: string badge-icon fallback + reachable restart-identity modal (release-audit follow-ups)
PostHog's Persons-page free-text search is hardcoded server-side to
properties.email / properties.name / person id / distinct_id — it never
reads username, so no Peanut user was findable by username (0 of 2,658
persons have email or name set). Duplicate username into name on
identify so the search box works. Existing persons were backfilled via
ops/scripts/posthog/backfill-person-name.mjs (mono).
…searchable

fix(analytics): make persons searchable by username in PostHog
The event-badge CTA is a bare ?campaign=EVENT_ALUMNI link (no invite
code). That path only awarded the badge during new-account registration
in useZeroDev, so the 2025 event cohorts — who already have accounts —
clicked through and never got it (and were dumped into /setup signup).
Treat EVENT_ALUMNI like the skip campaign so the logged-in auto-claim
effect fires and awards on a returning visit.
200x200 on Peanut-yellow so it blends into the EmailLayout header.
Served at peanut.me/email/badge-event-alumni.png — the imageUrl the
event-badge template (email-campaigns 003) points at.
Brazil PIX sends from the withdraw/send screen used the offramp/withdraw
endpoint, which is gated on full Manteca KYC (a rail carrying mantecaUserId).
Users who only hold the QR-pool "pay" capability — a strictly broader cohort —
could reach the PIX entry but hit a verification wall they couldn't clear,
even though they can already pay any PIX key by pasting it into the QR scanner.

Reuse that exact path: wrap the key into a BR Code and hand off to /qr-pay,
where the amount is entered and the capability gate (canDo('pay', manteca))
is enforced. All Brazil-PIX send entry points funnel through
/withdraw/manteca?country=brazil&method=pix, so a single delegation at that
page flips the endpoint without touching the Argentina or bank flows. The
saved-account withdraw path (no method=pix) intentionally stays on the
withdraw endpoint.

- pixKeyToQrPayUrl: shared wrap→/qr-pay helper, now used by both the scanner
  overlay and the new PIX-key entry (single source of truth).
- PixKeySend.view: lightweight key-entry surface for the send flow.
The PIX-key normalize block (trim → EMVCo passthrough → +55 phone
canonicalize) was duplicated across the manteca withdraw input and the new
send entry, and a third whitespace-only copy lived inline in the withdraw
test. Promote it to one exported helper and point all three at it, so a future
change to phone/EMVCo handling can't make the two entries disagree.
feat(withdraw): route Brazil PIX sends through the QR-payment endpoint
…epare intent

For a collateral-funded ("mixed") link, useSpendBundle already returns the Rain
prepare intent id as `intentId`, but useCreateLink dropped it — so /send-links
created a second SEND_LINK intent instead of reconciling with the prepare leg
(duplicate in history, wrong amount, uncancellable phantom).

Surface that id out of useCreateLink as `preparationId` and pass it to
sendLinksApi.create. Undefined for smart-only / non-card links, so those are
unchanged. Pairs with the backend adopt in peanut-api-ts.
…ion-id

fix(send-links): thread preparationId so the backend can adopt the prepare intent
Branch was 141 commits behind dev. dev refactored the /invite campaign maps
out of InvitesPage.tsx into campaign-maps.ts (+ campaign-maps.test.ts guard),
which conflicted with this branch's inline edits to those maps.

Resolved by migrating the branch's intent into dev's shape:
- campaign-maps.ts: added the alumni/touched_grass invite-code + utm entries.
- InvitesPage.tsx: took dev's structure (PeanutWavingHello mascot, imported
  maps), then fixed TOUCHED_GRASS claimability (see follow-up): it was never
  registered as a bare campaign, so every touched-grass link dead-ended at the
  "Invalid Invite Code" screen. Introduced classifyBareCampaign() to split
  "claimable without invite" (skip + event_alumni + touched_grass) from
  "promises a card-waitlist skip" (skip + event_alumni only) so the vanity
  touched_grass badge is claimable without showing misleading skip copy.
- campaign-maps.test.ts: guards classifyBareCampaign + the new map entries
  (the existing guard already asserts every mapped code exists in BADGES).

badge.utils.ts auto-merged cleanly — verified TOUCHED_GRASS + EVENT_ALUMNI
entries and the second-person Seedling copy all survived (no silent drop).
FE side of the link-granted Loud Mouth badge (inverse of SHHHHH): the sticker
SVG, its BADGES entry (icon path + profile copy), and the notsoshhh invite-code
→ NOT_SO_SHHHH campaign mapping. Backend wiring is the paired peanut-api-ts PR.
feat(badges): add TOUCHED_GRASS + EVENT_ALUMNI badges
…dev-20260616

chore: back-merge main → dev (20260616) — withdraw + send dead-button hotfixes (#2224, #2225)
Same class as the withdraw Continue throw + Send card: handlers whose failure
path produces no visible change on a control the user can't escape. From the
dead-button audit, the 4 remaining actionable ones:

- HIGH EnableAutoBalanceBanner ('Finish setting up your card'): the modal is
  preventClose + hideModalCloseButton and 'void grant()' discarded the result,
  so a cancelled/failed passkey (common on iOS/1Password) trapped the user with
  a CTA that did nothing. Now surfaces the error and adds a 'Skip for now'
  escape once a grant fails (the grant re-prompts on first card spend anyway).
  + tests.
- HIGH UnlockedRegions ('verify now' → nothing happens; customer-reported by
  Federico): setSelectedRegion(null) closed the modal before the await, then any
  initiate error landed in an off-screen inline <p>. Now a dismissible error
  modal (Try again + Contact support) surfaces flow.error.
- MED CancelDepositActions: cancel failures were only console.error'd, so users
  thought a deposit was cancelled when it wasn't. Added an error state + alert.
- MED useMultiPhaseKycFlow ToS confirm: the await had no try/catch, freezing the
  'preparing' modal ~30s with no feedback. Wrapped it; on failure surface the
  recovery state immediately (mirrors BridgeTosStep).

#5 (payQR non-MANTECA no-op) left as-is: currently unreachable, adding handling
to an impossible path is the opposite anti-pattern. Documented only.
Code-review catch: a ROW (rest-of-world) region has no provider/rail, so an
initiate returns a terminal 'not available in your region yet'. The new error
modal was showing 'Verification couldn't start' + 'Try again', which re-issues
the identical ROW request and loops. Gate retriability on the region's provider:
terminal ROW → neutral title + 'Got it'; transient → 'Try again' + support.
…ning

fix(ui): harden 4 dead-button / silent-no-op handlers (card-setup lockout, KYC region verify, cancel-deposit, ToS confirm)
@vercel

vercel Bot commented Jun 17, 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 17, 2026 4:28pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ab1d0292-4096-48c0-bded-ea16f7d5987f

📥 Commits

Reviewing files that changed from the base of the PR and between fa3874b and 7ff5cd7.

⛔ Files ignored due to path filters (1)
  • public/badges/not_so_shhhh.svg is excluded by !**/*.svg
📒 Files selected for processing (2)
  • src/components/Badges/badge.utils.ts
  • src/components/Invites/campaign-maps.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/Badges/badge.utils.ts
  • src/components/Invites/campaign-maps.ts

Walkthrough

This PR adds a Brazil PIX key send flow (new PixKeySendView, pixKeyToQrPayUrl, and normalizePixInput utilities), introduces bare campaign classification for /invite?campaign= links with new TOUCHED_GRASS and EVENT_ALUMNI badges, threads a preparationId/intentId through the send-link creation path, and improves error handling in EnableAutoBalanceBanner, CancelDepositActions, UnlockedRegions, and useMultiPhaseKycFlow. Minor cleanups include removing an unused router import, adding a PostHog name field, and an empty alt attribute fix.

Changes

Brazil PIX Send Flow

Layer / File(s) Summary
PIX URL builder and normalization utilities
src/utils/pix.utils.ts, src/utils/withdraw.utils.ts, src/utils/__tests__/pix.utils.test.ts, src/utils/__tests__/withdraw.utils.test.ts
Adds pixKeyToQrPayUrl (converts a PIX key to a /qr-pay URL or returns null) and normalizePixInput (trims/strips whitespace, canonicalizes phone keys) as shared exports; extends both test suites to cover the new functions.
PixKeySendView component
src/components/Withdraw/views/PixKeySend.view.tsx
Creates the new client component that validates and normalizes PIX key input, converts on continue via pixKeyToQrPayUrl, and navigates with router.push; renders ValidatedInput, a hint, Continue button, and conditional ErrorAlert.
Manteca withdraw PIX routing and QR scanner update
src/app/(mobile-ui)/withdraw/manteca/page.tsx, src/components/Global/QRScannerOverlay/index.tsx
Routes MantecaWithdrawFlow to PixKeySendView when country=brazil and method=pix; replaces inline PIX normalization with normalizePixInput; refactors QRScannerOverlay PIX_KEY handling to use pixKeyToQrPayUrl.

Bare Campaign Classification and New Badges

Layer / File(s) Summary
New badge entries and getBadgeIcon string return
src/components/Badges/badge.utils.ts, src/components/Badges/__tests__/badge.utils.test.ts, src/utils/__mocks__/static-image.ts, package.json
Adds TOUCHED_GRASS and EVENT_ALUMNI to BADGES; fixes getBadgeIcon to return string (PEANUTMAN_LOGO.src as fallback); introduces a StaticImageData-shaped Jest mock and updates moduleNameMapper so .src is observable in tests.
classifyBareCampaign logic and campaign map extensions
src/components/Invites/campaign-maps.ts, src/components/Invites/campaign-maps.test.ts
Extends INVITE_CODE_TO_CAMPAIGN_MAP and UTM_CAMPAIGN_TO_BADGE_MAP with alumni and touched_grass; introduces WAITLIST_SKIP_CAMPAIGNS, BARE_VANITY_CAMPAIGNS, CampaignClassification type, and classifyBareCampaign(); adds test suite covering all classification branches.
InvitesPage bare-campaign branching
src/components/Invites/InvitesPage.tsx
Replaces skip-only detection with classifyBareCampaign to derive isBareClaimCampaign and isWaitlistSkip; updates show-content gating, auto-claim early-return, analytics tags, invalid-invite suppression, and UI copy/CTA to three variants: waitlist-skip, vanity badge, and default inviter-code.

Send Link preparationId Threading

Layer / File(s) Summary
preparationId in CreateLinkBody, useCreateLink, and Initial.link.send.view
src/services/sendLinks.ts, src/components/Create/useCreateLink.tsx, src/components/Send/link/views/Initial.link.send.view.tsx
Adds optional preparationId to CreateLinkBody; useCreateLink destructures intentId from sendTransactions and returns it as preparationId; LinkSendInitialView passes it through to sendLinksApi.create.

Error Handling and UX Improvements

Layer / File(s) Summary
EnableAutoBalanceBanner error state and escape CTA
src/components/Home/EnableAutoBalanceBanner.tsx, src/components/Home/__tests__/EnableAutoBalanceBanner.test.tsx
Adds dismissed state and lastError handling; builds dynamic CTA list with conditional "Skip for now" button and updates modal visibility and description text; adds full test suite covering no-error, user-cancelled, and unexpected-error states.
CancelDepositActions error capture and display
src/components/TransactionDetails/provider-actions/CancelDepositActions.tsx
Adds error state and ErrorAlert import; resets errors before running, captures exceptions with a user-facing message on failure; introduces withError helper wrapping all three cancel-button branches.
UnlockedRegions KYC error modal
src/components/Profile/views/UnlockedRegions.view.tsx
Adds errorAcknowledged state; widens hasProviderRejectionForRegion to any non-happy state; derives failedRegionRetriable; replaces inline error text with a conditional ActionModal offering "Try again" or "Got it" based on retriability.
useMultiPhaseKycFlow ToS close error handling and minor fixes
src/hooks/useMultiPhaseKycFlow.ts, src/app/[...recipient]/error.tsx, src/context/authContext.tsx, src/components/Jobs/index.tsx
Wraps the ToS-accepted close path in try/catch, setting preparingTimedOut on failure; removes unused useRouter; adds name to PostHog identify payload; adds empty alt to Careers mascot.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • peanutprotocol/peanut-ui#2222: Introduces the same Brazil PIX send routing changes in manteca/page.tsx, QRScannerOverlay, and shared PIX utilities (normalizePixInput, pixKeyToQrPayUrl).
  • peanutprotocol/peanut-ui#2223: Adds the same preparationId/intentId threading through useCreateLinksendLinksApi.create to prevent duplicate SEND_LINK creation.
  • peanutprotocol/peanut-ui#2229: Overlaps directly with the error-handling fixes in EnableAutoBalanceBanner, UnlockedRegions.view.tsx, CancelDepositActions.tsx, and useMultiPhaseKycFlow.ts.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title "Release 2026-06-17: dev → main" clearly identifies this as a release merge commit, which is the primary intent of the changeset containing 21 coordinated commits.
Description check ✅ Passed The description comprehensively details the release scope across multiple feature areas (badges, Brazil PIX, send-links, reliability hardening) and includes merge validation notes, directly relating to the extensive changeset.
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 and usage tips.

@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Code-analysis diff

Painscore total: 5716.56 → 5732.02 (+15.46)
Findings: +2 net (+61 new, -59 resolved)

🆕 New findings (61)

  • critical complexity — src/app/(mobile-ui)/withdraw/manteca/page.tsx — CC 151, MI 52.36, SLOC 520
  • critical complexity — src/components/Global/QRScannerOverlay/index.tsx — CC 74, MI 56.88, SLOC 270
  • critical complexity — src/hooks/useMultiPhaseKycFlow.ts — CC 66, MI 57.79, SLOC 322
  • critical complexity — src/utils/withdraw.utils.ts — CC 63, MI 53.39, SLOC 229
  • critical complexity — src/components/Profile/views/UnlockedRegions.view.tsx — CC 62, MI 62.4, SLOC 179
  • critical complexity — src/components/Invites/InvitesPage.tsx — CC 57, MI 56.95, SLOC 125
  • high hotspot — src/app/(mobile-ui)/withdraw/manteca/page.tsx — 65 commits, +663/-391 lines since 6 months ago
  • high method-complexity — src/app/(mobile-ui)/withdraw/manteca/page.tsx:87 — MantecaBankWithdrawFlow CC 43 SLOC 198
  • high method-complexity — src/components/Global/QRScannerOverlay/index.tsx:196 — CC 31 SLOC 137
  • high complexity — src/components/Badges/badge.utils.ts — CC 10, MI 48.57, SLOC 129
  • medium high-mdd — src/app/(mobile-ui)/withdraw/manteca/page.tsx:87 — MantecaBankWithdrawFlow: MDD 247.4 (uses across many lines from declarations)
  • medium high-mdd — src/hooks/useMultiPhaseKycFlow.ts:90 — useMultiPhaseKycFlow: MDD 112.5 (uses across many lines from declarations)
  • medium high-dlt — src/app/(mobile-ui)/withdraw/manteca/page.tsx:87 — MantecaBankWithdrawFlow: DLT 89 (calls 89 distinct functions — high context load)
  • medium high-mdd — src/components/Profile/views/UnlockedRegions.view.tsx:60 — UnlockedRegions: MDD 82.0 (uses across many lines from declarations)
  • medium high-mdd — src/context/authContext.tsx:62 — AuthProvider: MDD 76.4 (uses across many lines from declarations)
  • medium high-mdd — src/components/Invites/InvitesPage.tsx:24 — InvitePageContent: MDD 55.5 (uses across many lines from declarations)
  • medium high-mdd — src/components/Global/QRScannerOverlay/index.tsx:176 — QRScannerOverlay: MDD 52.5 (uses across many lines from declarations)
  • medium high-mdd — src/components/Send/link/views/Initial.link.send.view.tsx:25 — LinkSendInitialView: MDD 51.3 (uses across many lines from declarations)
  • medium high-mdd — src/app/(mobile-ui)/withdraw/manteca/page.tsx:328 — handleWithdraw: MDD 47.7 (uses across many lines from declarations)
  • medium high-mdd — src/components/Global/QRScannerOverlay/index.tsx:196 — processQRCode: MDD 40.0 (uses across many lines from declarations)

…and 41 more.

✅ Resolved (59)

  • src/app/(mobile-ui)/withdraw/manteca/page.tsx — CC 150, MI 52, SLOC 520
  • src/components/Global/QRScannerOverlay/index.tsx — CC 74, MI 56.7, SLOC 273
  • src/hooks/useMultiPhaseKycFlow.ts — CC 66, MI 57.89, SLOC 319
  • src/utils/withdraw.utils.ts — CC 60, MI 53.14, SLOC 219
  • src/components/Profile/views/UnlockedRegions.view.tsx — CC 56, MI 62.4, SLOC 150
  • src/components/Invites/InvitesPage.tsx — CC 54, MI 57.17, SLOC 122
  • src/app/(mobile-ui)/withdraw/manteca/page.tsx — 63 commits, +644/-375 lines since 6 months ago
  • src/app/(mobile-ui)/withdraw/manteca/page.tsx:77 — MantecaWithdrawFlow CC 43 SLOC 198
  • src/components/Global/QRScannerOverlay/index.tsx:196 — CC 31 SLOC 140
  • src/components/Badges/badge.utils.ts — CC 10, MI 49.41, SLOC 120
  • src/app/(mobile-ui)/withdraw/manteca/page.tsx:77 — MantecaWithdrawFlow is 830 lines — split it
  • src/app/(mobile-ui)/withdraw/manteca/page.tsx:77 — MantecaWithdrawFlow: MDD 239.1 (uses across many lines from declarations)
  • src/hooks/useMultiPhaseKycFlow.ts:90 — useMultiPhaseKycFlow: MDD 110.6 (uses across many lines from declarations)
  • src/app/(mobile-ui)/withdraw/manteca/page.tsx:77 — MantecaWithdrawFlow: DLT 90 (calls 90 distinct functions — high context load)
  • src/context/authContext.tsx:62 — AuthProvider: MDD 75.4 (uses across many lines from declarations)
  • src/components/Profile/views/UnlockedRegions.view.tsx:60 — UnlockedRegions: MDD 71.0 (uses across many lines from declarations)
  • src/components/Invites/InvitesPage.tsx:29 — InvitePageContent: MDD 60.4 (uses across many lines from declarations)
  • src/components/Global/QRScannerOverlay/index.tsx:176 — QRScannerOverlay: MDD 51.3 (uses across many lines from declarations)
  • src/components/Send/link/views/Initial.link.send.view.tsx:25 — LinkSendInitialView: MDD 50.8 (uses across many lines from declarations)
  • src/app/(mobile-ui)/withdraw/manteca/page.tsx:318 — handleWithdraw: MDD 47.7 (uses across many lines from declarations)

…and 39 more.

📈 Painscore deltas (top movers)

File Before After Δ
src/components/Withdraw/views/PixKeySend.view.tsx 0.0 7.2 +7.2
src/utils/__mocks__/static-image.ts 0.0 2.9 +2.9
src/components/Invites/campaign-maps.ts 3.5 5.8 +2.3
src/components/Badges/badge.utils.ts 9.0 9.9 +0.9
src/components/Home/EnableAutoBalanceBanner.tsx 4.8 5.6 +0.8
src/components/Profile/views/UnlockedRegions.view.tsx 8.2 8.9 +0.7
src/components/Invites/InvitesPage.tsx 10.8 11.3 +0.5
src/utils/pix.utils.ts 7.4 6.6 -0.7

@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

🧪 UI test report — ✅ all green

Suites

  • unit: 1455 ran, 0 failed, 0 skipped, 21.1s

📊 Coverage (unit)

metric %
statements 52.3%
branches 34.6%
functions 39.4%
lines 52.2%
⏱ 10 slowest test cases
time test
0.4s src/app/actions/__tests__/api-headers-extended.test.ts › should not include apiKey in updateUserById body
0.3s 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/Request/__tests__/request-states.test.tsx › API failure shows error message and toast
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle valid 9-digit US account
0.1s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › Manteca PIX form ready shows merchant card + amount input + pay button
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 ETH address with surrounding spaces
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 ETH address in lowercase
📍 Inline annotations are in the **Unit test report** check above. Coverage artifact: `coverage-unit`. Generated by `.github/workflows/tests.yml`.

@coderabbitai coderabbitai Bot added the enhancement New feature or request label Jun 17, 2026

@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.

Actionable comments posted: 3

🧹 Nitpick comments (3)
src/utils/__tests__/withdraw.utils.test.ts (1)

281-310: ⚡ Quick win

Add a regression case for +55 phone keys with separators in shared normalization tests.

Now that normalizePixInput is the shared path, include a case like +55-11-99999-9999 to lock canonical phone output behavior and prevent drift.

🤖 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/utils/__tests__/withdraw.utils.test.ts` around lines 281 - 310, The test
cases in the it.each array for the 'Pasted values with whitespace' describe
block currently cover phone numbers with spaces but are missing coverage for
phone numbers with dash/hyphen separators. Add a new test case to the it.each
array with a phone number formatted with dashes (such as +55-11-99999-9999) to
ensure the normalizePixInput function properly handles and normalizes this
common phone formatting pattern alongside the existing whitespace test cases.
src/components/Withdraw/views/PixKeySend.view.tsx (1)

31-33: ⚡ Quick win

Use normalizePixInput inside validation to keep a single normalization source.

validatePixDestination reimplements normalization inline. Reusing normalizePixInput here avoids future drift between this view and other PIX entry points.

Proposed fix
-    const normalized = isPixEmvcoQr(value.trim()) ? value.trim() : value.replace(/\s/g, '')
+    const normalized = normalizePixInput(value)
🤖 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/components/Withdraw/views/PixKeySend.view.tsx` around lines 31 - 33, The
validatePixDestination function contains inline normalization logic that
duplicates the normalization behavior. Replace the inline normalization logic
(the ternary expression checking isPixEmvcoQr and conditionally trimming or
removing spaces) with a call to the normalizePixInput function to ensure a
single source of truth for PIX input normalization across all entry points.
src/components/Invites/campaign-maps.test.ts (1)

47-58: ⚡ Quick win

Add regression tests for campaign alias forms.

Please add explicit expectations for campaign=alumni and campaign=touched-grass so bare-claim behavior is pinned for both accepted naming styles.

Also applies to: 73-76

🤖 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/components/Invites/campaign-maps.test.ts` around lines 47 - 58, The test
for classifyBareCampaign currently only verifies behavior for the uppercase
snake_case campaign names (EVENT_ALUMNI and TOUCHED_GRASS) but does not test
their lowercase kebab-case alias forms (alumni and touched-grass). Add
additional expect calls within the test block to verify that
classifyBareCampaign produces identical results when called with the alias forms
alumni and touched-grass as it does with EVENT_ALUMNI and TOUCHED_GRASS
respectively. Apply the same additions to the other test block mentioned in the
comment.
🤖 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.

Inline comments:
In `@src/components/Invites/campaign-maps.ts`:
- Around line 41-43: The campaign classification system does not normalize
campaign aliases before checking them against the BARE_VANITY_CAMPAIGNS and
WAITLIST_SKIP_CAMPAIGNS sets. This means that variations like 'alumni' or
'touched-grass' are not recognized as matching their canonical forms
'event_alumni' and 'touched_grass'. Create a normalization function that maps
campaign aliases to their canonical names, then apply this normalization to the
campaign parameter before it is classified in the classifyBareCampaign function
to ensure all recognized variants are properly handled and do not fall into the
invalid-invite path.

In `@src/hooks/useMultiPhaseKycFlow.ts`:
- Around line 339-342: The completeFlow() function on line 341 is called
unconditionally after confirmBridgeTosAndAwaitRails, but that helper can exit
after retries without throwing an error even when needsTos is still true,
meaning ToS propagation failed to settle. Modify the
confirmBridgeTosAndAwaitRails helper to throw an error when it exits due to
needsTos still being true after exhausting retries, so that the catch block
handles the failure and completeFlow() is only called when the operation truly
succeeds.

In `@src/utils/withdraw.utils.ts`:
- Around line 236-239: The normalizePixPhoneNumber function is not properly
canonicalizing phone numbers that start with the `+` prefix, leaving separators
intact in inputs like `+55-11-99999-9999`. To fix this, you need to ensure that
when normalizePixInput calls normalizePixPhoneNumber on a phone number
(determined by isPixPhoneNumber check), the returned value has all non-digit
separators removed while preserving the `+` country code prefix. Either update
the normalizePixPhoneNumber function to remove all separators from phone numbers
starting with `+`, or add additional logic in normalizePixInput after the
normalizePixPhoneNumber call to strip separators from the result while keeping
the canonical format intact.

---

Nitpick comments:
In `@src/components/Invites/campaign-maps.test.ts`:
- Around line 47-58: The test for classifyBareCampaign currently only verifies
behavior for the uppercase snake_case campaign names (EVENT_ALUMNI and
TOUCHED_GRASS) but does not test their lowercase kebab-case alias forms (alumni
and touched-grass). Add additional expect calls within the test block to verify
that classifyBareCampaign produces identical results when called with the alias
forms alumni and touched-grass as it does with EVENT_ALUMNI and TOUCHED_GRASS
respectively. Apply the same additions to the other test block mentioned in the
comment.

In `@src/components/Withdraw/views/PixKeySend.view.tsx`:
- Around line 31-33: The validatePixDestination function contains inline
normalization logic that duplicates the normalization behavior. Replace the
inline normalization logic (the ternary expression checking isPixEmvcoQr and
conditionally trimming or removing spaces) with a call to the normalizePixInput
function to ensure a single source of truth for PIX input normalization across
all entry points.

In `@src/utils/__tests__/withdraw.utils.test.ts`:
- Around line 281-310: The test cases in the it.each array for the 'Pasted
values with whitespace' describe block currently cover phone numbers with spaces
but are missing coverage for phone numbers with dash/hyphen separators. Add a
new test case to the it.each array with a phone number formatted with dashes
(such as +55-11-99999-9999) to ensure the normalizePixInput function properly
handles and normalizes this common phone formatting pattern alongside the
existing whitespace test cases.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5521cf5a-9229-44f2-8894-88eef758e4c3

📥 Commits

Reviewing files that changed from the base of the PR and between 0196a02 and fa3874b.

⛔ Files ignored due to path filters (3)
  • public/badges/event_alumni.svg is excluded by !**/*.svg
  • public/badges/touched_grass.svg is excluded by !**/*.svg
  • public/email/badge-event-alumni.png is excluded by !**/*.png
📒 Files selected for processing (25)
  • package.json
  • src/app/(mobile-ui)/withdraw/manteca/page.tsx
  • src/app/[...recipient]/error.tsx
  • src/components/Badges/__tests__/badge.utils.test.ts
  • src/components/Badges/badge.utils.ts
  • src/components/Create/useCreateLink.tsx
  • src/components/Global/QRScannerOverlay/index.tsx
  • src/components/Home/EnableAutoBalanceBanner.tsx
  • src/components/Home/__tests__/EnableAutoBalanceBanner.test.tsx
  • src/components/Invites/InvitesPage.tsx
  • src/components/Invites/campaign-maps.test.ts
  • src/components/Invites/campaign-maps.ts
  • src/components/Jobs/index.tsx
  • src/components/Profile/views/UnlockedRegions.view.tsx
  • src/components/Send/link/views/Initial.link.send.view.tsx
  • src/components/TransactionDetails/provider-actions/CancelDepositActions.tsx
  • src/components/Withdraw/views/PixKeySend.view.tsx
  • src/context/authContext.tsx
  • src/hooks/useMultiPhaseKycFlow.ts
  • src/services/sendLinks.ts
  • src/utils/__mocks__/static-image.ts
  • src/utils/__tests__/pix.utils.test.ts
  • src/utils/__tests__/withdraw.utils.test.ts
  • src/utils/pix.utils.ts
  • src/utils/withdraw.utils.ts
💤 Files with no reviewable changes (1)
  • src/app/[...recipient]/error.tsx

Comment thread src/components/Invites/campaign-maps.ts
Comment thread src/hooks/useMultiPhaseKycFlow.ts
Comment thread src/utils/withdraw.utils.ts
feat(badges): NOT_SO_SHHHH 'Loud Mouth' badge icon + invite mapping
@jjramirezn jjramirezn merged commit 984ed01 into main Jun 17, 2026
23 of 26 checks passed
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