@payez/next-mvp 4.1.9 → 4.1.11
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/api-handlers/auth/refresh.js +38 -15
- package/dist/auth/better-auth.js +4 -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/ensure-fresh-access-token.js +42 -8
- package/dist/lib/idp-fetch.js +25 -9
- package/dist/lib/session-store.d.ts +1 -0
- package/dist/lib/session-store.js +12 -8
- package/dist/lib/token-lifecycle.d.ts +2 -0
- package/dist/lib/token-lifecycle.js +21 -3
- package/dist/pages/login/page.js +28 -28
- package/dist/pages/settings/EnhancedSettingsPage.js +17 -17
- package/package.json +893 -893
- 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 +705 -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 +467 -466
- 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/ensure-fresh-access-token.ts +369 -335
- package/src/lib/geolocation.ts +265 -265
- package/src/lib/idp-fetch.ts +26 -10
- package/src/lib/jwt-decode.ts +82 -82
- package/src/lib/roles.ts +177 -177
- package/src/lib/session-store.ts +4 -1
- 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/token-lifecycle.ts +30 -3
- 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,267 +1,267 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import React, { useState } from 'react';
|
|
4
|
-
import { useAuthConfig } from '../../client/AuthContext';
|
|
5
|
-
import { FederatedProvider } from '@/types/auth';
|
|
6
|
-
import { FederatedAuthSection } from './FederatedAuthSection';
|
|
7
|
-
import { TraditionalAuthSection } from './TraditionalAuthSection';
|
|
8
|
-
|
|
9
|
-
interface Props {
|
|
10
|
-
onFederatedSignUp?: (provider: FederatedProvider) => void;
|
|
11
|
-
onTraditionalSignUp?: (email: string, password: string, confirmPassword: string) => Promise<void>;
|
|
12
|
-
isLoading?: boolean;
|
|
13
|
-
error?: string | null;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function ModeAwareSignupPage({
|
|
17
|
-
onFederatedSignUp,
|
|
18
|
-
onTraditionalSignUp,
|
|
19
|
-
isLoading = false,
|
|
20
|
-
error = null,
|
|
21
|
-
}: Props) {
|
|
22
|
-
const config = useAuthConfig();
|
|
23
|
-
const [email, setEmail] = useState('');
|
|
24
|
-
const [password, setPassword] = useState('');
|
|
25
|
-
const [confirmPassword, setConfirmPassword] = useState('');
|
|
26
|
-
const [localLoading, setLocalLoading] = useState(false);
|
|
27
|
-
|
|
28
|
-
const handleFederatedClick = (provider: FederatedProvider) => {
|
|
29
|
-
onFederatedSignUp?.(provider);
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const handleTraditionalSubmit = async (e: React.FormEvent) => {
|
|
33
|
-
e.preventDefault();
|
|
34
|
-
if (!onTraditionalSignUp) return;
|
|
35
|
-
|
|
36
|
-
if (password !== confirmPassword) {
|
|
37
|
-
alert('Passwords do not match');
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
setLocalLoading(true);
|
|
42
|
-
try {
|
|
43
|
-
await onTraditionalSignUp(email, password, confirmPassword);
|
|
44
|
-
} finally {
|
|
45
|
-
setLocalLoading(false);
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const isFederated = config.mode === 'federated';
|
|
50
|
-
const isTraditional = config.mode === 'traditional';
|
|
51
|
-
const totalLoading = isLoading || localLoading;
|
|
52
|
-
|
|
53
|
-
return (
|
|
54
|
-
<>
|
|
55
|
-
{error && (
|
|
56
|
-
<div
|
|
57
|
-
className="mb-6 px-4 py-3 rounded-lg"
|
|
58
|
-
style={{
|
|
59
|
-
background: 'var(--color-error, #ef4444)',
|
|
60
|
-
color: 'white',
|
|
61
|
-
}}
|
|
62
|
-
>
|
|
63
|
-
{error}
|
|
64
|
-
</div>
|
|
65
|
-
)}
|
|
66
|
-
|
|
67
|
-
{/* Federated Only Mode */}
|
|
68
|
-
{isFederated && (
|
|
69
|
-
<FederatedAuthSection
|
|
70
|
-
providers={config.providers}
|
|
71
|
-
onProviderClick={handleFederatedClick}
|
|
72
|
-
isLoading={totalLoading}
|
|
73
|
-
/>
|
|
74
|
-
)}
|
|
75
|
-
|
|
76
|
-
{/* Traditional Only Mode */}
|
|
77
|
-
{isTraditional && config.enableEmailSignup && (
|
|
78
|
-
<form onSubmit={handleTraditionalSubmit} className="space-y-4">
|
|
79
|
-
<div>
|
|
80
|
-
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
|
|
81
|
-
Email Address
|
|
82
|
-
</label>
|
|
83
|
-
<input
|
|
84
|
-
type="email"
|
|
85
|
-
value={email}
|
|
86
|
-
onChange={(e) => setEmail(e.target.value)}
|
|
87
|
-
placeholder="you@example.com"
|
|
88
|
-
disabled={totalLoading}
|
|
89
|
-
required
|
|
90
|
-
className="w-full px-4 py-3 rounded-lg border transition-colors focus:outline-none focus:ring-2 disabled:opacity-50"
|
|
91
|
-
style={{
|
|
92
|
-
border: '1px solid var(--border-default)',
|
|
93
|
-
color: 'var(--text-primary)',
|
|
94
|
-
background: 'var(--bg-default)',
|
|
95
|
-
}}
|
|
96
|
-
/>
|
|
97
|
-
</div>
|
|
98
|
-
|
|
99
|
-
<div>
|
|
100
|
-
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
|
|
101
|
-
Password
|
|
102
|
-
</label>
|
|
103
|
-
<input
|
|
104
|
-
type="password"
|
|
105
|
-
value={password}
|
|
106
|
-
onChange={(e) => setPassword(e.target.value)}
|
|
107
|
-
placeholder="••••••••"
|
|
108
|
-
disabled={totalLoading}
|
|
109
|
-
required
|
|
110
|
-
className="w-full px-4 py-3 rounded-lg border transition-colors focus:outline-none focus:ring-2 disabled:opacity-50"
|
|
111
|
-
style={{
|
|
112
|
-
border: '1px solid var(--border-default)',
|
|
113
|
-
color: 'var(--text-primary)',
|
|
114
|
-
background: 'var(--bg-default)',
|
|
115
|
-
}}
|
|
116
|
-
/>
|
|
117
|
-
</div>
|
|
118
|
-
|
|
119
|
-
<div>
|
|
120
|
-
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
|
|
121
|
-
Confirm Password
|
|
122
|
-
</label>
|
|
123
|
-
<input
|
|
124
|
-
type="password"
|
|
125
|
-
value={confirmPassword}
|
|
126
|
-
onChange={(e) => setConfirmPassword(e.target.value)}
|
|
127
|
-
placeholder="••••••••"
|
|
128
|
-
disabled={totalLoading}
|
|
129
|
-
required
|
|
130
|
-
className="w-full px-4 py-3 rounded-lg border transition-colors focus:outline-none focus:ring-2 disabled:opacity-50"
|
|
131
|
-
style={{
|
|
132
|
-
border: '1px solid var(--border-default)',
|
|
133
|
-
color: 'var(--text-primary)',
|
|
134
|
-
background: 'var(--bg-default)',
|
|
135
|
-
}}
|
|
136
|
-
/>
|
|
137
|
-
</div>
|
|
138
|
-
|
|
139
|
-
<button
|
|
140
|
-
type="submit"
|
|
141
|
-
disabled={totalLoading}
|
|
142
|
-
className="w-full py-3 px-4 rounded-lg font-semibold focus:outline-none focus:ring-2 disabled:opacity-50 disabled:cursor-not-allowed transition-colors mt-6"
|
|
143
|
-
style={{
|
|
144
|
-
border: '1px solid var(--border-default)',
|
|
145
|
-
color: 'white',
|
|
146
|
-
background: 'var(--color-primary, #10b981)',
|
|
147
|
-
}}
|
|
148
|
-
>
|
|
149
|
-
{totalLoading ? 'Creating Account...' : 'Create Account'}
|
|
150
|
-
</button>
|
|
151
|
-
</form>
|
|
152
|
-
)}
|
|
153
|
-
|
|
154
|
-
{/* Hybrid Mode - Show both */}
|
|
155
|
-
{isFederated === false && isTraditional === false && (
|
|
156
|
-
<>
|
|
157
|
-
<FederatedAuthSection
|
|
158
|
-
providers={config.providers}
|
|
159
|
-
onProviderClick={handleFederatedClick}
|
|
160
|
-
isLoading={totalLoading}
|
|
161
|
-
/>
|
|
162
|
-
|
|
163
|
-
{config.providers.length > 0 && config.enableEmailSignup && (
|
|
164
|
-
<div className="relative my-6">
|
|
165
|
-
<div className="absolute inset-0 flex items-center">
|
|
166
|
-
<div
|
|
167
|
-
className="w-full"
|
|
168
|
-
style={{
|
|
169
|
-
borderTop: '1px solid var(--border-default)',
|
|
170
|
-
}}
|
|
171
|
-
></div>
|
|
172
|
-
</div>
|
|
173
|
-
<div className="relative flex justify-center text-sm">
|
|
174
|
-
<span
|
|
175
|
-
className="px-2"
|
|
176
|
-
style={{
|
|
177
|
-
background: 'var(--bg-card)',
|
|
178
|
-
color: 'var(--text-secondary)',
|
|
179
|
-
}}
|
|
180
|
-
>
|
|
181
|
-
Or sign up with email
|
|
182
|
-
</span>
|
|
183
|
-
</div>
|
|
184
|
-
</div>
|
|
185
|
-
)}
|
|
186
|
-
|
|
187
|
-
{config.enableEmailSignup && (
|
|
188
|
-
<form onSubmit={handleTraditionalSubmit} className="space-y-4">
|
|
189
|
-
<div>
|
|
190
|
-
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
|
|
191
|
-
Email Address
|
|
192
|
-
</label>
|
|
193
|
-
<input
|
|
194
|
-
type="email"
|
|
195
|
-
value={email}
|
|
196
|
-
onChange={(e) => setEmail(e.target.value)}
|
|
197
|
-
placeholder="you@example.com"
|
|
198
|
-
disabled={totalLoading}
|
|
199
|
-
required
|
|
200
|
-
className="w-full px-4 py-3 rounded-lg border transition-colors focus:outline-none focus:ring-2 disabled:opacity-50"
|
|
201
|
-
style={{
|
|
202
|
-
border: '1px solid var(--border-default)',
|
|
203
|
-
color: 'var(--text-primary)',
|
|
204
|
-
background: 'var(--bg-default)',
|
|
205
|
-
}}
|
|
206
|
-
/>
|
|
207
|
-
</div>
|
|
208
|
-
|
|
209
|
-
<div>
|
|
210
|
-
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
|
|
211
|
-
Password
|
|
212
|
-
</label>
|
|
213
|
-
<input
|
|
214
|
-
type="password"
|
|
215
|
-
value={password}
|
|
216
|
-
onChange={(e) => setPassword(e.target.value)}
|
|
217
|
-
placeholder="••••••••"
|
|
218
|
-
disabled={totalLoading}
|
|
219
|
-
required
|
|
220
|
-
className="w-full px-4 py-3 rounded-lg border transition-colors focus:outline-none focus:ring-2 disabled:opacity-50"
|
|
221
|
-
style={{
|
|
222
|
-
border: '1px solid var(--border-default)',
|
|
223
|
-
color: 'var(--text-primary)',
|
|
224
|
-
background: 'var(--bg-default)',
|
|
225
|
-
}}
|
|
226
|
-
/>
|
|
227
|
-
</div>
|
|
228
|
-
|
|
229
|
-
<div>
|
|
230
|
-
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
|
|
231
|
-
Confirm Password
|
|
232
|
-
</label>
|
|
233
|
-
<input
|
|
234
|
-
type="password"
|
|
235
|
-
value={confirmPassword}
|
|
236
|
-
onChange={(e) => setConfirmPassword(e.target.value)}
|
|
237
|
-
placeholder="••••••••"
|
|
238
|
-
disabled={totalLoading}
|
|
239
|
-
required
|
|
240
|
-
className="w-full px-4 py-3 rounded-lg border transition-colors focus:outline-none focus:ring-2 disabled:opacity-50"
|
|
241
|
-
style={{
|
|
242
|
-
border: '1px solid var(--border-default)',
|
|
243
|
-
color: 'var(--text-primary)',
|
|
244
|
-
background: 'var(--bg-default)',
|
|
245
|
-
}}
|
|
246
|
-
/>
|
|
247
|
-
</div>
|
|
248
|
-
|
|
249
|
-
<button
|
|
250
|
-
type="submit"
|
|
251
|
-
disabled={totalLoading}
|
|
252
|
-
className="w-full py-3 px-4 rounded-lg font-semibold focus:outline-none focus:ring-2 disabled:opacity-50 disabled:cursor-not-allowed transition-colors mt-6"
|
|
253
|
-
style={{
|
|
254
|
-
border: '1px solid var(--border-default)',
|
|
255
|
-
color: 'white',
|
|
256
|
-
background: 'var(--color-primary, #10b981)',
|
|
257
|
-
}}
|
|
258
|
-
>
|
|
259
|
-
{totalLoading ? 'Creating Account...' : 'Create Account'}
|
|
260
|
-
</button>
|
|
261
|
-
</form>
|
|
262
|
-
)}
|
|
263
|
-
</>
|
|
264
|
-
)}
|
|
265
|
-
</>
|
|
266
|
-
);
|
|
267
|
-
}
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { useState } from 'react';
|
|
4
|
+
import { useAuthConfig } from '../../client/AuthContext';
|
|
5
|
+
import { FederatedProvider } from '@/types/auth';
|
|
6
|
+
import { FederatedAuthSection } from './FederatedAuthSection';
|
|
7
|
+
import { TraditionalAuthSection } from './TraditionalAuthSection';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
onFederatedSignUp?: (provider: FederatedProvider) => void;
|
|
11
|
+
onTraditionalSignUp?: (email: string, password: string, confirmPassword: string) => Promise<void>;
|
|
12
|
+
isLoading?: boolean;
|
|
13
|
+
error?: string | null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function ModeAwareSignupPage({
|
|
17
|
+
onFederatedSignUp,
|
|
18
|
+
onTraditionalSignUp,
|
|
19
|
+
isLoading = false,
|
|
20
|
+
error = null,
|
|
21
|
+
}: Props) {
|
|
22
|
+
const config = useAuthConfig();
|
|
23
|
+
const [email, setEmail] = useState('');
|
|
24
|
+
const [password, setPassword] = useState('');
|
|
25
|
+
const [confirmPassword, setConfirmPassword] = useState('');
|
|
26
|
+
const [localLoading, setLocalLoading] = useState(false);
|
|
27
|
+
|
|
28
|
+
const handleFederatedClick = (provider: FederatedProvider) => {
|
|
29
|
+
onFederatedSignUp?.(provider);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const handleTraditionalSubmit = async (e: React.FormEvent) => {
|
|
33
|
+
e.preventDefault();
|
|
34
|
+
if (!onTraditionalSignUp) return;
|
|
35
|
+
|
|
36
|
+
if (password !== confirmPassword) {
|
|
37
|
+
alert('Passwords do not match');
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
setLocalLoading(true);
|
|
42
|
+
try {
|
|
43
|
+
await onTraditionalSignUp(email, password, confirmPassword);
|
|
44
|
+
} finally {
|
|
45
|
+
setLocalLoading(false);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const isFederated = config.mode === 'federated';
|
|
50
|
+
const isTraditional = config.mode === 'traditional';
|
|
51
|
+
const totalLoading = isLoading || localLoading;
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<>
|
|
55
|
+
{error && (
|
|
56
|
+
<div
|
|
57
|
+
className="mb-6 px-4 py-3 rounded-lg"
|
|
58
|
+
style={{
|
|
59
|
+
background: 'var(--color-error, #ef4444)',
|
|
60
|
+
color: 'white',
|
|
61
|
+
}}
|
|
62
|
+
>
|
|
63
|
+
{error}
|
|
64
|
+
</div>
|
|
65
|
+
)}
|
|
66
|
+
|
|
67
|
+
{/* Federated Only Mode */}
|
|
68
|
+
{isFederated && (
|
|
69
|
+
<FederatedAuthSection
|
|
70
|
+
providers={config.providers}
|
|
71
|
+
onProviderClick={handleFederatedClick}
|
|
72
|
+
isLoading={totalLoading}
|
|
73
|
+
/>
|
|
74
|
+
)}
|
|
75
|
+
|
|
76
|
+
{/* Traditional Only Mode */}
|
|
77
|
+
{isTraditional && config.enableEmailSignup && (
|
|
78
|
+
<form onSubmit={handleTraditionalSubmit} className="space-y-4">
|
|
79
|
+
<div>
|
|
80
|
+
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
|
|
81
|
+
Email Address
|
|
82
|
+
</label>
|
|
83
|
+
<input
|
|
84
|
+
type="email"
|
|
85
|
+
value={email}
|
|
86
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
87
|
+
placeholder="you@example.com"
|
|
88
|
+
disabled={totalLoading}
|
|
89
|
+
required
|
|
90
|
+
className="w-full px-4 py-3 rounded-lg border transition-colors focus:outline-none focus:ring-2 disabled:opacity-50"
|
|
91
|
+
style={{
|
|
92
|
+
border: '1px solid var(--border-default)',
|
|
93
|
+
color: 'var(--text-primary)',
|
|
94
|
+
background: 'var(--bg-default)',
|
|
95
|
+
}}
|
|
96
|
+
/>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<div>
|
|
100
|
+
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
|
|
101
|
+
Password
|
|
102
|
+
</label>
|
|
103
|
+
<input
|
|
104
|
+
type="password"
|
|
105
|
+
value={password}
|
|
106
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
107
|
+
placeholder="••••••••"
|
|
108
|
+
disabled={totalLoading}
|
|
109
|
+
required
|
|
110
|
+
className="w-full px-4 py-3 rounded-lg border transition-colors focus:outline-none focus:ring-2 disabled:opacity-50"
|
|
111
|
+
style={{
|
|
112
|
+
border: '1px solid var(--border-default)',
|
|
113
|
+
color: 'var(--text-primary)',
|
|
114
|
+
background: 'var(--bg-default)',
|
|
115
|
+
}}
|
|
116
|
+
/>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<div>
|
|
120
|
+
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
|
|
121
|
+
Confirm Password
|
|
122
|
+
</label>
|
|
123
|
+
<input
|
|
124
|
+
type="password"
|
|
125
|
+
value={confirmPassword}
|
|
126
|
+
onChange={(e) => setConfirmPassword(e.target.value)}
|
|
127
|
+
placeholder="••••••••"
|
|
128
|
+
disabled={totalLoading}
|
|
129
|
+
required
|
|
130
|
+
className="w-full px-4 py-3 rounded-lg border transition-colors focus:outline-none focus:ring-2 disabled:opacity-50"
|
|
131
|
+
style={{
|
|
132
|
+
border: '1px solid var(--border-default)',
|
|
133
|
+
color: 'var(--text-primary)',
|
|
134
|
+
background: 'var(--bg-default)',
|
|
135
|
+
}}
|
|
136
|
+
/>
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
<button
|
|
140
|
+
type="submit"
|
|
141
|
+
disabled={totalLoading}
|
|
142
|
+
className="w-full py-3 px-4 rounded-lg font-semibold focus:outline-none focus:ring-2 disabled:opacity-50 disabled:cursor-not-allowed transition-colors mt-6"
|
|
143
|
+
style={{
|
|
144
|
+
border: '1px solid var(--border-default)',
|
|
145
|
+
color: 'white',
|
|
146
|
+
background: 'var(--color-primary, #10b981)',
|
|
147
|
+
}}
|
|
148
|
+
>
|
|
149
|
+
{totalLoading ? 'Creating Account...' : 'Create Account'}
|
|
150
|
+
</button>
|
|
151
|
+
</form>
|
|
152
|
+
)}
|
|
153
|
+
|
|
154
|
+
{/* Hybrid Mode - Show both */}
|
|
155
|
+
{isFederated === false && isTraditional === false && (
|
|
156
|
+
<>
|
|
157
|
+
<FederatedAuthSection
|
|
158
|
+
providers={config.providers}
|
|
159
|
+
onProviderClick={handleFederatedClick}
|
|
160
|
+
isLoading={totalLoading}
|
|
161
|
+
/>
|
|
162
|
+
|
|
163
|
+
{config.providers.length > 0 && config.enableEmailSignup && (
|
|
164
|
+
<div className="relative my-6">
|
|
165
|
+
<div className="absolute inset-0 flex items-center">
|
|
166
|
+
<div
|
|
167
|
+
className="w-full"
|
|
168
|
+
style={{
|
|
169
|
+
borderTop: '1px solid var(--border-default)',
|
|
170
|
+
}}
|
|
171
|
+
></div>
|
|
172
|
+
</div>
|
|
173
|
+
<div className="relative flex justify-center text-sm">
|
|
174
|
+
<span
|
|
175
|
+
className="px-2"
|
|
176
|
+
style={{
|
|
177
|
+
background: 'var(--bg-card)',
|
|
178
|
+
color: 'var(--text-secondary)',
|
|
179
|
+
}}
|
|
180
|
+
>
|
|
181
|
+
Or sign up with email
|
|
182
|
+
</span>
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
)}
|
|
186
|
+
|
|
187
|
+
{config.enableEmailSignup && (
|
|
188
|
+
<form onSubmit={handleTraditionalSubmit} className="space-y-4">
|
|
189
|
+
<div>
|
|
190
|
+
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
|
|
191
|
+
Email Address
|
|
192
|
+
</label>
|
|
193
|
+
<input
|
|
194
|
+
type="email"
|
|
195
|
+
value={email}
|
|
196
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
197
|
+
placeholder="you@example.com"
|
|
198
|
+
disabled={totalLoading}
|
|
199
|
+
required
|
|
200
|
+
className="w-full px-4 py-3 rounded-lg border transition-colors focus:outline-none focus:ring-2 disabled:opacity-50"
|
|
201
|
+
style={{
|
|
202
|
+
border: '1px solid var(--border-default)',
|
|
203
|
+
color: 'var(--text-primary)',
|
|
204
|
+
background: 'var(--bg-default)',
|
|
205
|
+
}}
|
|
206
|
+
/>
|
|
207
|
+
</div>
|
|
208
|
+
|
|
209
|
+
<div>
|
|
210
|
+
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
|
|
211
|
+
Password
|
|
212
|
+
</label>
|
|
213
|
+
<input
|
|
214
|
+
type="password"
|
|
215
|
+
value={password}
|
|
216
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
217
|
+
placeholder="••••••••"
|
|
218
|
+
disabled={totalLoading}
|
|
219
|
+
required
|
|
220
|
+
className="w-full px-4 py-3 rounded-lg border transition-colors focus:outline-none focus:ring-2 disabled:opacity-50"
|
|
221
|
+
style={{
|
|
222
|
+
border: '1px solid var(--border-default)',
|
|
223
|
+
color: 'var(--text-primary)',
|
|
224
|
+
background: 'var(--bg-default)',
|
|
225
|
+
}}
|
|
226
|
+
/>
|
|
227
|
+
</div>
|
|
228
|
+
|
|
229
|
+
<div>
|
|
230
|
+
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
|
|
231
|
+
Confirm Password
|
|
232
|
+
</label>
|
|
233
|
+
<input
|
|
234
|
+
type="password"
|
|
235
|
+
value={confirmPassword}
|
|
236
|
+
onChange={(e) => setConfirmPassword(e.target.value)}
|
|
237
|
+
placeholder="••••••••"
|
|
238
|
+
disabled={totalLoading}
|
|
239
|
+
required
|
|
240
|
+
className="w-full px-4 py-3 rounded-lg border transition-colors focus:outline-none focus:ring-2 disabled:opacity-50"
|
|
241
|
+
style={{
|
|
242
|
+
border: '1px solid var(--border-default)',
|
|
243
|
+
color: 'var(--text-primary)',
|
|
244
|
+
background: 'var(--bg-default)',
|
|
245
|
+
}}
|
|
246
|
+
/>
|
|
247
|
+
</div>
|
|
248
|
+
|
|
249
|
+
<button
|
|
250
|
+
type="submit"
|
|
251
|
+
disabled={totalLoading}
|
|
252
|
+
className="w-full py-3 px-4 rounded-lg font-semibold focus:outline-none focus:ring-2 disabled:opacity-50 disabled:cursor-not-allowed transition-colors mt-6"
|
|
253
|
+
style={{
|
|
254
|
+
border: '1px solid var(--border-default)',
|
|
255
|
+
color: 'white',
|
|
256
|
+
background: 'var(--color-primary, #10b981)',
|
|
257
|
+
}}
|
|
258
|
+
>
|
|
259
|
+
{totalLoading ? 'Creating Account...' : 'Create Account'}
|
|
260
|
+
</button>
|
|
261
|
+
</form>
|
|
262
|
+
)}
|
|
263
|
+
</>
|
|
264
|
+
)}
|
|
265
|
+
</>
|
|
266
|
+
);
|
|
267
|
+
}
|