From 8b97ed2d904ad70a7e1e87e6bcfe0ccd7e70e58c Mon Sep 17 00:00:00 2001
From: kushagrasarathe <76868364+kushagrasarathe@users.noreply.github.com>
Date: Wed, 27 May 2026 11:25:12 +0530
Subject: [PATCH 1/3] add /dev/kyc-ui audit page for verification vs
capabilities review
dev-only page showing all KYC UI states, modals, copy audit, and a
proposed "Where you can pay" page design that separates identity
confirmation from per-region payment status. splits manteca into
separate argentina/brazil entries matching the geo-scoped enrollment.
---
src/app/dev/kyc-ui/page.tsx | 603 ++++++++++++++++++++++++++++++++++++
1 file changed, 603 insertions(+)
create mode 100644 src/app/dev/kyc-ui/page.tsx
diff --git a/src/app/dev/kyc-ui/page.tsx b/src/app/dev/kyc-ui/page.tsx
new file mode 100644
index 000000000..021193cfd
--- /dev/null
+++ b/src/app/dev/kyc-ui/page.tsx
@@ -0,0 +1,603 @@
+'use client'
+
+import { useState } from 'react'
+import NavHeader from '@/components/Global/NavHeader'
+import { Button } from '@/components/0_Bruddle/Button'
+import Card from '@/components/Global/Card'
+import InfoCard from '@/components/Global/InfoCard'
+import { Icon } from '@/components/Global/Icons/Icon'
+import StatusBadge from '@/components/Global/Badges/StatusBadge'
+import { ActionListCard } from '@/components/ActionListCard'
+import { getCardPosition } from '@/components/Global/Card/card.utils'
+
+// kyc state components (rendered inline with mock data)
+import { KycCompleted } from '@/components/Kyc/states/KycCompleted'
+import { KycProcessing } from '@/components/Kyc/states/KycProcessing'
+import { KycActionRequired } from '@/components/Kyc/states/KycActionRequired'
+import { KycRequiresDocuments } from '@/components/Kyc/states/KycRequiresDocuments'
+import { KycProviderRejection } from '@/components/Kyc/states/KycProviderRejection'
+import { KycFailed } from '@/components/Kyc/states/KycFailed'
+import { KycNotStarted } from '@/components/Kyc/states/KycNotStarted'
+
+// modals
+import { InitiateKycModal } from '@/components/Kyc/InitiateKycModal'
+import { GuestVerificationModal } from '@/components/Global/GuestVerificationModal'
+
+// types
+import type { ProviderRejectionInfo } from '@/hooks/useProviderRejectionStatus'
+
+// ─── mock data ──────────────────────────────────────────────────
+
+const MOCK_FIXABLE_REJECTION: ProviderRejectionInfo = {
+ provider: 'BRIDGE',
+ state: 'fixable',
+ userMessage: 'We need an additional document to enable payments.',
+ rejectedRails: [],
+ kycVerification: null,
+ selfHealAttempt: 1,
+ maxAttempts: 3,
+}
+
+const MOCK_BLOCKED_REJECTION: ProviderRejectionInfo = {
+ provider: 'BRIDGE',
+ state: 'blocked',
+ userMessage: "We couldn't enable payments for your account. Please contact support for assistance.",
+ rejectedRails: [],
+ kycVerification: null,
+ selfHealAttempt: 3,
+ maxAttempts: 3,
+}
+
+const MOCK_TOS_REJECTION: ProviderRejectionInfo = {
+ provider: 'BRIDGE',
+ state: 'fixable',
+ userMessage: 'Please accept the terms to enable payments.',
+ rejectedRails: [],
+ kycVerification: null,
+ selfHealAttempt: 0,
+ maxAttempts: 3,
+}
+
+const MOCK_FIELDS_REJECTION: ProviderRejectionInfo = {
+ provider: 'BRIDGE',
+ state: 'fixable',
+ userMessage: 'We need a few more details to enable payments.',
+ rejectedRails: [],
+ kycVerification: null,
+ selfHealAttempt: 0,
+ maxAttempts: 3,
+}
+
+// ─── helpers ────────────────────────────────────────────────────
+
+function StateCard({
+ label,
+ scenario,
+ children,
+ problem,
+}: {
+ label: string
+ scenario: string
+ children: React.ReactNode
+ problem?: string
+}) {
+ return (
+
+
+
+ {children}
+
+ {problem && (
+
{problem}
+ )}
+
+ )
+}
+
+function CopyAuditRow({
+ file,
+ current,
+ proposed,
+ severity,
+}: {
+ file: string
+ current: string
+ proposed: string
+ severity: 'HIGH' | 'MED' | 'LOW'
+}) {
+ const badgeStatus = severity === 'HIGH' ? 'failed' : severity === 'MED' ? 'pending' : 'processing'
+ return (
+
+
+
+ {file}
+
+
+
{current}
+
{proposed}
+
+
+ )
+}
+
+// ─── proposed regions page mockup ───────────────────────────────
+
+function ProposedRegionsPage({ userState }: { userState: 'new' | 'verified-rfi' | 'eea' | 'happy' }) {
+ const noop = () => {}
+
+ // bridge rails — europe, us, mexico
+ const europePayments = [
+ { name: 'SEPA bank transfer', desc: '30+ countries' },
+ { name: 'UK bank transfer', desc: 'Faster Payments' },
+ ]
+ const usPayments = [
+ { name: 'US bank transfer', desc: 'ACH' },
+ { name: 'US wire transfer', desc: 'Wire' },
+ { name: 'Mexico SPEI', desc: 'Instant' },
+ ]
+
+ // manteca rails — separate enrollment per country
+ const argPayments = [
+ { name: 'MercadoPago QR', desc: 'Pay at stores' },
+ { name: 'Bank transfer', desc: 'Send to your own accounts' },
+ ]
+ const brPayments = [
+ { name: 'PIX QR', desc: 'Pay at stores and send to friends' },
+ { name: 'Bank transfer', desc: 'Send to your own accounts' },
+ ]
+
+ return (
+
+
+
+ {/* id confirmed — subtle inline, not a banner */}
+ {userState !== 'new' && (
+
+
+ ID confirmed
+
+ )}
+
+ {/* new user: onboarding */}
+ {userState === 'new' && (
+
+
+
+
+
+
+
Send money worldwide
+
+ Bank transfers, QR payments, and more. Confirm your ID to get started.
+
+
+
+ Get started
+
+
+
+ )}
+
+ {/* eea deadline notice */}
+ {userState === 'eea' && (
+
+ )}
+
+ {/* active payment methods */}
+ {userState !== 'new' && (
+ <>
+ {/* europe (bridge) */}
+
+
Europe
+ {europePayments.map((p, i) => {
+ const needsAction = userState === 'eea' && p.name === 'SEPA bank transfer'
+ return (
+
: undefined}
+ title={p.name}
+ description={needsAction ? 'Needs a quick update to stay active' : p.desc}
+ descriptionClassName="text-xs"
+ onClick={noop}
+ rightContent={
+ needsAction ? (
+
+ ) : (
+
+ )
+ }
+ />
+ )
+ })}
+
+
+ {/* us & mexico (bridge) */}
+
+
US & Mexico
+ {usPayments.map((p, i) => {
+ const needsAction = userState === 'verified-rfi' && p.name === 'US bank transfer'
+ return (
+
: undefined}
+ title={p.name}
+ description={needsAction ? 'One document needed to start sending' : p.desc}
+ descriptionClassName="text-xs"
+ onClick={noop}
+ rightContent={
+ needsAction ? (
+
+ ) : (
+
+ )
+ }
+ />
+ )
+ })}
+
+
+ {/* argentina (manteca AR) — separate enrollment */}
+
+
Argentina
+ {argPayments.map((p, i) => (
+
}
+ />
+ ))}
+
+
+ {/* brazil (manteca BR) — separate enrollment */}
+
+
Brazil
+ {brPayments.map((p, i) => (
+
}
+ />
+ ))}
+
+ >
+ )}
+
+ {/* new user: preview what they can unlock */}
+ {userState === 'new' && (
+
+
Available after setup
+ {[
+ { name: 'Europe', desc: 'SEPA and UK bank transfers to 30+ countries' },
+ { name: 'US & Mexico', desc: 'ACH, wire transfers, Mexico SPEI' },
+ { name: 'Argentina', desc: 'MercadoPago QR + bank transfers' },
+ { name: 'Brazil', desc: 'PIX QR + bank transfers' },
+ ].map((r, i, arr) => (
+
}
+ title={r.name}
+ description={r.desc}
+ descriptionClassName="text-xs"
+ onClick={() => {}}
+ isDisabled
+ rightContent={
}
+ />
+ ))}
+
+ )}
+
+ )
+}
+
+// ─── page ───────────────────────────────────────────────────────
+
+export default function KycUiAuditPage() {
+ const [openModal, setOpenModal] = useState(null)
+ const [activeTab, setActiveTab] = useState<'proposed' | 'states' | 'modals' | 'audit'>('proposed')
+ const noop = () => {}
+
+ return (
+
+
+
+ {/* tab nav */}
+
+ {(['proposed', 'states', 'modals', 'audit'] as const).map((tab) => (
+ setActiveTab(tab)}
+ className={`whitespace-nowrap rounded-sm border px-3 py-1.5 text-sm font-bold ${
+ activeTab === tab ? 'border-n-1 bg-black text-white' : 'border-n-1 text-grey-1'
+ }`}
+ >
+ {tab === 'proposed'
+ ? 'Proposed'
+ : tab === 'states'
+ ? 'Current States'
+ : tab === 'modals'
+ ? 'Modals'
+ : 'Copy Audit'}
+
+ ))}
+
+
+ {/* ─── TAB: proposed page ─────────────────────────── */}
+ {activeTab === 'proposed' && (
+
+
+
+
+
Brand new user
+
hasn't confirmed ID yet
+
+
+
+
+
+
+
One payment method needs a document
+
everything works except US bank transfers — needs proof of address
+
+
+
+
+
+
+
European user with deadline
+
SEPA needs a small update by June 29 to stay active
+
+
+
+
+
+
+
Everything working
+
all set up, no actions needed
+
+
+
+
+
+ )}
+
+ {/* ─── TAB: current drawer states ─────────────────── */}
+ {activeTab === 'states' && (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )}
+
+ {/* ─── TAB: modals ────────────────────────────────── */}
+ {activeTab === 'modals' && (
+
+
+
InitiateKycModal
+
+ {[
+ { key: 'default', label: 'Fresh KYC' },
+ { key: 'provider_rejection', label: 'Needs docs' },
+ { key: 'blocked', label: 'Blocked' },
+ { key: 'cross_region', label: 'Cross region' },
+ { key: 'error', label: 'Error' },
+ ].map(({ key, label }) => (
+ setOpenModal(`initiate_${key}`)}
+ >
+ {label}
+
+ ))}
+
+
+
+
+
+
+
Guest modal
+ setOpenModal('guest')}>
+ Open
+
+
+
+ {/* render modals */}
+
setOpenModal(null)} onVerify={noop} variant="default" />
+ setOpenModal(null)} onVerify={noop} variant="provider_rejection" providerMessage="Please upload a clearer photo of your ID to continue." />
+ setOpenModal(null)} onVerify={noop} variant="blocked" />
+ setOpenModal(null)} onVerify={noop} variant="cross_region" regionName="Brazil" />
+ setOpenModal(null)} onVerify={noop} error="Bridge API returned an unexpected error." />
+ setOpenModal(null)} description="You need to verify your identity to use bank transfers." secondaryCtaLabel="Continue as guest" />
+
+ )}
+
+ {/* ─── TAB: copy audit ────────────────────────────── */}
+ {activeTab === 'audit' && (
+
+
+
Copy issues
+
+ The app tells users they're verified, then asks them to "complete verification" for a specific payment method.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Principles
+
+
+
+
+
Good example from today's code
+
+
+ "Your identity is already verified. To send money in this region, we need a valid ID from there."
+
+
+ cross_region variant — acknowledges the user, explains what's needed, ties it to their goal
+
+
+
{
+ setActiveTab('modals')
+ setTimeout(() => setOpenModal('initiate_cross_region'), 100)
+ }}
+ >
+ View this modal live
+
+
+
+ )}
+
+ )
+}
From 9a8dc7bd7b00b62ac85d39bb78f7c50d386003de Mon Sep 17 00:00:00 2001
From: kushagrasarathe <76868364+kushagrasarathe@users.noreply.github.com>
Date: Wed, 27 May 2026 11:30:48 +0530
Subject: [PATCH 2/3] =?UTF-8?q?remove=20useless=20'ID=20confirmed'=20banne?=
=?UTF-8?q?r=20=E2=80=94=20users=20don't=20need=20a=20status=20label=20whe?=
=?UTF-8?q?n=20everything=20works?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/app/dev/kyc-ui/page.tsx | 8 --------
1 file changed, 8 deletions(-)
diff --git a/src/app/dev/kyc-ui/page.tsx b/src/app/dev/kyc-ui/page.tsx
index 021193cfd..9a11c55e4 100644
--- a/src/app/dev/kyc-ui/page.tsx
+++ b/src/app/dev/kyc-ui/page.tsx
@@ -156,14 +156,6 @@ function ProposedRegionsPage({ userState }: { userState: 'new' | 'verified-rfi'
- {/* id confirmed — subtle inline, not a banner */}
- {userState !== 'new' && (
-
-
- ID confirmed
-
- )}
-
{/* new user: onboarding */}
{userState === 'new' && (
From c0997d01525452b36986fbb9da0a67722eeb579e Mon Sep 17 00:00:00 2001
From: kushagrasarathe <76868364+kushagrasarathe@users.noreply.github.com>
Date: Wed, 27 May 2026 11:32:14 +0530
Subject: [PATCH 3/3] remove wrapping cards from proposed page sections
---
src/app/dev/kyc-ui/page.tsx | 16 ++++------------
1 file changed, 4 insertions(+), 12 deletions(-)
diff --git a/src/app/dev/kyc-ui/page.tsx b/src/app/dev/kyc-ui/page.tsx
index 9a11c55e4..6d73535ac 100644
--- a/src/app/dev/kyc-ui/page.tsx
+++ b/src/app/dev/kyc-ui/page.tsx
@@ -350,33 +350,25 @@ export default function KycUiAuditPage() {
Brand new user
hasn't confirmed ID yet
-
-
-
+
One payment method needs a document
everything works except US bank transfers — needs proof of address
-
-
-
+
European user with deadline
SEPA needs a small update by June 29 to stay active
-
-
-
+
Everything working
all set up, no actions needed
-
-
-
+
)}