@liedsonc/core-auth-kit 2.1.3 → 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.
Files changed (101) hide show
  1. package/README.md +802 -0
  2. package/dist/components/auth-card.d.ts +10 -0
  3. package/dist/components/auth-card.d.ts.map +1 -0
  4. package/dist/components/auth-card.js +10 -0
  5. package/dist/components/auth-form.d.ts +10 -0
  6. package/dist/components/auth-form.d.ts.map +1 -0
  7. package/dist/components/auth-form.js +21 -0
  8. package/dist/components/error-message.d.ts +6 -0
  9. package/dist/components/error-message.d.ts.map +1 -0
  10. package/dist/components/error-message.js +8 -0
  11. package/dist/components/form-field.d.ts +9 -0
  12. package/dist/components/form-field.d.ts.map +1 -0
  13. package/dist/components/form-field.js +7 -0
  14. package/dist/components/index.d.ts +9 -0
  15. package/dist/components/index.d.ts.map +1 -0
  16. package/{components/index.ts → dist/components/index.js} +8 -8
  17. package/dist/components/loading-spinner.d.ts +5 -0
  18. package/dist/components/loading-spinner.d.ts.map +1 -0
  19. package/dist/components/loading-spinner.js +6 -0
  20. package/dist/components/oauth-buttons.d.ts +11 -0
  21. package/dist/components/oauth-buttons.d.ts.map +1 -0
  22. package/dist/components/oauth-buttons.js +14 -0
  23. package/dist/components/password-input.d.ts +8 -0
  24. package/dist/components/password-input.d.ts.map +1 -0
  25. package/dist/components/password-input.js +36 -0
  26. package/dist/components/success-message.d.ts +6 -0
  27. package/dist/components/success-message.d.ts.map +1 -0
  28. package/dist/components/success-message.js +8 -0
  29. package/dist/components/ui/button.d.ts +12 -0
  30. package/dist/components/ui/button.d.ts.map +1 -0
  31. package/dist/components/ui/button.js +33 -0
  32. package/dist/components/ui/card.d.ts +9 -0
  33. package/dist/components/ui/card.d.ts.map +1 -0
  34. package/dist/components/ui/card.js +16 -0
  35. package/dist/components/ui/input.d.ts +4 -0
  36. package/dist/components/ui/input.d.ts.map +1 -0
  37. package/dist/components/ui/input.js +8 -0
  38. package/dist/components/ui/label.d.ts +6 -0
  39. package/dist/components/ui/label.d.ts.map +1 -0
  40. package/dist/components/ui/label.js +10 -0
  41. package/dist/context.d.ts +8 -0
  42. package/dist/context.d.ts.map +1 -0
  43. package/dist/context.js +13 -0
  44. package/dist/hooks/use-auth.d.ts +22 -0
  45. package/dist/hooks/use-auth.d.ts.map +1 -0
  46. package/dist/hooks/use-auth.js +112 -0
  47. package/dist/hooks/use-oauth.d.ts +6 -0
  48. package/dist/hooks/use-oauth.d.ts.map +1 -0
  49. package/dist/hooks/use-oauth.js +18 -0
  50. package/dist/index.d.ts +7 -0
  51. package/dist/index.d.ts.map +1 -0
  52. package/dist/index.js +5 -0
  53. package/dist/pages/forgot-password/index.d.ts +2 -0
  54. package/dist/pages/forgot-password/index.d.ts.map +1 -0
  55. package/dist/pages/forgot-password/index.js +30 -0
  56. package/dist/pages/index.d.ts +6 -0
  57. package/dist/pages/index.d.ts.map +1 -0
  58. package/{pages/index.ts → dist/pages/index.js} +5 -5
  59. package/dist/pages/login/index.d.ts +2 -0
  60. package/dist/pages/login/index.d.ts.map +1 -0
  61. package/dist/pages/login/index.js +45 -0
  62. package/dist/pages/register/index.d.ts +2 -0
  63. package/dist/pages/register/index.d.ts.map +1 -0
  64. package/dist/pages/register/index.js +59 -0
  65. package/dist/pages/reset-password/index.d.ts +2 -0
  66. package/dist/pages/reset-password/index.d.ts.map +1 -0
  67. package/dist/pages/reset-password/index.js +48 -0
  68. package/dist/pages/verify-email/index.d.ts +2 -0
  69. package/dist/pages/verify-email/index.d.ts.map +1 -0
  70. package/dist/pages/verify-email/index.js +47 -0
  71. package/dist/tsconfig.build.tsbuildinfo +1 -0
  72. package/dist/types/index.d.ts +60 -0
  73. package/dist/types/index.d.ts.map +1 -0
  74. package/dist/types/index.js +1 -0
  75. package/dist/utils.d.ts +3 -0
  76. package/dist/utils.d.ts.map +1 -0
  77. package/dist/utils.js +5 -0
  78. package/package.json +15 -11
  79. package/components/auth-card.tsx +0 -49
  80. package/components/auth-form.tsx +0 -53
  81. package/components/error-message.tsx +0 -24
  82. package/components/form-field.tsx +0 -39
  83. package/components/loading-spinner.tsx +0 -19
  84. package/components/oauth-buttons.tsx +0 -49
  85. package/components/password-input.tsx +0 -93
  86. package/components/success-message.tsx +0 -24
  87. package/components/ui/button.tsx +0 -52
  88. package/components/ui/card.tsx +0 -75
  89. package/components/ui/input.tsx +0 -21
  90. package/components/ui/label.tsx +0 -25
  91. package/context.tsx +0 -26
  92. package/hooks/use-auth.ts +0 -131
  93. package/hooks/use-oauth.ts +0 -25
  94. package/index.ts +0 -28
  95. package/pages/forgot-password/index.tsx +0 -83
  96. package/pages/login/index.tsx +0 -119
  97. package/pages/register/index.tsx +0 -149
  98. package/pages/reset-password/index.tsx +0 -133
  99. package/pages/verify-email/index.tsx +0 -143
  100. package/types/index.ts +0 -34
  101. 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
- }
@@ -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
- }
@@ -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
- }
@@ -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
- }