@main12/auth-login 0.4.2 → 1.0.1

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.
Files changed (66) hide show
  1. package/README.md +54 -7
  2. package/dist/components/AuthCard.d.ts +41 -0
  3. package/dist/components/AuthCard.js +28 -0
  4. package/dist/components/AuthCardServer.d.ts +29 -0
  5. package/dist/components/AuthCardServer.js +57 -0
  6. package/dist/components/AuthLayout.d.ts +23 -13
  7. package/dist/components/AuthLayout.js +26 -87
  8. package/dist/components/AuthPages.d.ts +4 -29
  9. package/dist/components/AuthPages.js +23 -105
  10. package/dist/components/AuthPagesServer.d.ts +3 -4
  11. package/dist/components/AuthPagesServer.js +15 -8
  12. package/dist/components/auth-card/AuthCardShell.d.ts +21 -0
  13. package/dist/components/auth-card/AuthCardShell.js +104 -0
  14. package/dist/components/auth-card/FormRenderer.d.ts +32 -0
  15. package/dist/components/auth-card/FormRenderer.js +88 -0
  16. package/dist/components/auth-card/formConfigs.d.ts +25 -0
  17. package/dist/components/auth-card/formConfigs.js +56 -0
  18. package/dist/components/auth-card/index.d.ts +3 -0
  19. package/dist/components/auth-card/index.js +3 -0
  20. package/dist/components/forms/ForgotPasswordForm.d.ts +7 -0
  21. package/dist/components/forms/ForgotPasswordForm.js +56 -0
  22. package/dist/components/forms/LoginForm.d.ts +15 -0
  23. package/dist/components/forms/LoginForm.js +247 -0
  24. package/dist/components/forms/SetPasswordForm.d.ts +7 -0
  25. package/dist/components/forms/SetPasswordForm.js +75 -0
  26. package/dist/components/forms/SignupForm.d.ts +13 -0
  27. package/dist/components/forms/SignupForm.js +152 -0
  28. package/dist/components/forms/VerifyOtpForm.d.ts +9 -0
  29. package/dist/components/forms/VerifyOtpForm.js +107 -0
  30. package/dist/components/forms/index.d.ts +19 -0
  31. package/dist/components/forms/index.js +21 -0
  32. package/dist/components/pages/ForgotPasswordPage.d.ts +2 -1
  33. package/dist/components/pages/ForgotPasswordPageHero.d.ts +3 -2
  34. package/dist/components/pages/ForgotPasswordPageHero.js +70 -64
  35. package/dist/components/pages/ForgotPasswordPageTailwind.d.ts +3 -2
  36. package/dist/components/pages/ForgotPasswordPageTailwind.js +42 -36
  37. package/dist/components/pages/LoginPage.d.ts +2 -1
  38. package/dist/components/pages/LoginPageHero.d.ts +2 -1
  39. package/dist/components/pages/LoginPageHero.js +297 -291
  40. package/dist/components/pages/LoginPageTailwind.d.ts +2 -1
  41. package/dist/components/pages/LoginPageTailwind.js +197 -191
  42. package/dist/components/pages/SetPasswordPage.d.ts +2 -1
  43. package/dist/components/pages/SetPasswordPageHero.d.ts +3 -2
  44. package/dist/components/pages/SetPasswordPageHero.js +98 -92
  45. package/dist/components/pages/SetPasswordPageTailwind.d.ts +3 -2
  46. package/dist/components/pages/SetPasswordPageTailwind.js +63 -57
  47. package/dist/components/pages/SignupPage.d.ts +2 -1
  48. package/dist/components/pages/SignupPageHero.d.ts +3 -2
  49. package/dist/components/pages/SignupPageHero.js +139 -133
  50. package/dist/components/pages/SignupPageTailwind.d.ts +3 -2
  51. package/dist/components/pages/SignupPageTailwind.js +123 -117
  52. package/dist/components/pages/VerifyOtpPage.d.ts +2 -1
  53. package/dist/components/pages/VerifyOtpPageHero.d.ts +2 -1
  54. package/dist/components/pages/VerifyOtpPageHero.js +110 -104
  55. package/dist/components/pages/VerifyOtpPageTailwind.d.ts +2 -1
  56. package/dist/components/pages/VerifyOtpPageTailwind.js +60 -54
  57. package/dist/components/ui/translations.d.ts +9 -0
  58. package/dist/components/ui/translations.js +18 -0
  59. package/dist/config.d.ts +12 -0
  60. package/dist/config.js +11 -0
  61. package/dist/exports/client.d.ts +5 -0
  62. package/dist/exports/client.js +5 -0
  63. package/dist/exports/rsc.d.ts +7 -0
  64. package/dist/exports/rsc.js +8 -0
  65. package/dist/index.js +12 -2
  66. package/package.json +3 -2
