@liedsonc/core-auth-kit 2.1.4 → 2.1.6
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 +802 -0
- package/dist/components/auth-card.d.ts +10 -0
- package/dist/components/auth-card.d.ts.map +1 -0
- package/dist/components/auth-card.js +10 -0
- package/dist/components/auth-form.d.ts +10 -0
- package/dist/components/auth-form.d.ts.map +1 -0
- package/dist/components/auth-form.js +21 -0
- package/dist/components/error-message.d.ts +6 -0
- package/dist/components/error-message.d.ts.map +1 -0
- package/dist/components/error-message.js +8 -0
- package/dist/components/form-field.d.ts +9 -0
- package/dist/components/form-field.d.ts.map +1 -0
- package/dist/components/form-field.js +7 -0
- package/dist/components/index.d.ts +9 -0
- package/dist/components/index.d.ts.map +1 -0
- package/{components/index.ts → dist/components/index.js} +8 -8
- package/dist/components/loading-spinner.d.ts +5 -0
- package/dist/components/loading-spinner.d.ts.map +1 -0
- package/dist/components/loading-spinner.js +6 -0
- package/dist/components/oauth-buttons.d.ts +11 -0
- package/dist/components/oauth-buttons.d.ts.map +1 -0
- package/dist/components/oauth-buttons.js +14 -0
- package/dist/components/password-input.d.ts +8 -0
- package/dist/components/password-input.d.ts.map +1 -0
- package/dist/components/password-input.js +36 -0
- package/dist/components/success-message.d.ts +6 -0
- package/dist/components/success-message.d.ts.map +1 -0
- package/dist/components/success-message.js +8 -0
- package/dist/components/ui/button.d.ts +12 -0
- package/dist/components/ui/button.d.ts.map +1 -0
- package/dist/components/ui/button.js +33 -0
- package/dist/components/ui/card.d.ts +9 -0
- package/dist/components/ui/card.d.ts.map +1 -0
- package/dist/components/ui/card.js +16 -0
- package/dist/components/ui/input.d.ts +4 -0
- package/dist/components/ui/input.d.ts.map +1 -0
- package/dist/components/ui/input.js +8 -0
- package/dist/components/ui/label.d.ts +6 -0
- package/dist/components/ui/label.d.ts.map +1 -0
- package/dist/components/ui/label.js +10 -0
- package/dist/context.d.ts +8 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +13 -0
- package/dist/hooks/use-auth.d.ts +22 -0
- package/dist/hooks/use-auth.d.ts.map +1 -0
- package/dist/hooks/use-auth.js +112 -0
- package/dist/hooks/use-oauth.d.ts +6 -0
- package/dist/hooks/use-oauth.d.ts.map +1 -0
- package/dist/hooks/use-oauth.js +18 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/pages/forgot-password/index.d.ts +2 -0
- package/dist/pages/forgot-password/index.d.ts.map +1 -0
- package/dist/pages/forgot-password/index.js +30 -0
- package/dist/pages/index.d.ts +6 -0
- package/dist/pages/index.d.ts.map +1 -0
- package/{pages/index.ts → dist/pages/index.js} +5 -5
- package/dist/pages/login/index.d.ts +2 -0
- package/dist/pages/login/index.d.ts.map +1 -0
- package/dist/pages/login/index.js +45 -0
- package/dist/pages/register/index.d.ts +2 -0
- package/dist/pages/register/index.d.ts.map +1 -0
- package/dist/pages/register/index.js +59 -0
- package/dist/pages/reset-password/index.d.ts +2 -0
- package/dist/pages/reset-password/index.d.ts.map +1 -0
- package/dist/pages/reset-password/index.js +48 -0
- package/dist/pages/verify-email/index.d.ts +2 -0
- package/dist/pages/verify-email/index.d.ts.map +1 -0
- package/dist/pages/verify-email/index.js +47 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/dist/types/index.d.ts +60 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +1 -0
- package/dist/utils.d.ts +3 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +5 -0
- package/package.json +15 -11
- package/components/auth-card.tsx +0 -49
- package/components/auth-form.tsx +0 -53
- package/components/error-message.tsx +0 -24
- package/components/form-field.tsx +0 -39
- package/components/loading-spinner.tsx +0 -19
- package/components/oauth-buttons.tsx +0 -49
- package/components/password-input.tsx +0 -93
- package/components/success-message.tsx +0 -24
- package/components/ui/button.tsx +0 -52
- package/components/ui/card.tsx +0 -75
- package/components/ui/input.tsx +0 -21
- package/components/ui/label.tsx +0 -25
- package/context.tsx +0 -26
- package/hooks/use-auth.ts +0 -131
- package/hooks/use-oauth.ts +0 -25
- package/index.ts +0 -28
- package/pages/forgot-password/index.tsx +0 -83
- package/pages/login/index.tsx +0 -119
- package/pages/register/index.tsx +0 -149
- package/pages/reset-password/index.tsx +0 -133
- package/pages/verify-email/index.tsx +0 -143
- package/types/index.ts +0 -34
- package/utils.ts +0 -6
package/context.tsx
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import * as React from "react";
|
|
4
|
-
import type { AuthUIConfig } from "./types";
|
|
5
|
-
|
|
6
|
-
const AuthUIConfigContext = React.createContext<AuthUIConfig | null>(null);
|
|
7
|
-
|
|
8
|
-
export function AuthUIProvider({
|
|
9
|
-
config,
|
|
10
|
-
children,
|
|
11
|
-
}: {
|
|
12
|
-
config: AuthUIConfig;
|
|
13
|
-
children: React.ReactNode;
|
|
14
|
-
}) {
|
|
15
|
-
return (
|
|
16
|
-
<AuthUIConfigContext.Provider value={config}>
|
|
17
|
-
{children}
|
|
18
|
-
</AuthUIConfigContext.Provider>
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function useAuthUIConfig(): AuthUIConfig {
|
|
23
|
-
const ctx = React.useContext(AuthUIConfigContext);
|
|
24
|
-
if (!ctx) throw new Error("useAuthUIConfig must be used within AuthUIProvider");
|
|
25
|
-
return ctx;
|
|
26
|
-
}
|
package/hooks/use-auth.ts
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { useCallback, useState } from "react";
|
|
4
|
-
import { useAuthUIConfig } from "../context";
|
|
5
|
-
import type { AuthError, AuthSession } from "../types";
|
|
6
|
-
|
|
7
|
-
type AuthResult = { success: true } | { success: false; error: AuthError };
|
|
8
|
-
|
|
9
|
-
export function useAuth() {
|
|
10
|
-
const { authClient, redirectAfterLogin, redirectAfterRegister, redirectAfterReset } =
|
|
11
|
-
useAuthUIConfig();
|
|
12
|
-
const [loading, setLoading] = useState(false);
|
|
13
|
-
const [error, setError] = useState<AuthError | null>(null);
|
|
14
|
-
const [session, setSession] = useState<AuthSession | null>(null);
|
|
15
|
-
|
|
16
|
-
const login = useCallback(
|
|
17
|
-
async (email: string, password: string): Promise<AuthResult> => {
|
|
18
|
-
setLoading(true);
|
|
19
|
-
setError(null);
|
|
20
|
-
try {
|
|
21
|
-
const result = await authClient.login(email, password);
|
|
22
|
-
if (result.success) {
|
|
23
|
-
if (redirectAfterLogin && typeof window !== "undefined") {
|
|
24
|
-
window.location.href = redirectAfterLogin;
|
|
25
|
-
}
|
|
26
|
-
return result;
|
|
27
|
-
}
|
|
28
|
-
setError(result.error);
|
|
29
|
-
return result;
|
|
30
|
-
} finally {
|
|
31
|
-
setLoading(false);
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
[authClient, redirectAfterLogin]
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
const register = useCallback(
|
|
38
|
-
async (email: string, password: string): Promise<AuthResult> => {
|
|
39
|
-
setLoading(true);
|
|
40
|
-
setError(null);
|
|
41
|
-
try {
|
|
42
|
-
const result = await authClient.register(email, password);
|
|
43
|
-
if (result.success && redirectAfterRegister && typeof window !== "undefined") {
|
|
44
|
-
window.location.href = redirectAfterRegister;
|
|
45
|
-
}
|
|
46
|
-
if (!result.success) setError(result.error);
|
|
47
|
-
return result;
|
|
48
|
-
} finally {
|
|
49
|
-
setLoading(false);
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
[authClient, redirectAfterRegister]
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
const logout = useCallback(async () => {
|
|
56
|
-
setLoading(true);
|
|
57
|
-
try {
|
|
58
|
-
await authClient.logout();
|
|
59
|
-
setSession(null);
|
|
60
|
-
} finally {
|
|
61
|
-
setLoading(false);
|
|
62
|
-
}
|
|
63
|
-
}, [authClient]);
|
|
64
|
-
|
|
65
|
-
const forgotPassword = useCallback(
|
|
66
|
-
async (email: string): Promise<AuthResult> => {
|
|
67
|
-
setLoading(true);
|
|
68
|
-
setError(null);
|
|
69
|
-
try {
|
|
70
|
-
const result = await authClient.forgotPassword(email);
|
|
71
|
-
if (!result.success) setError(result.error);
|
|
72
|
-
return result;
|
|
73
|
-
} finally {
|
|
74
|
-
setLoading(false);
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
[authClient]
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
const resetPassword = useCallback(
|
|
81
|
-
async (token: string, newPassword: string): Promise<AuthResult> => {
|
|
82
|
-
setLoading(true);
|
|
83
|
-
setError(null);
|
|
84
|
-
try {
|
|
85
|
-
const result = await authClient.resetPassword(token, newPassword);
|
|
86
|
-
if (result.success && redirectAfterReset && typeof window !== "undefined") {
|
|
87
|
-
window.location.href = redirectAfterReset;
|
|
88
|
-
}
|
|
89
|
-
if (!result.success) setError(result.error);
|
|
90
|
-
return result;
|
|
91
|
-
} finally {
|
|
92
|
-
setLoading(false);
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
[authClient, redirectAfterReset]
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
const verifyEmail = useCallback(
|
|
99
|
-
async (token: string): Promise<AuthResult> => {
|
|
100
|
-
setLoading(true);
|
|
101
|
-
setError(null);
|
|
102
|
-
try {
|
|
103
|
-
return await authClient.verifyEmail(token);
|
|
104
|
-
} finally {
|
|
105
|
-
setLoading(false);
|
|
106
|
-
}
|
|
107
|
-
},
|
|
108
|
-
[authClient]
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
const loadSession = useCallback(async () => {
|
|
112
|
-
if (!authClient.getSession) return null;
|
|
113
|
-
const s = await authClient.getSession();
|
|
114
|
-
setSession(s);
|
|
115
|
-
return s;
|
|
116
|
-
}, [authClient]);
|
|
117
|
-
|
|
118
|
-
return {
|
|
119
|
-
login,
|
|
120
|
-
register,
|
|
121
|
-
logout,
|
|
122
|
-
forgotPassword,
|
|
123
|
-
resetPassword,
|
|
124
|
-
verifyEmail,
|
|
125
|
-
session,
|
|
126
|
-
loadSession,
|
|
127
|
-
loading,
|
|
128
|
-
error,
|
|
129
|
-
clearError: () => setError(null),
|
|
130
|
-
};
|
|
131
|
-
}
|
package/hooks/use-oauth.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { useCallback, useState } from "react";
|
|
4
|
-
import type { OAuthProvider } from "../types";
|
|
5
|
-
|
|
6
|
-
export function useOAuth(onRedirect: (provider: OAuthProvider) => string | Promise<string>) {
|
|
7
|
-
const [loadingProvider, setLoadingProvider] = useState<OAuthProvider | null>(null);
|
|
8
|
-
|
|
9
|
-
const signIn = useCallback(
|
|
10
|
-
async (provider: OAuthProvider) => {
|
|
11
|
-
setLoadingProvider(provider);
|
|
12
|
-
try {
|
|
13
|
-
const url = await onRedirect(provider);
|
|
14
|
-
if (typeof window !== "undefined" && url) {
|
|
15
|
-
window.location.href = url;
|
|
16
|
-
}
|
|
17
|
-
} finally {
|
|
18
|
-
setLoadingProvider(null);
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
[onRedirect]
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
return { signIn, loadingProvider };
|
|
25
|
-
}
|
package/index.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
export { AuthUIProvider, useAuthUIConfig } from "./context";
|
|
2
|
-
export {
|
|
3
|
-
AuthCard,
|
|
4
|
-
AuthForm,
|
|
5
|
-
ErrorMessage,
|
|
6
|
-
FormField,
|
|
7
|
-
LoadingSpinner,
|
|
8
|
-
OAuthButtons,
|
|
9
|
-
PasswordInput,
|
|
10
|
-
SuccessMessage,
|
|
11
|
-
} from "./components";
|
|
12
|
-
export { useAuth } from "./hooks/use-auth";
|
|
13
|
-
export { useOAuth } from "./hooks/use-oauth";
|
|
14
|
-
export {
|
|
15
|
-
LoginPage,
|
|
16
|
-
RegisterPage,
|
|
17
|
-
ForgotPasswordPage,
|
|
18
|
-
ResetPasswordPage,
|
|
19
|
-
VerifyEmailPage,
|
|
20
|
-
} from "./pages";
|
|
21
|
-
export type {
|
|
22
|
-
AuthClient,
|
|
23
|
-
AuthError,
|
|
24
|
-
AuthFormState,
|
|
25
|
-
AuthSession,
|
|
26
|
-
AuthUIConfig,
|
|
27
|
-
OAuthProvider,
|
|
28
|
-
} from "./types";
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import Link from "next/link";
|
|
4
|
-
import { useAuth } from "../../hooks/use-auth";
|
|
5
|
-
import { AuthCard } from "../../components/auth-card";
|
|
6
|
-
import { AuthForm } from "../../components/auth-form";
|
|
7
|
-
import { FormField } from "../../components/form-field";
|
|
8
|
-
import { SuccessMessage } from "../../components/success-message";
|
|
9
|
-
import { Button } from "../../components/ui/button";
|
|
10
|
-
import { Input } from "../../components/ui/input";
|
|
11
|
-
import { useState } from "react";
|
|
12
|
-
|
|
13
|
-
const successMessage =
|
|
14
|
-
"If an account exists for this email, you will receive a link to reset your password.";
|
|
15
|
-
|
|
16
|
-
export function ForgotPasswordPage() {
|
|
17
|
-
const { forgotPassword, loading, error, clearError } = useAuth();
|
|
18
|
-
const [email, setEmail] = useState("");
|
|
19
|
-
const [submitted, setSubmitted] = useState(false);
|
|
20
|
-
const [fieldError, setFieldError] = useState<string | undefined>();
|
|
21
|
-
|
|
22
|
-
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
|
23
|
-
e.preventDefault();
|
|
24
|
-
clearError();
|
|
25
|
-
setFieldError(undefined);
|
|
26
|
-
if (!email.trim()) {
|
|
27
|
-
setFieldError("Email is required.");
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
await forgotPassword(email.trim());
|
|
31
|
-
setSubmitted(true);
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
return (
|
|
35
|
-
<div className="flex min-h-screen flex-col items-center justify-center p-4">
|
|
36
|
-
<AuthCard
|
|
37
|
-
title="Forgot password"
|
|
38
|
-
subtitle="Enter your email and we'll send you a reset link"
|
|
39
|
-
footer={
|
|
40
|
-
<div className="flex w-full justify-center text-sm text-muted-foreground">
|
|
41
|
-
<Link
|
|
42
|
-
href="/login"
|
|
43
|
-
className="underline underline-offset-4 hover:text-foreground"
|
|
44
|
-
>
|
|
45
|
-
Back to sign in
|
|
46
|
-
</Link>
|
|
47
|
-
</div>
|
|
48
|
-
}
|
|
49
|
-
>
|
|
50
|
-
{submitted ? (
|
|
51
|
-
<SuccessMessage>{successMessage}</SuccessMessage>
|
|
52
|
-
) : (
|
|
53
|
-
<AuthForm onSubmit={handleSubmit} loading={loading}>
|
|
54
|
-
{error && (
|
|
55
|
-
<p role="alert" className="text-sm text-destructive">
|
|
56
|
-
Something went wrong. Please try again later.
|
|
57
|
-
</p>
|
|
58
|
-
)}
|
|
59
|
-
<FormField label="Email" htmlFor="forgot-email" error={fieldError} required>
|
|
60
|
-
<Input
|
|
61
|
-
id="forgot-email"
|
|
62
|
-
type="email"
|
|
63
|
-
autoComplete="email"
|
|
64
|
-
placeholder="you@example.com"
|
|
65
|
-
value={email}
|
|
66
|
-
onChange={(e) => setEmail(e.target.value)}
|
|
67
|
-
disabled={loading}
|
|
68
|
-
aria-invalid={!!fieldError}
|
|
69
|
-
/>
|
|
70
|
-
</FormField>
|
|
71
|
-
<Button type="submit" className="w-full" disabled={loading}>
|
|
72
|
-
{loading ? (
|
|
73
|
-
<span className="inline-block size-4 animate-spin rounded-full border-2 border-current border-t-transparent" />
|
|
74
|
-
) : (
|
|
75
|
-
"Send reset link"
|
|
76
|
-
)}
|
|
77
|
-
</Button>
|
|
78
|
-
</AuthForm>
|
|
79
|
-
)}
|
|
80
|
-
</AuthCard>
|
|
81
|
-
</div>
|
|
82
|
-
);
|
|
83
|
-
}
|
package/pages/login/index.tsx
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import Link from "next/link";
|
|
4
|
-
import { useAuth } from "../../hooks/use-auth";
|
|
5
|
-
import { useOAuth } from "../../hooks/use-oauth";
|
|
6
|
-
import { useAuthUIConfig } from "../../context";
|
|
7
|
-
import { AuthCard } from "../../components/auth-card";
|
|
8
|
-
import { AuthForm } from "../../components/auth-form";
|
|
9
|
-
import { FormField } from "../../components/form-field";
|
|
10
|
-
import { OAuthButtons } from "../../components/oauth-buttons";
|
|
11
|
-
import { PasswordInput } from "../../components/password-input";
|
|
12
|
-
import { Button } from "../../components/ui/button";
|
|
13
|
-
import { Input } from "../../components/ui/input";
|
|
14
|
-
import { useCallback, useState } from "react";
|
|
15
|
-
|
|
16
|
-
const genericError = "Invalid email or password. Please try again.";
|
|
17
|
-
|
|
18
|
-
export function LoginPage() {
|
|
19
|
-
const config = useAuthUIConfig();
|
|
20
|
-
const { login, loading, error, clearError } = useAuth();
|
|
21
|
-
const oauthProviders = config.oauthProviders ?? [];
|
|
22
|
-
const { signIn, loadingProvider } = useOAuth(
|
|
23
|
-
useCallback(
|
|
24
|
-
(provider) => {
|
|
25
|
-
const baseUrl = typeof window !== "undefined" ? window.location.origin : "";
|
|
26
|
-
return Promise.resolve(`${baseUrl}/api/auth/${provider}`);
|
|
27
|
-
},
|
|
28
|
-
[]
|
|
29
|
-
)
|
|
30
|
-
);
|
|
31
|
-
const [email, setEmail] = useState("");
|
|
32
|
-
const [password, setPassword] = useState("");
|
|
33
|
-
const [fieldErrors, setFieldErrors] = useState<{ email?: string; password?: string }>({});
|
|
34
|
-
|
|
35
|
-
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
|
36
|
-
e.preventDefault();
|
|
37
|
-
clearError();
|
|
38
|
-
setFieldErrors({});
|
|
39
|
-
const err: { email?: string; password?: string } = {};
|
|
40
|
-
if (!email.trim()) err.email = "Email is required.";
|
|
41
|
-
if (!password) err.password = "Password is required.";
|
|
42
|
-
if (Object.keys(err).length) {
|
|
43
|
-
setFieldErrors(err);
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
const result = await login(email.trim(), password);
|
|
47
|
-
if (!result.success) setFieldErrors({ password: genericError });
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
return (
|
|
51
|
-
<div className="flex min-h-screen flex-col items-center justify-center p-4">
|
|
52
|
-
<AuthCard
|
|
53
|
-
title="Sign in"
|
|
54
|
-
subtitle="Enter your credentials to continue"
|
|
55
|
-
footer={
|
|
56
|
-
<div className="flex w-full flex-col items-center gap-2 text-center text-sm text-muted-foreground">
|
|
57
|
-
<Link
|
|
58
|
-
href="/register"
|
|
59
|
-
className="underline underline-offset-4 hover:text-foreground"
|
|
60
|
-
>
|
|
61
|
-
Create an account
|
|
62
|
-
</Link>
|
|
63
|
-
<Link
|
|
64
|
-
href="/forgot-password"
|
|
65
|
-
className="underline underline-offset-4 hover:text-foreground"
|
|
66
|
-
>
|
|
67
|
-
Forgot password?
|
|
68
|
-
</Link>
|
|
69
|
-
</div>
|
|
70
|
-
}
|
|
71
|
-
>
|
|
72
|
-
<AuthForm onSubmit={handleSubmit} loading={loading} error={error ? genericError : undefined}>
|
|
73
|
-
<OAuthButtons
|
|
74
|
-
providers={oauthProviders}
|
|
75
|
-
loadingProvider={loadingProvider}
|
|
76
|
-
onSignIn={signIn}
|
|
77
|
-
/>
|
|
78
|
-
<div className="relative">
|
|
79
|
-
<div className="absolute inset-0 flex items-center">
|
|
80
|
-
<span className="w-full border-t border-border" />
|
|
81
|
-
</div>
|
|
82
|
-
<div className="relative flex justify-center text-xs uppercase">
|
|
83
|
-
<span className="bg-card px-2 text-muted-foreground">Or continue with email</span>
|
|
84
|
-
</div>
|
|
85
|
-
</div>
|
|
86
|
-
<FormField label="Email" htmlFor="login-email" error={fieldErrors.email} required>
|
|
87
|
-
<Input
|
|
88
|
-
id="login-email"
|
|
89
|
-
type="email"
|
|
90
|
-
autoComplete="email"
|
|
91
|
-
placeholder="you@example.com"
|
|
92
|
-
value={email}
|
|
93
|
-
onChange={(e) => setEmail(e.target.value)}
|
|
94
|
-
disabled={loading}
|
|
95
|
-
aria-invalid={!!fieldErrors.email}
|
|
96
|
-
/>
|
|
97
|
-
</FormField>
|
|
98
|
-
<FormField label="Password" htmlFor="login-password" error={fieldErrors.password} required>
|
|
99
|
-
<PasswordInput
|
|
100
|
-
id="login-password"
|
|
101
|
-
placeholder="••••••••"
|
|
102
|
-
value={password}
|
|
103
|
-
onChange={(e) => setPassword(e.target.value)}
|
|
104
|
-
disabled={loading}
|
|
105
|
-
aria-invalid={!!fieldErrors.password}
|
|
106
|
-
/>
|
|
107
|
-
</FormField>
|
|
108
|
-
<Button type="submit" className="w-full" disabled={loading}>
|
|
109
|
-
{loading ? (
|
|
110
|
-
<span className="inline-block size-4 animate-spin rounded-full border-2 border-current border-t-transparent" />
|
|
111
|
-
) : (
|
|
112
|
-
"Sign in"
|
|
113
|
-
)}
|
|
114
|
-
</Button>
|
|
115
|
-
</AuthForm>
|
|
116
|
-
</AuthCard>
|
|
117
|
-
</div>
|
|
118
|
-
);
|
|
119
|
-
}
|
package/pages/register/index.tsx
DELETED
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import Link from "next/link";
|
|
4
|
-
import { useAuth } from "../../hooks/use-auth";
|
|
5
|
-
import { useOAuth } from "../../hooks/use-oauth";
|
|
6
|
-
import { useAuthUIConfig } from "../../context";
|
|
7
|
-
import { AuthCard } from "../../components/auth-card";
|
|
8
|
-
import { AuthForm } from "../../components/auth-form";
|
|
9
|
-
import { FormField } from "../../components/form-field";
|
|
10
|
-
import { OAuthButtons } from "../../components/oauth-buttons";
|
|
11
|
-
import { PasswordInput } from "../../components/password-input";
|
|
12
|
-
import { SuccessMessage } from "../../components/success-message";
|
|
13
|
-
import { Button } from "../../components/ui/button";
|
|
14
|
-
import { Input } from "../../components/ui/input";
|
|
15
|
-
import { useCallback, useState } from "react";
|
|
16
|
-
|
|
17
|
-
const genericError = "Something went wrong. Please try again.";
|
|
18
|
-
const duplicateError = "An account with this email already exists.";
|
|
19
|
-
|
|
20
|
-
export function RegisterPage() {
|
|
21
|
-
const config = useAuthUIConfig();
|
|
22
|
-
const { register, loading, error, clearError } = useAuth();
|
|
23
|
-
const oauthProviders = config.oauthProviders ?? [];
|
|
24
|
-
const { signIn, loadingProvider } = useOAuth(
|
|
25
|
-
useCallback((provider) => {
|
|
26
|
-
const baseUrl = typeof window !== "undefined" ? window.location.origin : "";
|
|
27
|
-
return Promise.resolve(`${baseUrl}/api/auth/${provider}`);
|
|
28
|
-
}, [])
|
|
29
|
-
);
|
|
30
|
-
const [email, setEmail] = useState("");
|
|
31
|
-
const [password, setPassword] = useState("");
|
|
32
|
-
const [confirmPassword, setConfirmPassword] = useState("");
|
|
33
|
-
const [fieldErrors, setFieldErrors] = useState<{
|
|
34
|
-
email?: string;
|
|
35
|
-
password?: string;
|
|
36
|
-
confirm?: string;
|
|
37
|
-
}>({});
|
|
38
|
-
const [showVerificationMessage, setShowVerificationMessage] = useState(false);
|
|
39
|
-
|
|
40
|
-
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
|
41
|
-
e.preventDefault();
|
|
42
|
-
clearError();
|
|
43
|
-
setFieldErrors({});
|
|
44
|
-
setShowVerificationMessage(false);
|
|
45
|
-
const err: { email?: string; password?: string; confirm?: string } = {};
|
|
46
|
-
if (!email.trim()) err.email = "Email is required.";
|
|
47
|
-
if (!password) err.password = "Password is required.";
|
|
48
|
-
if (password.length < 8) err.password = "Password must be at least 8 characters.";
|
|
49
|
-
if (password !== confirmPassword) err.confirm = "Passwords do not match.";
|
|
50
|
-
if (Object.keys(err).length) {
|
|
51
|
-
setFieldErrors(err);
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
const result = await register(email.trim(), password);
|
|
55
|
-
if (result.success) {
|
|
56
|
-
setShowVerificationMessage(true);
|
|
57
|
-
} else {
|
|
58
|
-
const isDuplicate = result.error.code === "DUPLICATE_EMAIL" || result.error.code === "EMAIL_TAKEN";
|
|
59
|
-
setFieldErrors({ email: isDuplicate ? duplicateError : genericError });
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
return (
|
|
64
|
-
<div className="flex min-h-screen flex-col items-center justify-center p-4">
|
|
65
|
-
<AuthCard
|
|
66
|
-
title="Create an account"
|
|
67
|
-
subtitle="Enter your details to get started"
|
|
68
|
-
footer={
|
|
69
|
-
<div className="flex w-full justify-center text-sm text-muted-foreground">
|
|
70
|
-
<Link
|
|
71
|
-
href="/login"
|
|
72
|
-
className="underline underline-offset-4 hover:text-foreground"
|
|
73
|
-
>
|
|
74
|
-
Already have an account? Sign in
|
|
75
|
-
</Link>
|
|
76
|
-
</div>
|
|
77
|
-
}
|
|
78
|
-
>
|
|
79
|
-
{showVerificationMessage ? (
|
|
80
|
-
<SuccessMessage>
|
|
81
|
-
Check your email to verify your account. You can sign in after verification.
|
|
82
|
-
</SuccessMessage>
|
|
83
|
-
) : (
|
|
84
|
-
<AuthForm onSubmit={handleSubmit} loading={loading} error={error ? genericError : undefined}>
|
|
85
|
-
<OAuthButtons
|
|
86
|
-
providers={oauthProviders}
|
|
87
|
-
loadingProvider={loadingProvider}
|
|
88
|
-
onSignIn={signIn}
|
|
89
|
-
/>
|
|
90
|
-
<div className="relative">
|
|
91
|
-
<div className="absolute inset-0 flex items-center">
|
|
92
|
-
<span className="w-full border-t border-border" />
|
|
93
|
-
</div>
|
|
94
|
-
<div className="relative flex justify-center text-xs uppercase">
|
|
95
|
-
<span className="bg-card px-2 text-muted-foreground">Or continue with email</span>
|
|
96
|
-
</div>
|
|
97
|
-
</div>
|
|
98
|
-
<FormField label="Email" htmlFor="register-email" error={fieldErrors.email} required>
|
|
99
|
-
<Input
|
|
100
|
-
id="register-email"
|
|
101
|
-
type="email"
|
|
102
|
-
autoComplete="email"
|
|
103
|
-
placeholder="you@example.com"
|
|
104
|
-
value={email}
|
|
105
|
-
onChange={(e) => setEmail(e.target.value)}
|
|
106
|
-
disabled={loading}
|
|
107
|
-
aria-invalid={!!fieldErrors.email}
|
|
108
|
-
/>
|
|
109
|
-
</FormField>
|
|
110
|
-
<FormField label="Password" htmlFor="register-password" error={fieldErrors.password} required>
|
|
111
|
-
<PasswordInput
|
|
112
|
-
id="register-password"
|
|
113
|
-
placeholder="••••••••"
|
|
114
|
-
value={password}
|
|
115
|
-
onChange={(e) => setPassword(e.target.value)}
|
|
116
|
-
disabled={loading}
|
|
117
|
-
showStrength
|
|
118
|
-
aria-invalid={!!fieldErrors.password}
|
|
119
|
-
/>
|
|
120
|
-
</FormField>
|
|
121
|
-
<FormField
|
|
122
|
-
label="Confirm password"
|
|
123
|
-
htmlFor="register-confirm"
|
|
124
|
-
error={fieldErrors.confirm}
|
|
125
|
-
required
|
|
126
|
-
>
|
|
127
|
-
<PasswordInput
|
|
128
|
-
id="register-confirm"
|
|
129
|
-
placeholder="••••••••"
|
|
130
|
-
autoComplete="new-password"
|
|
131
|
-
value={confirmPassword}
|
|
132
|
-
onChange={(e) => setConfirmPassword(e.target.value)}
|
|
133
|
-
disabled={loading}
|
|
134
|
-
aria-invalid={!!fieldErrors.confirm}
|
|
135
|
-
/>
|
|
136
|
-
</FormField>
|
|
137
|
-
<Button type="submit" className="w-full" disabled={loading}>
|
|
138
|
-
{loading ? (
|
|
139
|
-
<span className="inline-block size-4 animate-spin rounded-full border-2 border-current border-t-transparent" />
|
|
140
|
-
) : (
|
|
141
|
-
"Create account"
|
|
142
|
-
)}
|
|
143
|
-
</Button>
|
|
144
|
-
</AuthForm>
|
|
145
|
-
)}
|
|
146
|
-
</AuthCard>
|
|
147
|
-
</div>
|
|
148
|
-
);
|
|
149
|
-
}
|