Skip to content

Commit 0a15968

Browse files
committed
feat: enhance UI responsiveness for settings and 2FA components, and rename Didit API verification URL field.
1 parent dd8b3ff commit 0a15968

7 files changed

Lines changed: 129 additions & 82 deletions

File tree

app/me/settings/SettingsContent.tsx

Lines changed: 55 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,15 @@ const SettingsContent = () => {
5454
);
5555
}
5656
return (
57-
<div className='p-10'>
57+
<div className='p-4 sm:p-6 md:p-10'>
5858
<VerificationSubmittedModal
5959
open={showVerificationModal}
6060
onClose={() => setShowVerificationModal(false)}
6161
/>
6262
<div className=''>
6363
{/* Header */}
64-
<div className='mb-8'>
65-
<h1 className='mb-2 text-2xl font-medium text-white'>
64+
<div className='mb-6 sm:mb-8'>
65+
<h1 className='mb-2 text-xl font-medium text-white sm:text-2xl'>
6666
Profile Settings
6767
</h1>
6868
<p className='text-sm text-zinc-500'>
@@ -73,56 +73,58 @@ const SettingsContent = () => {
7373
defaultValue={fromVerification ? 'identity' : 'profile'}
7474
className='w-full'
7575
>
76-
<TabsList className='inline-flex h-auto gap-6 bg-transparent p-0'>
77-
<TabsTrigger
78-
value='profile'
79-
className='text-sm font-medium text-zinc-400 transition-all data-[state=active]:text-white data-[state=active]:shadow-none'
80-
>
81-
Profile
82-
</TabsTrigger>
83-
<TabsTrigger
84-
value='settings'
85-
className='text-sm font-medium text-zinc-400 transition-all data-[state=active]:text-white data-[state=active]:shadow-none'
86-
>
87-
Settings
88-
</TabsTrigger>
89-
<TabsTrigger
90-
value='notifications'
91-
className='text-sm font-medium text-zinc-400 transition-all data-[state=active]:text-white data-[state=active]:shadow-none'
92-
>
93-
Notifications
94-
</TabsTrigger>
95-
<TabsTrigger
96-
value='privacy'
97-
className='text-sm font-medium text-zinc-400 transition-all data-[state=active]:text-white data-[state=active]:shadow-none'
98-
>
99-
Privacy
100-
</TabsTrigger>
101-
<TabsTrigger
102-
value='preferences'
103-
className='text-sm font-medium text-zinc-400 transition-all data-[state=active]:text-white data-[state=active]:shadow-none'
104-
>
105-
Preferences
106-
</TabsTrigger>
107-
<TabsTrigger
108-
value='security'
109-
className='text-sm font-medium text-zinc-400 transition-all data-[state=active]:text-white data-[state=active]:shadow-none'
110-
>
111-
Security
112-
</TabsTrigger>
113-
<TabsTrigger
114-
value='2fa'
115-
className='text-sm font-medium text-zinc-400 transition-all data-[state=active]:text-white data-[state=active]:shadow-none'
116-
>
117-
2FA
118-
</TabsTrigger>
119-
<TabsTrigger
120-
value='identity'
121-
className='text-sm font-medium text-zinc-400 transition-all data-[state=active]:text-white data-[state=active]:shadow-none'
122-
>
123-
Identity
124-
</TabsTrigger>
125-
</TabsList>
76+
<div className='-mx-4 overflow-x-auto px-4 sm:mx-0 sm:px-0'>
77+
<TabsList className='inline-flex h-auto gap-4 bg-transparent p-0 sm:gap-6'>
78+
<TabsTrigger
79+
value='profile'
80+
className='text-sm font-medium whitespace-nowrap text-zinc-400 transition-all data-[state=active]:text-white data-[state=active]:shadow-none'
81+
>
82+
Profile
83+
</TabsTrigger>
84+
<TabsTrigger
85+
value='settings'
86+
className='text-sm font-medium whitespace-nowrap text-zinc-400 transition-all data-[state=active]:text-white data-[state=active]:shadow-none'
87+
>
88+
Settings
89+
</TabsTrigger>
90+
<TabsTrigger
91+
value='notifications'
92+
className='text-sm font-medium whitespace-nowrap text-zinc-400 transition-all data-[state=active]:text-white data-[state=active]:shadow-none'
93+
>
94+
Notifications
95+
</TabsTrigger>
96+
<TabsTrigger
97+
value='privacy'
98+
className='text-sm font-medium whitespace-nowrap text-zinc-400 transition-all data-[state=active]:text-white data-[state=active]:shadow-none'
99+
>
100+
Privacy
101+
</TabsTrigger>
102+
<TabsTrigger
103+
value='preferences'
104+
className='text-sm font-medium whitespace-nowrap text-zinc-400 transition-all data-[state=active]:text-white data-[state=active]:shadow-none'
105+
>
106+
Preferences
107+
</TabsTrigger>
108+
<TabsTrigger
109+
value='security'
110+
className='text-sm font-medium whitespace-nowrap text-zinc-400 transition-all data-[state=active]:text-white data-[state=active]:shadow-none'
111+
>
112+
Security
113+
</TabsTrigger>
114+
<TabsTrigger
115+
value='2fa'
116+
className='text-sm font-medium whitespace-nowrap text-zinc-400 transition-all data-[state=active]:text-white data-[state=active]:shadow-none'
117+
>
118+
2FA
119+
</TabsTrigger>
120+
<TabsTrigger
121+
value='identity'
122+
className='text-sm font-medium whitespace-nowrap text-zinc-400 transition-all data-[state=active]:text-white data-[state=active]:shadow-none'
123+
>
124+
Identity
125+
</TabsTrigger>
126+
</TabsList>
127+
</div>
126128
<TabsContent value='profile'>
127129
{userData?.user ? (
128130
<Profile user={userData.user as User} />

components/didit/DiditVerifyButton.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ export interface DiditVerifyButtonProps {
99
onError?: (error: Error | { code?: string; message?: string }) => void;
1010
className?: string;
1111
disabled?: boolean;
12+
label?: string;
1213
}
1314

1415
export function DiditVerifyButton({
1516
onError,
1617
className,
1718
disabled = false,
19+
label = 'Verify identity',
1820
}: DiditVerifyButtonProps) {
1921
const [loading, setLoading] = useState(false);
2022
const [error, setError] = useState<string | null>(null);
@@ -50,7 +52,7 @@ export function DiditVerifyButton({
5052
disabled={disabled || loading}
5153
className={clsx(className)}
5254
>
53-
{loading ? 'Redirecting to verification…' : 'Verify identity'}
55+
{loading ? 'Redirecting to verification…' : label}
5456
</Button>
5557
{error && (
5658
<p className='text-sm text-red-500' role='alert'>

components/didit/IdentityVerificationSection.tsx

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ export function IdentityVerificationSection({
6969
</CardDescription>
7070
</CardHeader>
7171
<CardContent className='space-y-4'>
72-
{status && config ? (
72+
{/* Status badge row */}
73+
{status && config && (
7374
<div className='flex flex-wrap items-center gap-3'>
7475
<Badge
7576
variant='outline'
@@ -90,15 +91,57 @@ export function IdentityVerificationSection({
9091
</span>
9192
)}
9293
</div>
93-
) : (
94+
)}
95+
96+
{/* Per-state messaging */}
97+
{status === 'Approved' && (
98+
<p className='text-sm text-zinc-400'>
99+
Your identity has been verified. You have full access to all
100+
compliance-gated features.
101+
</p>
102+
)}
103+
104+
{status === 'In Review' && (
105+
<div className='rounded-md border border-amber-500/20 bg-amber-500/5 px-4 py-3'>
106+
<p className='text-sm font-medium text-amber-400'>
107+
Your submission is under review
108+
</p>
109+
<p className='mt-1 text-sm text-zinc-400'>
110+
We&apos;re reviewing your identity documents. This typically takes
111+
1–3 business days. You&apos;ll be notified once a decision has
112+
been made — no further action is needed right now.
113+
</p>
114+
</div>
115+
)}
116+
117+
{status === 'Declined' && (
118+
<div className='rounded-md border border-red-500/20 bg-red-500/5 px-4 py-3'>
119+
<p className='text-sm font-medium text-red-400'>
120+
Verification was declined
121+
</p>
122+
<p className='mt-1 text-sm text-zinc-400'>
123+
Your identity verification was not successful. This can happen if
124+
the documents were unclear, expired, or didn&apos;t match the
125+
required criteria. Please try again with valid, legible documents.
126+
</p>
127+
</div>
128+
)}
129+
130+
{!status && (
94131
<p className='text-sm text-zinc-500'>
95-
You have not completed identity verification yet.
132+
You have not completed identity verification yet. Verify your
133+
identity to unlock compliance-gated features.
96134
</p>
97135
)}
98136

137+
{/* CTA — shown for unverified and declined users */}
99138
{user && status !== 'Approved' && status !== 'In Review' && (
100139
<>
101-
<DiditVerifyButton />
140+
<DiditVerifyButton
141+
label={
142+
status === 'Declined' ? 'Re-verify identity' : 'Verify identity'
143+
}
144+
/>
102145
{isLocalhost() && (
103146
<p className='rounded-md border border-zinc-700 bg-zinc-800/50 px-3 py-2 text-xs text-zinc-500'>
104147
Using localhost? If verification is blocked by the browser, use

components/profile/update/Profile.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ function Profile({ user }: ProfileDataProps) {
292292
<div className='flex flex-col gap-4'>
293293
{/* Avatar Upload */}
294294
<Card className='space-y-8 rounded-xl border border-zinc-800 bg-zinc-900/30 p-6 md:p-8'>
295-
<div className='flex items-center gap-4'>
295+
<div className='flex flex-col items-start gap-4 sm:flex-row sm:items-center'>
296296
<div className='relative'>
297297
<input
298298
ref={avatarRef}
@@ -334,7 +334,7 @@ function Profile({ user }: ProfileDataProps) {
334334
</div>
335335
</div>
336336
</div>
337-
<div className='text-center'>
337+
<div>
338338
<BoundlessButton
339339
icon={<Upload className='h-4 w-4' />}
340340
variant='outline'

components/profile/update/SecurityTab.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,9 @@ const SecurityTab = ({ user }: SecurityTabProps) => {
134134
</div>
135135

136136
<div className='rounded-xl border border-zinc-800 bg-zinc-900/30 p-6 md:p-8'>
137-
<div className='flex items-start justify-between'>
137+
<div className='flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between'>
138138
<div className='flex gap-3'>
139-
<div className='bg-primary/10 rounded-lg p-2'>
139+
<div className='bg-primary/10 shrink-0 rounded-lg p-2'>
140140
<ShieldCheck className='text-primary h-5 w-5' />
141141
</div>
142142
<div>
@@ -149,11 +149,11 @@ const SecurityTab = ({ user }: SecurityTabProps) => {
149149
</p>
150150
</div>
151151
</div>
152-
<div className='flex items-center gap-2 rounded-full border border-white/10 bg-white/5 px-3 py-1'>
152+
<div className='flex w-fit items-center gap-2 rounded-full border border-white/10 bg-white/5 px-3 py-1'>
153153
<div
154-
className={`h-2 w-2 rounded-full ${user.twoFactorEnabled ? 'bg-primary' : 'bg-[#B5B5B5]'}`}
154+
className={`h-2 w-2 shrink-0 rounded-full ${user.twoFactorEnabled ? 'bg-primary' : 'bg-[#B5B5B5]'}`}
155155
/>
156-
<span className='text-xs font-medium text-white uppercase'>
156+
<span className='text-xs font-medium whitespace-nowrap text-white uppercase'>
157157
{user.twoFactorEnabled ? 'Enabled' : 'Disabled'}
158158
</span>
159159
</div>

components/profile/update/TwoFactorTab.tsx

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ const TwoFactorTab = ({ user, onStatusChange }: TwoFactorTabProps) => {
188188
if (user.twoFactorEnabled && step === 'status') {
189189
return (
190190
<div className='space-y-6'>
191-
<div className='border-primary/20 bg-primary/5 flex items-start gap-4 rounded-xl border p-6 md:p-8'>
191+
<div className='border-primary/20 bg-primary/5 flex items-start gap-4 rounded-xl border p-4 sm:p-6 md:p-8'>
192192
<div className='bg-primary/10 rounded-lg p-2'>
193193
<ShieldCheck className='text-primary h-6 w-6' />
194194
</div>
@@ -201,7 +201,7 @@ const TwoFactorTab = ({ user, onStatusChange }: TwoFactorTabProps) => {
201201
</div>
202202
</div>
203203

204-
<div className='space-y-6 rounded-xl border border-zinc-800 bg-zinc-900/30 p-6 md:p-8'>
204+
<div className='space-y-6 rounded-xl border border-zinc-800 bg-zinc-900/30 p-4 sm:p-6 md:p-8'>
205205
<div className='space-y-4'>
206206
<div className='flex items-center gap-3'>
207207
<KeyRound className='text-primary h-5 w-5' />
@@ -227,7 +227,7 @@ const TwoFactorTab = ({ user, onStatusChange }: TwoFactorTabProps) => {
227227
className='h-11 border-zinc-800 bg-zinc-900/50 text-white placeholder:text-zinc-600'
228228
/>
229229
</div>
230-
<div className='flex gap-3'>
230+
<div className='flex flex-col gap-3 sm:flex-row'>
231231
<BoundlessButton
232232
variant='outline'
233233
onClick={handleRegenerateCodes}
@@ -257,7 +257,7 @@ const TwoFactorTab = ({ user, onStatusChange }: TwoFactorTabProps) => {
257257
</div>
258258
))}
259259
</div>
260-
<div className='flex gap-3'>
260+
<div className='flex flex-col gap-3 sm:flex-row'>
261261
<BoundlessButton
262262
variant='outline'
263263
size='sm'
@@ -284,7 +284,7 @@ const TwoFactorTab = ({ user, onStatusChange }: TwoFactorTabProps) => {
284284
if (step === 'status') {
285285
return (
286286
<div className='space-y-6'>
287-
<div className='flex items-start gap-4 rounded-xl border border-zinc-800 bg-zinc-900/30 p-6 md:p-8'>
287+
<div className='flex items-start gap-4 rounded-xl border border-zinc-800 bg-zinc-900/30 p-4 sm:p-6 md:p-8'>
288288
<div className='rounded-lg bg-yellow-500/10 p-2'>
289289
<ShieldAlert className='h-6 w-6 text-yellow-500' />
290290
</div>
@@ -300,7 +300,7 @@ const TwoFactorTab = ({ user, onStatusChange }: TwoFactorTabProps) => {
300300
</div>
301301
</div>
302302

303-
<div className='space-y-4 rounded-xl border border-zinc-800 bg-zinc-900/30 p-6 md:p-8'>
303+
<div className='space-y-4 rounded-xl border border-zinc-800 bg-zinc-900/30 p-4 sm:p-6 md:p-8'>
304304
<div className='space-y-2'>
305305
<Label htmlFor='setup-password'>Confirm Password to Setup</Label>
306306
<Input
@@ -330,7 +330,7 @@ const TwoFactorTab = ({ user, onStatusChange }: TwoFactorTabProps) => {
330330
<h3 className='text-xl font-bold text-white'>Scan QR Code</h3>
331331
</div>
332332

333-
<div className='flex flex-col items-center gap-8 rounded-xl border border-zinc-800 bg-zinc-900/30 p-8 md:flex-row'>
333+
<div className='flex flex-col items-center gap-6 rounded-xl border border-zinc-800 bg-zinc-900/30 p-4 sm:p-6 md:flex-row md:gap-8 md:p-8'>
334334
<div className='rounded-lg bg-white p-4'>
335335
<QRCodeSVG value={totpUri} size={180} />
336336
</div>
@@ -396,7 +396,7 @@ const TwoFactorTab = ({ user, onStatusChange }: TwoFactorTabProps) => {
396396
<h3 className='text-xl font-bold text-white'>Verify Setup</h3>
397397
</div>
398398

399-
<div className='space-y-6 rounded-xl border border-zinc-800 bg-zinc-900/30 p-8'>
399+
<div className='space-y-6 rounded-xl border border-zinc-800 bg-zinc-900/30 p-4 sm:p-6 md:p-8'>
400400
<div className='space-y-2 text-center'>
401401
<p className='text-[#B5B5B5]'>
402402
Enter the 6-digit code from your app to confirm everything is
@@ -462,7 +462,7 @@ const TwoFactorTab = ({ user, onStatusChange }: TwoFactorTabProps) => {
462462
if (step === 'backup') {
463463
return (
464464
<div className='space-y-8'>
465-
<div className='border-primary/20 bg-primary/10 space-y-4 rounded-xl border p-6'>
465+
<div className='border-primary/20 bg-primary/10 space-y-4 rounded-xl border p-4 sm:p-6'>
466466
<div className='text-primary flex items-center gap-3'>
467467
<ShieldCheck className='h-6 w-6' />
468468
<h3 className='text-xl font-bold'>2FA Enabled Successfully</h3>
@@ -473,18 +473,18 @@ const TwoFactorTab = ({ user, onStatusChange }: TwoFactorTabProps) => {
473473
</p>
474474
</div>
475475

476-
<div className='space-y-6 rounded-xl border border-zinc-800 bg-zinc-900/30 p-8'>
477-
<div className='grid grid-cols-2 gap-4'>
476+
<div className='space-y-6 rounded-xl border border-zinc-800 bg-zinc-900/30 p-4 sm:p-6 md:p-8'>
477+
<div className='grid grid-cols-2 gap-3 sm:gap-4'>
478478
{backupCodes.map((code, i) => (
479479
<div
480480
key={i}
481-
className='rounded border border-white/10 bg-white/5 p-3 text-center font-mono tracking-widest text-white uppercase'
481+
className='rounded border border-white/10 bg-white/5 p-2 text-center font-mono text-xs tracking-widest text-white uppercase sm:p-3 sm:text-sm'
482482
>
483483
{code}
484484
</div>
485485
))}
486486
</div>
487-
<div className='flex gap-4'>
487+
<div className='flex flex-col gap-3 sm:flex-row sm:gap-4'>
488488
<BoundlessButton
489489
fullWidth
490490
variant='outline'

0 commit comments

Comments
 (0)