@@ -0,0 +1,247 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import React, { Suspense } from 'react';
4
+ import { useSearchParams } from 'next/navigation';
5
+ import { Button, Input, Divider, Spinner } from '../ui/index.js';
6
+ import { useLoginFlow } from '../../auth/application/hooks/useLoginFlow.js';
7
+ import { getUiTranslations } from '../ui/translations.js';
8
+ function LoginFormContent({ onPasswordLogin, redirectTo = '/', showGoogleOAuth = true, signupUrl, locale, messages, onStepChange }) {
9
+ const searchParams = useSearchParams();
10
+ const resolvedRedirect = searchParams.get('redirect') || redirectTo;
11
+ const translations = getUiTranslations(locale, messages);
12
+ const t = translations.login;
13
+ const errors = translations.errors;
14
+ // Helper to translate error keys
15
+ const getErrorMessage = (err)=>{
16
+ if (!err) return null;
17
+ // Check if it's a known error key
18
+ if (err in errors) return errors[err];
19
+ // Return as-is if it's already a message
20
+ return err;
21
+ };
22
+ const { step, email, password, error, isLoading, isSendingOtp, setEmail, setPassword, handleEmailSubmit, handlePasswordSubmit, handleSendOtp, handleEditEmail, handleGoogleLogin } = useLoginFlow({
23
+ redirectTo: resolvedRedirect,
24
+ onPasswordLogin
25
+ });
26
+ // Notify parent of step changes for dynamic title/subtitle
27
+ React.useEffect(()=>{
28
+ if (onStepChange) {
29
+ const title = step === 'otp-prompt' ? t.otpPromptTitle : step === 'email' ? t.title : t.passwordStepTitle;
30
+ const subtitle = step === 'otp-prompt' ? t.otpPromptSubtitle : step === 'email' ? t.subtitle : t.passwordStepSubtitle;
31
+ onStepChange(step, title, subtitle);
32
+ }
33
+ }, [
34
+ step,
35
+ onStepChange,
36
+ t
37
+ ]);
38
+ return /*#__PURE__*/ _jsxs(_Fragment, {
39
+ children: [
40
+ step === 'email' && /*#__PURE__*/ _jsxs("div", {
41
+ className: "animate-[fadeIn_0.3s_ease-out]",
42
+ children: [
43
+ showGoogleOAuth && /*#__PURE__*/ _jsxs(_Fragment, {
44
+ children: [
45
+ /*#__PURE__*/ _jsxs(Button, {
46
+ fullWidth: true,
47
+ variant: "secondary",
48
+ size: "lg",
49
+ className: "mb-4",
50
+ onPress: handleGoogleLogin,
51
+ children: [
52
+ /*#__PURE__*/ _jsxs("svg", {
53
+ width: "20",
54
+ height: "20",
55
+ viewBox: "0 0 48 48",
56
+ children: [
57
+ /*#__PURE__*/ _jsx("path", {
58
+ fill: "#EA4335",
59
+ d: "M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"
60
+ }),
61
+ /*#__PURE__*/ _jsx("path", {
62
+ fill: "#4285F4",
63
+ d: "M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"
64
+ }),
65
+ /*#__PURE__*/ _jsx("path", {
66
+ fill: "#FBBC05",
67
+ d: "M10.53 28.59a14.5 14.5 0 0 1 0-9.18l-7.98-6.19a24.01 24.01 0 0 0 0 21.56l7.98-6.19z"
68
+ }),
69
+ /*#__PURE__*/ _jsx("path", {
70
+ fill: "#34A853",
71
+ d: "M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"
72
+ })
73
+ ]
74
+ }),
75
+ t.continueWithGoogle
76
+ ]
77
+ }),
78
+ /*#__PURE__*/ _jsxs("div", {
79
+ className: "flex items-center gap-4 my-4",
80
+ children: [
81
+ /*#__PURE__*/ _jsx(Divider, {
82
+ className: "flex-1"
83
+ }),
84
+ /*#__PURE__*/ _jsx("span", {
85
+ className: "text-gray-500 text-sm",
86
+ children: t.or
87
+ }),
88
+ /*#__PURE__*/ _jsx(Divider, {
89
+ className: "flex-1"
90
+ })
91
+ ]
92
+ })
93
+ ]
94
+ }),
95
+ /*#__PURE__*/ _jsxs("form", {
96
+ onSubmit: handleEmailSubmit,
97
+ className: "space-y-4",
98
+ children: [
99
+ error && /*#__PURE__*/ _jsx("div", {
100
+ className: "bg-red-50 text-red-600 border border-red-200 rounded-lg p-3 text-sm",
101
+ children: getErrorMessage(error)
102
+ }),
103
+ /*#__PURE__*/ _jsx(Input, {
104
+ type: "email",
105
+ label: t.emailLabel,
106
+ value: email,
107
+ onValueChange: setEmail,
108
+ isRequired: true,
109
+ variant: "secondary"
110
+ }),
111
+ /*#__PURE__*/ _jsx(Button, {
112
+ type: "submit",
113
+ variant: "primary",
114
+ isLoading: isLoading,
115
+ children: t.continue
116
+ })
117
+ ]
118
+ }),
119
+ signupUrl && /*#__PURE__*/ _jsxs("p", {
120
+ className: "text-center text-gray-600 text-sm mt-6",
121
+ children: [
122
+ t.noAccount,
123
+ ' ',
124
+ /*#__PURE__*/ _jsx("a", {
125
+ href: signupUrl,
126
+ className: "text-gray-900 font-medium hover:underline",
127
+ children: t.signUpLink
128
+ })
129
+ ]
130
+ })
131
+ ]
132
+ }),
133
+ step === 'password' && /*#__PURE__*/ _jsxs("div", {
134
+ className: "animate-[fadeIn_0.3s_ease-out]",
135
+ children: [
136
+ /*#__PURE__*/ _jsxs("div", {
137
+ className: "flex items-center justify-between border border-gray-300 rounded-xl px-4 py-3 mb-4",
138
+ children: [
139
+ /*#__PURE__*/ _jsx("span", {
140
+ className: "text-gray-900 text-sm",
141
+ children: email
142
+ }),
143
+ /*#__PURE__*/ _jsx("button", {
144
+ type: "button",
145
+ onClick: handleEditEmail,
146
+ className: "text-gray-600 text-sm font-medium hover:text-gray-900",
147
+ children: t.edit
148
+ })
149
+ ]
150
+ }),
151
+ /*#__PURE__*/ _jsxs("form", {
152
+ onSubmit: handlePasswordSubmit,
153
+ className: "space-y-4",
154
+ children: [
155
+ error && /*#__PURE__*/ _jsx("div", {
156
+ className: "bg-red-50 text-red-600 border border-red-200 rounded-lg p-3 text-sm",
157
+ children: getErrorMessage(error)
158
+ }),
159
+ /*#__PURE__*/ _jsx(Input, {
160
+ type: "password",
161
+ label: t.passwordLabel,
162
+ value: password,
163
+ onValueChange: setPassword,
164
+ isRequired: true,
165
+ autoFocus: true,
166
+ variant: "secondary"
167
+ }),
168
+ /*#__PURE__*/ _jsx("div", {
169
+ className: "text-left",
170
+ children: /*#__PURE__*/ _jsx("a", {
171
+ href: "/forgot-password",
172
+ className: "text-sm text-gray-700 hover:text-gray-900 hover:underline",
173
+ children: t.forgotPassword
174
+ })
175
+ }),
176
+ /*#__PURE__*/ _jsx(Button, {
177
+ type: "submit",
178
+ variant: "primary",
179
+ isLoading: isLoading,
180
+ children: t.continue
181
+ })
182
+ ]
183
+ })
184
+ ]
185
+ }),
186
+ step === 'otp-prompt' && /*#__PURE__*/ _jsxs("div", {
187
+ className: "animate-[fadeIn_0.3s_ease-out]",
188
+ children: [
189
+ /*#__PURE__*/ _jsxs("div", {
190
+ className: "flex items-center justify-between border border-gray-300 rounded-xl px-4 py-3 mb-4",
191
+ children: [
192
+ /*#__PURE__*/ _jsx("span", {
193
+ className: "text-gray-900 text-sm",
194
+ children: email
195
+ }),
196
+ /*#__PURE__*/ _jsx("button", {
197
+ type: "button",
198
+ onClick: handleEditEmail,
199
+ className: "text-gray-600 text-sm font-medium hover:text-gray-900",
200
+ children: t.edit
201
+ })
202
+ ]
203
+ }),
204
+ error && /*#__PURE__*/ _jsx("div", {
205
+ className: "bg-red-50 text-red-600 border border-red-200 rounded-lg p-3 text-sm mb-4",
206
+ children: getErrorMessage(error)
207
+ }),
208
+ /*#__PURE__*/ _jsx("div", {
209
+ className: "bg-blue-50 border border-blue-200 rounded-lg p-4 mb-4",
210
+ children: /*#__PURE__*/ _jsxs("div", {
211
+ className: "flex gap-3",
212
+ children: [
213
+ /*#__PURE__*/ _jsx("span", {
214
+ className: "text-blue-500 text-lg",
215
+ children: "✉"
216
+ }),
217
+ /*#__PURE__*/ _jsx("p", {
218
+ className: "text-sm text-blue-700",
219
+ children: t.verificationNotice
220
+ })
221
+ ]
222
+ })
223
+ }),
224
+ /*#__PURE__*/ _jsx(Button, {
225
+ variant: "primary",
226
+ isLoading: isSendingOtp,
227
+ onPress: handleSendOtp,
228
+ children: isSendingOtp ? t.sendingCode : t.sendCode
229
+ })
230
+ ]
231
+ })
232
+ ]
233
+ });
234
+ }
235
+ export function LoginForm(props) {
236
+ return /*#__PURE__*/ _jsx(Suspense, {
237
+ fallback: /*#__PURE__*/ _jsx("div", {
238
+ className: "flex items-center justify-center py-8",
239
+ children: /*#__PURE__*/ _jsx(Spinner, {
240
+ size: "lg"
241
+ })
242
+ }),
243
+ children: /*#__PURE__*/ _jsx(LoginFormContent, {
244
+ ...props
245
+ })
246
+ });
247
+ }
@@ -0,0 +1,7 @@
1
+ import { type DeepPartial, type UiTranslations } from '../ui/translations';
2
+ export interface SetPasswordFormProps {
3
+ redirectTo?: string;
4
+ locale?: string;
5
+ messages?: Record<string, DeepPartial<UiTranslations>>;
6
+ }
7
+ export declare function SetPasswordForm({ redirectTo, locale, messages, }: SetPasswordFormProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,75 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import React from 'react';
4
+ import { Button, Input } from '../ui/index.js';
5
+ import { useSetPasswordFlow } from '../../auth/application/hooks/useSetPasswordFlow.js';
6
+ import { getUiTranslations } from '../ui/translations.js';
7
+ export function SetPasswordForm({ redirectTo = '/', locale, messages }) {
8
+ const { password, confirmPassword, error, isLoading, showPassword, strength, setPassword, setConfirmPassword, setShowPassword, handleSubmit } = useSetPasswordFlow({
9
+ redirectTo
10
+ });
11
+ const strengthBars = Array.from({
12
+ length: 5
13
+ }, (_, i)=>i < strength.score);
14
+ const translations = getUiTranslations(locale, messages);
15
+ const t = translations.setPassword;
16
+ const errors = translations.errors;
17
+ // Helper to translate error keys
18
+ const getErrorMessage = (err)=>{
19
+ if (!err) return null;
20
+ if (err in errors) return errors[err];
21
+ return err;
22
+ };
23
+ return /*#__PURE__*/ _jsxs("form", {
24
+ onSubmit: handleSubmit,
25
+ className: "space-y-4",
26
+ children: [
27
+ error && /*#__PURE__*/ _jsx("div", {
28
+ className: "bg-red-50 text-red-600 border border-red-200 rounded-lg p-3 text-sm animate-[fadeIn_0.2s_ease-out]",
29
+ children: getErrorMessage(error)
30
+ }),
31
+ /*#__PURE__*/ _jsxs("div", {
32
+ className: "relative",
33
+ children: [
34
+ /*#__PURE__*/ _jsx(Input, {
35
+ type: showPassword ? 'text' : 'password',
36
+ label: t.newPasswordLabel,
37
+ value: password,
38
+ onValueChange: setPassword,
39
+ isRequired: true,
40
+ autoFocus: true
41
+ }),
42
+ /*#__PURE__*/ _jsx("button", {
43
+ type: "button",
44
+ onClick: ()=>setShowPassword(!showPassword),
45
+ className: "absolute right-4 top-9 text-gray-400 hover:text-gray-600",
46
+ children: showPassword ? '🙈' : '👁'
47
+ })
48
+ ]
49
+ }),
50
+ password.length > 0 && /*#__PURE__*/ _jsx("div", {
51
+ className: "flex gap-1",
52
+ children: strengthBars.map((active, i)=>/*#__PURE__*/ _jsx("div", {
53
+ className: `h-1 flex-1 rounded-full ${active ? 'bg-[#D5E855]' : 'bg-gray-200'}`
54
+ }, i))
55
+ }),
56
+ password.length > 0 && !strength.isValid && /*#__PURE__*/ _jsx("p", {
57
+ className: "text-xs text-gray-500",
58
+ children: t.passwordRequirements
59
+ }),
60
+ /*#__PURE__*/ _jsx(Input, {
61
+ type: showPassword ? 'text' : 'password',
62
+ label: t.confirmPasswordLabel,
63
+ value: confirmPassword,
64
+ onValueChange: setConfirmPassword,
65
+ isRequired: true
66
+ }),
67
+ /*#__PURE__*/ _jsx(Button, {
68
+ type: "submit",
69
+ variant: "primary",
70
+ isLoading: isLoading,
71
+ children: t.setPassword
72
+ })
73
+ ]
74
+ });
75
+ }
@@ -0,0 +1,13 @@
1
+ import { type DeepPartial, type UiTranslations } from '../ui/translations';
2
+ export interface SignupFormProps {
3
+ onSignup: (data: {
4
+ name: string;
5
+ email: string;
6
+ }) => Promise<void>;
7
+ showGoogleOAuth?: boolean;
8
+ loginUrl?: string;
9
+ verifyOtpUrl?: string;
10
+ locale?: string;
11
+ messages?: Record<string, DeepPartial<UiTranslations>>;
12
+ }
13
+ export declare function SignupForm({ onSignup, showGoogleOAuth, loginUrl, verifyOtpUrl, locale, messages, }: SignupFormProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,152 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import React from 'react';
4
+ import { Button, Input, Divider } from '../ui/index.js';
5
+ import { getUiTranslations } from '../ui/translations.js';
6
+ export function SignupForm({ onSignup, showGoogleOAuth = true, loginUrl = '/login', verifyOtpUrl = '/verify-otp', locale, messages }) {
7
+ const [name, setName] = React.useState('');
8
+ const [email, setEmail] = React.useState('');
9
+ const [isLoading, setIsLoading] = React.useState(false);
10
+ const [error, setError] = React.useState(null);
11
+ const translations = getUiTranslations(locale, messages);
12
+ const t = translations.signup;
13
+ const errors = translations.errors;
14
+ // Helper to translate error keys
15
+ const getErrorMessage = (err)=>{
16
+ if (!err) return null;
17
+ if (err in errors) return errors[err];
18
+ return err;
19
+ };
20
+ const handleSubmit = async (e)=>{
21
+ e.preventDefault();
22
+ setIsLoading(true);
23
+ setError(null);
24
+ try {
25
+ await onSignup({
26
+ name,
27
+ email
28
+ });
29
+ window.location.href = `${verifyOtpUrl}?email=${encodeURIComponent(email)}&purpose=signup`;
30
+ } catch (err) {
31
+ setError(err.message || 'Signup failed');
32
+ } finally{
33
+ setIsLoading(false);
34
+ }
35
+ };
36
+ return /*#__PURE__*/ _jsxs(_Fragment, {
37
+ children: [
38
+ showGoogleOAuth && /*#__PURE__*/ _jsxs(_Fragment, {
39
+ children: [
40
+ /*#__PURE__*/ _jsxs(Button, {
41
+ fullWidth: true,
42
+ variant: "bordered",
43
+ size: "lg",
44
+ className: "mb-4",
45
+ children: [
46
+ /*#__PURE__*/ _jsxs("svg", {
47
+ width: "20",
48
+ height: "20",
49
+ viewBox: "0 0 48 48",
50
+ children: [
51
+ /*#__PURE__*/ _jsx("path", {
52
+ fill: "#EA4335",
53
+ d: "M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"
54
+ }),
55
+ /*#__PURE__*/ _jsx("path", {
56
+ fill: "#4285F4",
57
+ d: "M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"
58
+ }),
59
+ /*#__PURE__*/ _jsx("path", {
60
+ fill: "#FBBC05",
61
+ d: "M10.53 28.59a14.5 14.5 0 0 1 0-9.18l-7.98-6.19a24.01 24.01 0 0 0 0 21.56l7.98-6.19z"
62
+ }),
63
+ /*#__PURE__*/ _jsx("path", {
64
+ fill: "#34A853",
65
+ d: "M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"
66
+ })
67
+ ]
68
+ }),
69
+ t.continueWithGoogle
70
+ ]
71
+ }),
72
+ /*#__PURE__*/ _jsxs("div", {
73
+ className: "flex items-center gap-4 my-4",
74
+ children: [
75
+ /*#__PURE__*/ _jsx(Divider, {
76
+ className: "flex-1"
77
+ }),
78
+ /*#__PURE__*/ _jsx("span", {
79
+ className: "text-gray-500 text-sm",
80
+ children: t.or
81
+ }),
82
+ /*#__PURE__*/ _jsx(Divider, {
83
+ className: "flex-1"
84
+ })
85
+ ]
86
+ })
87
+ ]
88
+ }),
89
+ /*#__PURE__*/ _jsxs("form", {
90
+ onSubmit: handleSubmit,
91
+ className: "space-y-4",
92
+ children: [
93
+ error && /*#__PURE__*/ _jsx("div", {
94
+ className: "bg-red-50 text-red-600 border border-red-200 rounded-lg p-3 text-sm animate-[fadeIn_0.2s_ease-out]",
95
+ children: getErrorMessage(error)
96
+ }),
97
+ /*#__PURE__*/ _jsx(Input, {
98
+ label: t.fullNameLabel,
99
+ value: name,
100
+ onValueChange: setName,
101
+ isRequired: true
102
+ }),
103
+ /*#__PURE__*/ _jsx(Input, {
104
+ type: "email",
105
+ label: t.emailLabel,
106
+ value: email,
107
+ onValueChange: setEmail,
108
+ isRequired: true
109
+ }),
110
+ /*#__PURE__*/ _jsxs("p", {
111
+ className: "text-xs text-gray-600 text-center",
112
+ children: [
113
+ t.termsNotice,
114
+ ' ',
115
+ /*#__PURE__*/ _jsx("a", {
116
+ href: "/terms",
117
+ className: "text-gray-900 hover:underline",
118
+ children: t.termsLink
119
+ }),
120
+ " ",
121
+ t.andSeparator,
122
+ ' ',
123
+ /*#__PURE__*/ _jsx("a", {
124
+ href: "/privacy",
125
+ className: "text-gray-900 hover:underline",
126
+ children: t.privacyLink
127
+ })
128
+ ]
129
+ }),
130
+ /*#__PURE__*/ _jsx(Button, {
131
+ type: "submit",
132
+ variant: "primary",
133
+ isLoading: isLoading,
134
+ children: t.createAccount
135
+ })
136
+ ]
137
+ }),
138
+ /*#__PURE__*/ _jsxs("p", {
139
+ className: "text-center text-gray-600 text-sm mt-6",
140
+ children: [
141
+ t.haveAccount,
142
+ ' ',
143
+ /*#__PURE__*/ _jsx("a", {
144
+ href: loginUrl,
145
+ className: "text-gray-900 font-medium hover:underline",
146
+ children: t.loginLink
147
+ })
148
+ ]
149
+ })
150
+ ]
151
+ });
152
+ }
@@ -0,0 +1,9 @@
1
+ import { type DeepPartial, type UiTranslations } from '../ui/translations';
2
+ export interface VerifyOtpFormProps {
3
+ loginUrl?: string;
4
+ locale?: string;
5
+ messages?: Record<string, DeepPartial<UiTranslations>>;
6
+ /** Callback to update the card title based on purpose */
7
+ onPurposeChange?: (purpose: 'login' | 'signup' | 'password-reset', title: string, subtitle: string) => void;
8
+ }
9
+ export declare function VerifyOtpForm(props: VerifyOtpFormProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,107 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import React, { Suspense } from 'react';
4
+ import { useSearchParams } from 'next/navigation';
5
+ import { Button, OtpInput, Spinner } from '../ui/index.js';
6
+ import { useVerifyOtpFlow } from '../../auth/application/hooks/useVerifyOtpFlow.js';
7
+ import { getUiTranslations } from '../ui/translations.js';
8
+ function VerifyOtpFormContent({ loginUrl = '/login', locale, messages, onPurposeChange }) {
9
+ const searchParams = useSearchParams();
10
+ const email = searchParams.get('email') || '';
11
+ const purpose = searchParams.get('purpose') || 'login';
12
+ const redirectTo = searchParams.get('redirect') || '/';
13
+ const { otp, error, isLoading, isResending, resendCooldown, setOtp, handleSubmit, handleResendCode } = useVerifyOtpFlow({
14
+ email,
15
+ purpose,
16
+ redirectTo
17
+ });
18
+ const translations = getUiTranslations(locale, messages);
19
+ const t = translations.verifyOtp;
20
+ const errors = translations.errors;
21
+ // Helper to translate error keys
22
+ const getErrorMessage = (err)=>{
23
+ if (!err) return null;
24
+ if (err in errors) return errors[err];
25
+ return err;
26
+ };
27
+ const isPasswordReset = purpose === 'password-reset';
28
+ // Notify parent of purpose for dynamic title/subtitle
29
+ React.useEffect(()=>{
30
+ if (onPurposeChange) {
31
+ const title = isPasswordReset ? t.passwordResetTitle : t.title;
32
+ const subtitle = isPasswordReset ? t.passwordResetSubtitle : t.subtitle;
33
+ onPurposeChange(purpose, title, subtitle);
34
+ }
35
+ }, [
36
+ purpose,
37
+ onPurposeChange,
38
+ t,
39
+ isPasswordReset
40
+ ]);
41
+ if (!email) return null;
42
+ return /*#__PURE__*/ _jsxs("div", {
43
+ className: "flex flex-col items-center gap-4",
44
+ children: [
45
+ /*#__PURE__*/ _jsx("p", {
46
+ className: "text-gray-700 text-sm font-medium",
47
+ children: email
48
+ }),
49
+ /*#__PURE__*/ _jsx(OtpInput, {
50
+ value: otp,
51
+ onValueChange: setOtp,
52
+ isDisabled: isLoading,
53
+ autoFocus: true
54
+ }),
55
+ error && /*#__PURE__*/ _jsx("div", {
56
+ className: "w-full bg-red-50 text-red-600 border border-red-200 rounded-lg p-3 text-sm animate-[fadeIn_0.2s_ease-out]",
57
+ children: getErrorMessage(error)
58
+ }),
59
+ /*#__PURE__*/ _jsx(Button, {
60
+ variant: "primary",
61
+ isLoading: isLoading,
62
+ isDisabled: otp.length !== 6,
63
+ onPress: handleSubmit,
64
+ children: t.verify
65
+ }),
66
+ /*#__PURE__*/ _jsxs("div", {
67
+ className: "text-center",
68
+ children: [
69
+ /*#__PURE__*/ _jsx("p", {
70
+ className: "text-gray-600 text-sm mb-2",
71
+ children: t.noCodeReceived
72
+ }),
73
+ /*#__PURE__*/ _jsx("button", {
74
+ onClick: handleResendCode,
75
+ disabled: isResending || resendCooldown > 0,
76
+ className: "text-[#0071e3] font-medium text-sm hover:underline disabled:opacity-50 disabled:cursor-not-allowed",
77
+ children: isResending ? t.resending : resendCooldown > 0 ? t.resendIn.replace('{seconds}', String(resendCooldown)) : t.resendCode
78
+ })
79
+ ]
80
+ }),
81
+ /*#__PURE__*/ _jsx("div", {
82
+ className: "text-center mt-2",
83
+ children: /*#__PURE__*/ _jsxs("a", {
84
+ href: loginUrl,
85
+ className: "text-sm text-gray-600 hover:text-gray-900 flex items-center gap-1 justify-center",
86
+ children: [
87
+ "← ",
88
+ t.backToLogin
89
+ ]
90
+ })
91
+ })
92
+ ]
93
+ });
94
+ }
95
+ export function VerifyOtpForm(props) {
96
+ return /*#__PURE__*/ _jsx(Suspense, {
97
+ fallback: /*#__PURE__*/ _jsx("div", {
98
+ className: "flex items-center justify-center py-8",
99
+ children: /*#__PURE__*/ _jsx(Spinner, {
100
+ size: "lg"
101
+ })
102
+ }),
103
+ children: /*#__PURE__*/ _jsx(VerifyOtpFormContent, {
104
+ ...props
105
+ })
106
+ });
107
+ }
@@ -0,0 +1,19 @@
1
+ import { LoginForm } from './LoginForm';
2
+ import { SignupForm } from './SignupForm';
3
+ import { ForgotPasswordForm } from './ForgotPasswordForm';
4
+ import { VerifyOtpForm } from './VerifyOtpForm';
5
+ import { SetPasswordForm } from './SetPasswordForm';
6
+ export { LoginForm, type LoginFormProps } from './LoginForm';
7
+ export { SignupForm, type SignupFormProps } from './SignupForm';
8
+ export { ForgotPasswordForm, type ForgotPasswordFormProps } from './ForgotPasswordForm';
9
+ export { VerifyOtpForm, type VerifyOtpFormProps } from './VerifyOtpForm';
10
+ export { SetPasswordForm, type SetPasswordFormProps } from './SetPasswordForm';
11
+ export type AuthFormSlug = 'login' | 'signup' | 'forgot-password' | 'verify-otp' | 'set-password';
12
+ export declare const AUTH_FORMS: {
13
+ readonly login: typeof LoginForm;
14
+ readonly signup: typeof SignupForm;
15
+ readonly 'forgot-password': typeof ForgotPasswordForm;
16
+ readonly 'verify-otp': typeof VerifyOtpForm;
17
+ readonly 'set-password': typeof SetPasswordForm;
18
+ };
19
+ export declare function getFormBySlug(slug: string | string[] | undefined): React.ComponentType<any>;
@@ -0,0 +1,21 @@
1
+ import { LoginForm } from './LoginForm.js';
2
+ import { SignupForm } from './SignupForm.js';
3
+ import { ForgotPasswordForm } from './ForgotPasswordForm.js';
4
+ import { VerifyOtpForm } from './VerifyOtpForm.js';
5
+ import { SetPasswordForm } from './SetPasswordForm.js';
6
+ export { LoginForm } from './LoginForm.js';
7
+ export { SignupForm } from './SignupForm.js';
8
+ export { ForgotPasswordForm } from './ForgotPasswordForm.js';
9
+ export { VerifyOtpForm } from './VerifyOtpForm.js';
10
+ export { SetPasswordForm } from './SetPasswordForm.js';
11
+ export const AUTH_FORMS = {
12
+ 'login': LoginForm,
13
+ 'signup': SignupForm,
14
+ 'forgot-password': ForgotPasswordForm,
15
+ 'verify-otp': VerifyOtpForm,
16
+ 'set-password': SetPasswordForm
17
+ };
18
+ export function getFormBySlug(slug) {
19
+ const normalizedSlug = Array.isArray(slug) ? slug[0] : slug;
20
+ return AUTH_FORMS[normalizedSlug] ?? LoginForm;
21
+ }
@@ -1,5 +1,6 @@
1
1
  import type { AuthLayoutConfig } from '../AuthLayout';
2
- export interface ForgotPasswordPageProps extends AuthLayoutConfig {
2
+ import type { AuthCardConfig } from '../AuthCard';
3
+ export interface ForgotPasswordPageProps extends AuthLayoutConfig, AuthCardConfig {
3
4
  loginUrl?: string;
4
5
  }
5
6
  export default function ForgotPasswordPage(props: ForgotPasswordPageProps): import("react/jsx-runtime").JSX.Element;