@payez/next-mvp 4.1.8 → 4.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +792 -792
- package/dist/auth/better-auth.js +15 -3
- package/dist/components/account/MobileNavDrawer.js +18 -18
- package/dist/components/account/UserAvatarMenu.js +1 -1
- package/dist/components/admin/AlertSettingsTab.js +40 -40
- package/dist/components/admin/LoggingSettingsTab.js +28 -28
- package/dist/lib/idp-fetch.js +9 -25
- package/dist/pages/login/page.js +28 -28
- package/dist/pages/settings/EnhancedSettingsPage.js +17 -17
- package/package.json +1 -1
- package/scripts/dev-broker.ps1 +35 -35
- package/scripts/dev-local.ps1 +45 -45
- package/src/api/auth-handler.ts +548 -548
- package/src/api-handlers/account/change-password.ts +142 -142
- package/src/api-handlers/account/update-phone.ts +79 -79
- package/src/api-handlers/account/verify-email.ts +125 -125
- package/src/api-handlers/account/verify-sms.ts +125 -125
- package/src/api-handlers/admin/analytics.ts +443 -443
- package/src/api-handlers/admin/audit.ts +223 -223
- package/src/api-handlers/admin/index.ts +63 -63
- package/src/api-handlers/admin/redis-sessions.ts +251 -251
- package/src/api-handlers/admin/sessions.ts +318 -318
- package/src/api-handlers/admin/site-logs.ts +365 -365
- package/src/api-handlers/admin/users.ts +242 -242
- package/src/api-handlers/admin/vibe-data.ts +324 -324
- package/src/api-handlers/auth/refresh.ts +682 -682
- package/src/api-handlers/auth/signout.ts +199 -199
- package/src/api-handlers/auth/status.ts +20 -20
- package/src/api-handlers/auth/update-session.ts +110 -110
- package/src/api-handlers/auth/verify-code.ts +112 -112
- package/src/api-handlers/session/refresh-viability.ts +36 -36
- package/src/api-handlers/test/force-expire.ts +60 -60
- package/src/auth/auth-decision.ts +229 -229
- package/src/auth/better-auth.ts +15 -3
- package/src/auth/route-config.ts +219 -219
- package/src/auth/types/auth-types.ts +555 -555
- package/src/auth/types/index.ts +7 -7
- package/src/auth/utils/idp-client.ts +445 -445
- package/src/auth/utils/index.ts +6 -6
- package/src/auth/utils/token-utils.ts +243 -243
- package/src/client/AuthContext.tsx +143 -143
- package/src/client/fetch-with-auth.ts +47 -47
- package/src/components/SessionSync.tsx +138 -138
- package/src/components/SignalRHealthCheck.tsx +131 -131
- package/src/components/account/MobileNavDrawer.tsx +305 -305
- package/src/components/account/UserAvatarMenu.tsx +266 -266
- package/src/components/account/index.ts +13 -13
- package/src/components/admin/AlertSettingsTab.tsx +728 -728
- package/src/components/admin/AnalyticsTab.tsx +703 -703
- package/src/components/admin/DataBrowserTab.tsx +505 -505
- package/src/components/admin/LoggingSettingsTab.tsx +665 -665
- package/src/components/admin/SessionsTab.tsx +414 -414
- package/src/components/admin/StatsTab.tsx +379 -379
- package/src/components/admin/VibeAdminLayout.tsx +187 -187
- package/src/components/admin/index.ts +59 -59
- package/src/components/auth/FederatedAuthSection.tsx +95 -95
- package/src/components/auth/ModeAwareLoginPage.tsx +135 -135
- package/src/components/auth/ModeAwareSignupPage.tsx +267 -267
- package/src/components/auth/TraditionalAuthSection.tsx +99 -99
- package/src/components/recovery/CompleteStep.tsx +36 -36
- package/src/components/recovery/InitiateRecoveryStep.tsx +68 -68
- package/src/components/recovery/SelectMethodStep.tsx +73 -73
- package/src/components/recovery/SetPasswordStep.tsx +97 -97
- package/src/components/recovery/VerifyCodeStep.tsx +90 -90
- package/src/components/reserved/ReservedRecoveryWarning.tsx +160 -160
- package/src/components/reserved/ReservedStatusBox.tsx +118 -118
- package/src/components/ui/BetaBadge.tsx +58 -58
- package/src/components/ui/Footer.tsx +93 -93
- package/src/config/env.ts +57 -57
- package/src/config/logger.ts +62 -62
- package/src/config/vibe-log-transport.ts +264 -264
- package/src/hooks/useAuth.ts +117 -117
- package/src/hooks/useAuthSettings.ts +97 -97
- package/src/hooks/useAvailableProviders.ts +120 -120
- package/src/hooks/usePasswordValidation.ts +127 -127
- package/src/hooks/usePublicAuthSettings.ts +149 -149
- package/src/hooks/useSessionExpiration.ts +101 -101
- package/src/hooks/useViabilitySession.ts +336 -336
- package/src/index.ts +60 -60
- package/src/lib/api-handler.ts +643 -643
- package/src/lib/app-slug.ts +178 -178
- package/src/lib/geolocation.ts +265 -265
- package/src/lib/idp-fetch.ts +10 -26
- package/src/lib/jwt-decode.ts +82 -82
- package/src/lib/roles.ts +177 -177
- package/src/lib/site-logger.ts +245 -245
- package/src/lib/standardized-client-api.ts +901 -901
- package/src/lib/test-aware-get-token.ts +41 -41
- package/src/lib/user-agent-parser.ts +252 -252
- package/src/logging/api/admin-analytics.ts +51 -51
- package/src/logging/api/audit-log.ts +53 -53
- package/src/logging/components/AdminAnalyticsLayout.tsx +49 -49
- package/src/logging/components/AuditLogViewer.tsx +125 -125
- package/src/logging/components/ErrorMetricsCard.tsx +98 -98
- package/src/logging/components/HealthMetricsCard.tsx +70 -70
- package/src/logging/hooks/useAdminAnalytics.ts +22 -22
- package/src/logging/hooks/useAuditLog.ts +24 -24
- package/src/logging/hooks/useErrorMetrics.ts +40 -40
- package/src/logging/hooks/useHealthMetrics.ts +44 -44
- package/src/logging/index.ts +18 -18
- package/src/logging/types/analytics.ts +81 -81
- package/src/logging/types/audit.ts +31 -31
- package/src/logging/types/index.ts +3 -3
- package/src/middleware/create-middleware.ts +663 -663
- package/src/middleware/rbac-check.ts +279 -279
- package/src/middleware/twofa-presets.ts +224 -224
- package/src/pages/admin-login/page.tsx +221 -221
- package/src/pages/admin-page-permissions/PagePermissionsAdminPage.tsx +527 -527
- package/src/pages/admin-page-permissions/index.ts +7 -7
- package/src/pages/admin-roles/RolesAdminPage.tsx +533 -533
- package/src/pages/admin-roles/modals.tsx +469 -469
- package/src/pages/client-admin/ClientSiteAdminPage.tsx +382 -382
- package/src/pages/client-admin/index.ts +33 -33
- package/src/pages/coming-soon/page.tsx +83 -83
- package/src/pages/login/page.tsx +456 -456
- package/src/pages/profile/EnhancedProfilePage.tsx +485 -485
- package/src/pages/profile/index.ts +9 -9
- package/src/pages/profile/page.tsx +166 -166
- package/src/pages/recovery/page.tsx +234 -234
- package/src/pages/roles/MyRolesPage.tsx +211 -211
- package/src/pages/roles/components.tsx +294 -294
- package/src/pages/roles/index.ts +17 -17
- package/src/pages/security/EnhancedSecurityPage.tsx +565 -565
- package/src/pages/security/index.ts +9 -9
- package/src/pages/security/page.tsx +507 -507
- package/src/pages/settings/EnhancedSettingsPage.tsx +642 -642
- package/src/pages/settings/index.ts +9 -9
- package/src/pages/settings/page.tsx +47 -47
- package/src/pages/showcase/ShowcasePage.tsx +532 -532
- package/src/pages/showcase/index.ts +13 -13
- package/src/pages/test-env/EmergencyLogoutPage.tsx +180 -180
- package/src/pages/test-env/JwtInspectPage.tsx +456 -456
- package/src/pages/test-env/RefreshTokenPage.tsx +157 -157
- package/src/pages/test-env/TestEnvPage.tsx +118 -118
- package/src/pages/test-env/index.ts +25 -25
- package/src/pages/verify-code/page.tsx +652 -652
- package/src/routes/account/update-phone.ts +13 -13
- package/src/routes/auth/logout.ts +108 -108
- package/src/routes/auth/nextauth.ts +45 -45
- package/src/routes/auth/settings.ts +75 -75
- package/src/routes/auth/viability.ts +224 -224
- package/src/server/auth-guard.ts +170 -170
- package/src/server/slim-middleware.ts +117 -117
- package/src/server/with-auth.ts +93 -93
- package/src/stores/authStore.ts +1899 -1899
- package/src/templates/instrumentation.ts +41 -41
- package/src/utils/logout.ts +30 -30
- package/src/vibe/client.ts +590 -590
- package/src/vibe/enterprise-auth.ts +208 -208
- package/src/vibe/errors.ts +185 -185
- package/src/vibe/generic.ts +431 -431
- package/src/vibe/hooks/index.ts +367 -367
- package/src/vibe/index.ts +132 -132
- package/src/vibe/sessions.ts +551 -551
- package/src/vibe/types.ts +577 -577
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Profile Page exports
|
|
3
|
-
*
|
|
4
|
-
* - ProfilePage: Basic profile display (legacy)
|
|
5
|
-
* - EnhancedProfilePage: Full-featured profile with identity, personal info, contact, address sections
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export { default as ProfilePage } from './page';
|
|
9
|
-
export { default as EnhancedProfilePage } from './EnhancedProfilePage';
|
|
1
|
+
/**
|
|
2
|
+
* Profile Page exports
|
|
3
|
+
*
|
|
4
|
+
* - ProfilePage: Basic profile display (legacy)
|
|
5
|
+
* - EnhancedProfilePage: Full-featured profile with identity, personal info, contact, address sections
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { default as ProfilePage } from './page';
|
|
9
|
+
export { default as EnhancedProfilePage } from './EnhancedProfilePage';
|
|
@@ -1,166 +1,166 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Themed Profile Page for @payez/next-mvp
|
|
3
|
-
*
|
|
4
|
-
* DEPENDENCIES: Only React, Next.js, next-auth, React Query, and Tailwind CSS
|
|
5
|
-
* NO shadcn/ui or other UI library required!
|
|
6
|
-
*
|
|
7
|
-
* FEATURES:
|
|
8
|
-
* ✅ Displays user profile information
|
|
9
|
-
* ✅ Shows email, phone, roles
|
|
10
|
-
* ✅ 2FA status indicator
|
|
11
|
-
* ✅ Themeable styling via ThemeProvider
|
|
12
|
-
* ✅ Uses React Query for data fetching (matches website-membership pattern)
|
|
13
|
-
*
|
|
14
|
-
* USAGE:
|
|
15
|
-
* 1. Import from @payez/next-mvp/pages/profile
|
|
16
|
-
* 2. Wrap your app with ThemeProvider to customize branding
|
|
17
|
-
* 3. Create API route at: src/app/api/account/profile/route.ts
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
'use client';
|
|
21
|
-
|
|
22
|
-
import React from 'react';
|
|
23
|
-
import { useRouter } from 'next/navigation';
|
|
24
|
-
import { useProfile } from '../../hooks/useProfile';
|
|
25
|
-
import { useBranding, useColors, useLayout } from '../../theme/useTheme';
|
|
26
|
-
|
|
27
|
-
export default function ProfilePage() {
|
|
28
|
-
const router = useRouter();
|
|
29
|
-
const { data: profileData, isLoading, error } = useProfile();
|
|
30
|
-
const branding = useBranding();
|
|
31
|
-
const colors = useColors();
|
|
32
|
-
const layout = useLayout();
|
|
33
|
-
|
|
34
|
-
// Loading state
|
|
35
|
-
// Note: Auth protection is handled by middleware - no need to check here
|
|
36
|
-
if (isLoading || !profileData) {
|
|
37
|
-
return (
|
|
38
|
-
<div className="min-h-screen" style={{ background: 'var(--bg-default)' }}>
|
|
39
|
-
<div className="flex items-center justify-center min-h-[400px]">
|
|
40
|
-
<div className="flex flex-col items-center space-y-4">
|
|
41
|
-
<svg className="animate-spin h-8 w-8" style={{ color: 'var(--text-muted)' }} viewBox="0 0 24 24" fill="none">
|
|
42
|
-
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
|
|
43
|
-
<path className="opacity-75" fill="currentColor" d="M4 12 a 8 8 0 0 1 8 -8 v 4 a 4 4 0 0 0 -4 4 H4 z" />
|
|
44
|
-
</svg>
|
|
45
|
-
<p style={{ color: 'var(--text-muted)' }}>Loading profile...</p>
|
|
46
|
-
{error && (
|
|
47
|
-
<p className="text-red-600 text-sm mt-2">{(error as Error).message}</p>
|
|
48
|
-
)}
|
|
49
|
-
</div>
|
|
50
|
-
</div>
|
|
51
|
-
</div>
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Get 2FA status from security settings or direct field
|
|
56
|
-
const twoFactorEnabled = profileData.two_factor_enabled ||
|
|
57
|
-
profileData.security_settings?.some(
|
|
58
|
-
(setting: any) => setting.setting_type?.toLowerCase().includes('2fa') && setting.is_enabled
|
|
59
|
-
) || false;
|
|
60
|
-
|
|
61
|
-
return (
|
|
62
|
-
<div className="min-h-screen" style={{ background: 'var(--bg-default)' }}>
|
|
63
|
-
<div className={`${layout?.spacing || 'space-y-6'} ${layout?.maxWidth || 'max-w-4xl'} mx-auto ${layout?.padding || 'p-6'}`}>
|
|
64
|
-
{/* Header */}
|
|
65
|
-
<div className="flex items-center justify-between">
|
|
66
|
-
<h1 className="text-3xl font-bold" style={{ color: 'var(--text-primary)' }}>Profile</h1>
|
|
67
|
-
</div>
|
|
68
|
-
|
|
69
|
-
{/* Profile Information Card */}
|
|
70
|
-
<div className={`rounded-lg shadow-sm border ${layout?.padding || 'p-6'}`} style={{ background: 'var(--bg-card)', borderColor: 'var(--border-default)' }}>
|
|
71
|
-
{/* Profile Avatar */}
|
|
72
|
-
<div className="flex items-center space-x-4 mb-6">
|
|
73
|
-
<div className="w-20 h-20 bg-gray-300 rounded-full flex items-center justify-center">
|
|
74
|
-
<span className="text-gray-700 font-semibold text-2xl">
|
|
75
|
-
{profileData.email?.charAt(0).toUpperCase() || 'U'}
|
|
76
|
-
</span>
|
|
77
|
-
</div>
|
|
78
|
-
<div>
|
|
79
|
-
<h2 className="text-xl font-semibold text-gray-900">
|
|
80
|
-
{profileData.full_name ||
|
|
81
|
-
(profileData.first_name || profileData.last_name
|
|
82
|
-
? `${profileData.first_name || ''} ${profileData.last_name || ''}`.trim()
|
|
83
|
-
: profileData.email)}
|
|
84
|
-
</h2>
|
|
85
|
-
<p className="text-gray-600 text-sm">
|
|
86
|
-
User ID: {profileData.user_id || profileData.email}
|
|
87
|
-
</p>
|
|
88
|
-
</div>
|
|
89
|
-
</div>
|
|
90
|
-
|
|
91
|
-
{/* Profile Details */}
|
|
92
|
-
<div className="space-y-6">
|
|
93
|
-
{/* Email */}
|
|
94
|
-
<div>
|
|
95
|
-
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-secondary)' }}>Email</label>
|
|
96
|
-
<div className="flex items-center space-x-2">
|
|
97
|
-
<p style={{ color: 'var(--text-primary)' }}>{profileData.email}</p>
|
|
98
|
-
{profileData.email_confirmed && (
|
|
99
|
-
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800">
|
|
100
|
-
✓ Verified
|
|
101
|
-
</span>
|
|
102
|
-
)}
|
|
103
|
-
</div>
|
|
104
|
-
</div>
|
|
105
|
-
|
|
106
|
-
{/* Phone */}
|
|
107
|
-
{profileData?.phone_number && (
|
|
108
|
-
<div>
|
|
109
|
-
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-secondary)' }}>Phone</label>
|
|
110
|
-
<div className="flex items-center space-x-2">
|
|
111
|
-
<p style={{ color: 'var(--text-primary)' }}>{profileData.phone_number}</p>
|
|
112
|
-
{profileData.phone_confirmed && (
|
|
113
|
-
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800">
|
|
114
|
-
✓ Verified
|
|
115
|
-
</span>
|
|
116
|
-
)}
|
|
117
|
-
</div>
|
|
118
|
-
</div>
|
|
119
|
-
)}
|
|
120
|
-
|
|
121
|
-
{/* Roles */}
|
|
122
|
-
{profileData?.roles && profileData.roles.length > 0 && (
|
|
123
|
-
<div>
|
|
124
|
-
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-secondary)' }}>Roles</label>
|
|
125
|
-
<div className="flex flex-wrap gap-2">
|
|
126
|
-
{profileData.roles.map((role) => (
|
|
127
|
-
<span
|
|
128
|
-
key={role}
|
|
129
|
-
className="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-gray-200 text-gray-800"
|
|
130
|
-
>
|
|
131
|
-
{role}
|
|
132
|
-
</span>
|
|
133
|
-
))}
|
|
134
|
-
</div>
|
|
135
|
-
</div>
|
|
136
|
-
)}
|
|
137
|
-
|
|
138
|
-
{/* 2FA Status */}
|
|
139
|
-
<div>
|
|
140
|
-
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-secondary)' }}>Two-Factor Authentication</label>
|
|
141
|
-
<span
|
|
142
|
-
className={`inline-flex items-center px-3 py-1 rounded-full text-sm font-medium ${
|
|
143
|
-
profileData?.two_factor_enabled
|
|
144
|
-
? 'bg-green-100 text-green-800'
|
|
145
|
-
: 'bg-yellow-100 text-yellow-800'
|
|
146
|
-
}`}
|
|
147
|
-
>
|
|
148
|
-
{profileData?.two_factor_enabled ? '✓ Enabled' : 'Not Active'}
|
|
149
|
-
</span>
|
|
150
|
-
</div>
|
|
151
|
-
|
|
152
|
-
{/* Footer Link */}
|
|
153
|
-
<div className="mt-6 pt-6 border-t border-gray-200 text-center">
|
|
154
|
-
<a
|
|
155
|
-
href="/account/security"
|
|
156
|
-
className="text-gray-600 hover:text-gray-800 text-sm hover:underline"
|
|
157
|
-
>
|
|
158
|
-
Manage security settings
|
|
159
|
-
</a>
|
|
160
|
-
</div>
|
|
161
|
-
</div>
|
|
162
|
-
</div>
|
|
163
|
-
</div>
|
|
164
|
-
</div>
|
|
165
|
-
);
|
|
166
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Themed Profile Page for @payez/next-mvp
|
|
3
|
+
*
|
|
4
|
+
* DEPENDENCIES: Only React, Next.js, next-auth, React Query, and Tailwind CSS
|
|
5
|
+
* NO shadcn/ui or other UI library required!
|
|
6
|
+
*
|
|
7
|
+
* FEATURES:
|
|
8
|
+
* ✅ Displays user profile information
|
|
9
|
+
* ✅ Shows email, phone, roles
|
|
10
|
+
* ✅ 2FA status indicator
|
|
11
|
+
* ✅ Themeable styling via ThemeProvider
|
|
12
|
+
* ✅ Uses React Query for data fetching (matches website-membership pattern)
|
|
13
|
+
*
|
|
14
|
+
* USAGE:
|
|
15
|
+
* 1. Import from @payez/next-mvp/pages/profile
|
|
16
|
+
* 2. Wrap your app with ThemeProvider to customize branding
|
|
17
|
+
* 3. Create API route at: src/app/api/account/profile/route.ts
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
'use client';
|
|
21
|
+
|
|
22
|
+
import React from 'react';
|
|
23
|
+
import { useRouter } from 'next/navigation';
|
|
24
|
+
import { useProfile } from '../../hooks/useProfile';
|
|
25
|
+
import { useBranding, useColors, useLayout } from '../../theme/useTheme';
|
|
26
|
+
|
|
27
|
+
export default function ProfilePage() {
|
|
28
|
+
const router = useRouter();
|
|
29
|
+
const { data: profileData, isLoading, error } = useProfile();
|
|
30
|
+
const branding = useBranding();
|
|
31
|
+
const colors = useColors();
|
|
32
|
+
const layout = useLayout();
|
|
33
|
+
|
|
34
|
+
// Loading state
|
|
35
|
+
// Note: Auth protection is handled by middleware - no need to check here
|
|
36
|
+
if (isLoading || !profileData) {
|
|
37
|
+
return (
|
|
38
|
+
<div className="min-h-screen" style={{ background: 'var(--bg-default)' }}>
|
|
39
|
+
<div className="flex items-center justify-center min-h-[400px]">
|
|
40
|
+
<div className="flex flex-col items-center space-y-4">
|
|
41
|
+
<svg className="animate-spin h-8 w-8" style={{ color: 'var(--text-muted)' }} viewBox="0 0 24 24" fill="none">
|
|
42
|
+
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
|
|
43
|
+
<path className="opacity-75" fill="currentColor" d="M4 12 a 8 8 0 0 1 8 -8 v 4 a 4 4 0 0 0 -4 4 H4 z" />
|
|
44
|
+
</svg>
|
|
45
|
+
<p style={{ color: 'var(--text-muted)' }}>Loading profile...</p>
|
|
46
|
+
{error && (
|
|
47
|
+
<p className="text-red-600 text-sm mt-2">{(error as Error).message}</p>
|
|
48
|
+
)}
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Get 2FA status from security settings or direct field
|
|
56
|
+
const twoFactorEnabled = profileData.two_factor_enabled ||
|
|
57
|
+
profileData.security_settings?.some(
|
|
58
|
+
(setting: any) => setting.setting_type?.toLowerCase().includes('2fa') && setting.is_enabled
|
|
59
|
+
) || false;
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<div className="min-h-screen" style={{ background: 'var(--bg-default)' }}>
|
|
63
|
+
<div className={`${layout?.spacing || 'space-y-6'} ${layout?.maxWidth || 'max-w-4xl'} mx-auto ${layout?.padding || 'p-6'}`}>
|
|
64
|
+
{/* Header */}
|
|
65
|
+
<div className="flex items-center justify-between">
|
|
66
|
+
<h1 className="text-3xl font-bold" style={{ color: 'var(--text-primary)' }}>Profile</h1>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
{/* Profile Information Card */}
|
|
70
|
+
<div className={`rounded-lg shadow-sm border ${layout?.padding || 'p-6'}`} style={{ background: 'var(--bg-card)', borderColor: 'var(--border-default)' }}>
|
|
71
|
+
{/* Profile Avatar */}
|
|
72
|
+
<div className="flex items-center space-x-4 mb-6">
|
|
73
|
+
<div className="w-20 h-20 bg-gray-300 rounded-full flex items-center justify-center">
|
|
74
|
+
<span className="text-gray-700 font-semibold text-2xl">
|
|
75
|
+
{profileData.email?.charAt(0).toUpperCase() || 'U'}
|
|
76
|
+
</span>
|
|
77
|
+
</div>
|
|
78
|
+
<div>
|
|
79
|
+
<h2 className="text-xl font-semibold text-gray-900">
|
|
80
|
+
{profileData.full_name ||
|
|
81
|
+
(profileData.first_name || profileData.last_name
|
|
82
|
+
? `${profileData.first_name || ''} ${profileData.last_name || ''}`.trim()
|
|
83
|
+
: profileData.email)}
|
|
84
|
+
</h2>
|
|
85
|
+
<p className="text-gray-600 text-sm">
|
|
86
|
+
User ID: {profileData.user_id || profileData.email}
|
|
87
|
+
</p>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
{/* Profile Details */}
|
|
92
|
+
<div className="space-y-6">
|
|
93
|
+
{/* Email */}
|
|
94
|
+
<div>
|
|
95
|
+
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-secondary)' }}>Email</label>
|
|
96
|
+
<div className="flex items-center space-x-2">
|
|
97
|
+
<p style={{ color: 'var(--text-primary)' }}>{profileData.email}</p>
|
|
98
|
+
{profileData.email_confirmed && (
|
|
99
|
+
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800">
|
|
100
|
+
✓ Verified
|
|
101
|
+
</span>
|
|
102
|
+
)}
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
{/* Phone */}
|
|
107
|
+
{profileData?.phone_number && (
|
|
108
|
+
<div>
|
|
109
|
+
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-secondary)' }}>Phone</label>
|
|
110
|
+
<div className="flex items-center space-x-2">
|
|
111
|
+
<p style={{ color: 'var(--text-primary)' }}>{profileData.phone_number}</p>
|
|
112
|
+
{profileData.phone_confirmed && (
|
|
113
|
+
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800">
|
|
114
|
+
✓ Verified
|
|
115
|
+
</span>
|
|
116
|
+
)}
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
)}
|
|
120
|
+
|
|
121
|
+
{/* Roles */}
|
|
122
|
+
{profileData?.roles && profileData.roles.length > 0 && (
|
|
123
|
+
<div>
|
|
124
|
+
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-secondary)' }}>Roles</label>
|
|
125
|
+
<div className="flex flex-wrap gap-2">
|
|
126
|
+
{profileData.roles.map((role) => (
|
|
127
|
+
<span
|
|
128
|
+
key={role}
|
|
129
|
+
className="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-gray-200 text-gray-800"
|
|
130
|
+
>
|
|
131
|
+
{role}
|
|
132
|
+
</span>
|
|
133
|
+
))}
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
)}
|
|
137
|
+
|
|
138
|
+
{/* 2FA Status */}
|
|
139
|
+
<div>
|
|
140
|
+
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-secondary)' }}>Two-Factor Authentication</label>
|
|
141
|
+
<span
|
|
142
|
+
className={`inline-flex items-center px-3 py-1 rounded-full text-sm font-medium ${
|
|
143
|
+
profileData?.two_factor_enabled
|
|
144
|
+
? 'bg-green-100 text-green-800'
|
|
145
|
+
: 'bg-yellow-100 text-yellow-800'
|
|
146
|
+
}`}
|
|
147
|
+
>
|
|
148
|
+
{profileData?.two_factor_enabled ? '✓ Enabled' : 'Not Active'}
|
|
149
|
+
</span>
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
{/* Footer Link */}
|
|
153
|
+
<div className="mt-6 pt-6 border-t border-gray-200 text-center">
|
|
154
|
+
<a
|
|
155
|
+
href="/account/security"
|
|
156
|
+
className="text-gray-600 hover:text-gray-800 text-sm hover:underline"
|
|
157
|
+
>
|
|
158
|
+
Manage security settings
|
|
159
|
+
</a>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
);
|
|
166
|
+
}
|