@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.
- package/README.md +54 -7
- package/dist/components/AuthCard.d.ts +41 -0
- package/dist/components/AuthCard.js +28 -0
- package/dist/components/AuthCardServer.d.ts +29 -0
- package/dist/components/AuthCardServer.js +57 -0
- package/dist/components/AuthLayout.d.ts +23 -13
- package/dist/components/AuthLayout.js +26 -87
- package/dist/components/AuthPages.d.ts +4 -29
- package/dist/components/AuthPages.js +23 -105
- package/dist/components/AuthPagesServer.d.ts +3 -4
- package/dist/components/AuthPagesServer.js +15 -8
- package/dist/components/auth-card/AuthCardShell.d.ts +21 -0
- package/dist/components/auth-card/AuthCardShell.js +104 -0
- package/dist/components/auth-card/FormRenderer.d.ts +32 -0
- package/dist/components/auth-card/FormRenderer.js +88 -0
- package/dist/components/auth-card/formConfigs.d.ts +25 -0
- package/dist/components/auth-card/formConfigs.js +56 -0
- package/dist/components/auth-card/index.d.ts +3 -0
- package/dist/components/auth-card/index.js +3 -0
- package/dist/components/forms/ForgotPasswordForm.d.ts +7 -0
- package/dist/components/forms/ForgotPasswordForm.js +56 -0
- package/dist/components/forms/LoginForm.d.ts +15 -0
- package/dist/components/forms/LoginForm.js +247 -0
- package/dist/components/forms/SetPasswordForm.d.ts +7 -0
- package/dist/components/forms/SetPasswordForm.js +75 -0
- package/dist/components/forms/SignupForm.d.ts +13 -0
- package/dist/components/forms/SignupForm.js +152 -0
- package/dist/components/forms/VerifyOtpForm.d.ts +9 -0
- package/dist/components/forms/VerifyOtpForm.js +107 -0
- package/dist/components/forms/index.d.ts +19 -0
- package/dist/components/forms/index.js +21 -0
- package/dist/components/pages/ForgotPasswordPage.d.ts +2 -1
- package/dist/components/pages/ForgotPasswordPageHero.d.ts +3 -2
- package/dist/components/pages/ForgotPasswordPageHero.js +70 -64
- package/dist/components/pages/ForgotPasswordPageTailwind.d.ts +3 -2
- package/dist/components/pages/ForgotPasswordPageTailwind.js +42 -36
- package/dist/components/pages/LoginPage.d.ts +2 -1
- package/dist/components/pages/LoginPageHero.d.ts +2 -1
- package/dist/components/pages/LoginPageHero.js +297 -291
- package/dist/components/pages/LoginPageTailwind.d.ts +2 -1
- package/dist/components/pages/LoginPageTailwind.js +197 -191
- package/dist/components/pages/SetPasswordPage.d.ts +2 -1
- package/dist/components/pages/SetPasswordPageHero.d.ts +3 -2
- package/dist/components/pages/SetPasswordPageHero.js +98 -92
- package/dist/components/pages/SetPasswordPageTailwind.d.ts +3 -2
- package/dist/components/pages/SetPasswordPageTailwind.js +63 -57
- package/dist/components/pages/SignupPage.d.ts +2 -1
- package/dist/components/pages/SignupPageHero.d.ts +3 -2
- package/dist/components/pages/SignupPageHero.js +139 -133
- package/dist/components/pages/SignupPageTailwind.d.ts +3 -2
- package/dist/components/pages/SignupPageTailwind.js +123 -117
- package/dist/components/pages/VerifyOtpPage.d.ts +2 -1
- package/dist/components/pages/VerifyOtpPageHero.d.ts +2 -1
- package/dist/components/pages/VerifyOtpPageHero.js +110 -104
- package/dist/components/pages/VerifyOtpPageTailwind.d.ts +2 -1
- package/dist/components/pages/VerifyOtpPageTailwind.js +60 -54
- package/dist/components/ui/translations.d.ts +9 -0
- package/dist/components/ui/translations.js +18 -0
- package/dist/config.d.ts +12 -0
- package/dist/config.js +11 -0
- package/dist/exports/client.d.ts +5 -0
- package/dist/exports/client.js +5 -0
- package/dist/exports/rsc.d.ts +7 -0
- package/dist/exports/rsc.js +8 -0
- package/dist/index.js +12 -2
- 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
|
-
|
|
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;
|