luxlabs 1.0.1 → 1.0.2

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 (60) hide show
  1. package/package.json +1 -1
  2. package/templates/interface-boilerplate/app/api/auth/[...all]/route.ts +52 -0
  3. package/templates/interface-boilerplate/app/auth/callback/page.tsx +22 -31
  4. package/templates/interface-boilerplate/app/dashboard/page.tsx +41 -214
  5. package/templates/interface-boilerplate/app/favicon.ico +0 -0
  6. package/templates/interface-boilerplate/app/globals.css +18 -113
  7. package/templates/interface-boilerplate/app/layout.tsx +21 -33
  8. package/templates/interface-boilerplate/app/page.tsx +37 -116
  9. package/templates/interface-boilerplate/app/settings/page.tsx +71 -0
  10. package/templates/interface-boilerplate/app/sign-in/page.tsx +19 -0
  11. package/templates/interface-boilerplate/app/sign-up/page.tsx +19 -0
  12. package/templates/interface-boilerplate/components/auth/sign-in-form.tsx +137 -0
  13. package/templates/interface-boilerplate/components/auth/sign-up-form.tsx +225 -0
  14. package/templates/interface-boilerplate/components/ui/badge.tsx +18 -14
  15. package/templates/interface-boilerplate/components/ui/button.tsx +29 -24
  16. package/templates/interface-boilerplate/components/ui/card.tsx +76 -57
  17. package/templates/interface-boilerplate/components/ui/input.tsx +8 -9
  18. package/templates/interface-boilerplate/eslint.config.mjs +18 -0
  19. package/templates/interface-boilerplate/lib/auth-client.ts +18 -0
  20. package/templates/interface-boilerplate/lib/auth.config.ts +111 -0
  21. package/templates/interface-boilerplate/lib/utils.ts +3 -3
  22. package/templates/interface-boilerplate/middleware.ts +60 -0
  23. package/templates/interface-boilerplate/next.config.ts +7 -0
  24. package/templates/interface-boilerplate/package-lock.json +6855 -0
  25. package/templates/interface-boilerplate/package.json +18 -37
  26. package/templates/interface-boilerplate/postcss.config.mjs +7 -0
  27. package/templates/interface-boilerplate/tsconfig.json +18 -4
  28. package/templates/interface-boilerplate/.env.example +0 -2
  29. package/templates/interface-boilerplate/.eslintrc.json +0 -4
  30. package/templates/interface-boilerplate/app/login/page.tsx +0 -178
  31. package/templates/interface-boilerplate/app/signup/page.tsx +0 -199
  32. package/templates/interface-boilerplate/components/AnalyticsProvider.tsx +0 -142
  33. package/templates/interface-boilerplate/components/AuthGuard.tsx +0 -76
  34. package/templates/interface-boilerplate/components/ErrorBoundary.tsx +0 -106
  35. package/templates/interface-boilerplate/components/theme-provider.tsx +0 -9
  36. package/templates/interface-boilerplate/components/theme-toggle.tsx +0 -39
  37. package/templates/interface-boilerplate/components/ui/avatar.tsx +0 -46
  38. package/templates/interface-boilerplate/components/ui/checkbox.tsx +0 -27
  39. package/templates/interface-boilerplate/components/ui/dialog.tsx +0 -100
  40. package/templates/interface-boilerplate/components/ui/dropdown-menu.tsx +0 -173
  41. package/templates/interface-boilerplate/components/ui/index.ts +0 -53
  42. package/templates/interface-boilerplate/components/ui/label.tsx +0 -20
  43. package/templates/interface-boilerplate/components/ui/progress.tsx +0 -24
  44. package/templates/interface-boilerplate/components/ui/select.tsx +0 -149
  45. package/templates/interface-boilerplate/components/ui/separator.tsx +0 -25
  46. package/templates/interface-boilerplate/components/ui/skeleton.tsx +0 -12
  47. package/templates/interface-boilerplate/components/ui/switch.tsx +0 -28
  48. package/templates/interface-boilerplate/components/ui/tabs.tsx +0 -54
  49. package/templates/interface-boilerplate/components/ui/textarea.tsx +0 -22
  50. package/templates/interface-boilerplate/components/ui/tooltip.tsx +0 -29
  51. package/templates/interface-boilerplate/lib/analytics.ts +0 -182
  52. package/templates/interface-boilerplate/lib/auth-context.tsx +0 -83
  53. package/templates/interface-boilerplate/lib/auth.ts +0 -199
  54. package/templates/interface-boilerplate/lib/callFlow.ts +0 -234
  55. package/templates/interface-boilerplate/lib/flowTracer.ts +0 -195
  56. package/templates/interface-boilerplate/lib/hooks/.gitkeep +0 -0
  57. package/templates/interface-boilerplate/lib/stores/.gitkeep +0 -0
  58. package/templates/interface-boilerplate/next.config.js +0 -6
  59. package/templates/interface-boilerplate/postcss.config.js +0 -6
  60. package/templates/interface-boilerplate/tailwind.config.js +0 -103
@@ -1,128 +1,49 @@
1
- 'use client';
2
-
3
- import { useState, useEffect } from 'react';
4
- import { motion } from 'framer-motion';
5
- import { ArrowRight, Zap, Shield, Sparkles } from 'lucide-react';
6
- import { Button } from '@/components/ui/button';
7
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
8
- import { ThemeToggle } from '@/components/theme-toggle';
9
-
10
- const features = [
11
- {
12
- icon: Zap,
13
- title: 'Lightning Fast',
14
- description: 'Built for speed with optimized performance at every level.',
15
- },
16
- {
17
- icon: Shield,
18
- title: 'Secure by Default',
19
- description: 'Enterprise-grade security with end-to-end encryption.',
20
- },
21
- {
22
- icon: Sparkles,
23
- title: 'Modern Design',
24
- description: 'Beautiful, accessible UI that users love.',
25
- },
26
- ];
1
+ import Link from "next/link";
27
2
 
28
3
  export default function Home() {
29
- const [mounted, setMounted] = useState(false);
30
-
31
- useEffect(() => {
32
- setMounted(true);
33
- }, []);
34
-
35
- if (!mounted) return null;
36
-
37
4
  return (
38
- <div className="min-h-screen bg-background">
39
- {/* Header */}
40
- <header className="border-b">
41
- <div className="container mx-auto flex h-16 items-center justify-between px-4">
42
- <div className="flex items-center space-x-2">
43
- <div className="h-8 w-8 rounded-lg bg-primary" />
44
- <span className="text-xl font-semibold">My App</span>
45
- </div>
46
- <div className="flex items-center space-x-4">
47
- <ThemeToggle />
48
- <Button variant="ghost" asChild>
49
- <a href="/login">Login</a>
50
- </Button>
51
- <Button asChild>
52
- <a href="/dashboard">Get Started</a>
53
- </Button>
54
- </div>
55
- </div>
56
- </header>
57
-
58
- {/* Hero Section */}
59
- <section className="container mx-auto px-4 py-24 text-center">
60
- <motion.div
61
- initial={{ opacity: 0, y: 20 }}
62
- animate={{ opacity: 1, y: 0 }}
63
- transition={{ duration: 0.5 }}
64
- >
65
- <h1 className="text-4xl font-bold tracking-tight sm:text-6xl">
66
- Build amazing products
67
- <br />
68
- <span className="text-primary">with Lux</span>
5
+ <div className="min-h-screen flex flex-col bg-white">
6
+ <main className="flex-1 flex flex-col items-center justify-center px-6">
7
+ <div className="max-w-2xl w-full text-center">
8
+ <p className="text-sm font-medium tracking-widest text-zinc-400 uppercase mb-4">
9
+ Boilerplate
10
+ </p>
11
+ <h1 className="text-6xl font-semibold tracking-tight text-black mb-6">
12
+ Lux
69
13
  </h1>
70
- <p className="mx-auto mt-6 max-w-2xl text-lg text-muted-foreground">
71
- Create beautiful, fast, and secure applications with our modern platform.
72
- All your backend logic lives in Flows.
14
+ <p className="text-lg text-zinc-500 mb-12 max-w-md mx-auto">
15
+ Authentication, database, and UI components. Everything you need to start building.
73
16
  </p>
74
- <div className="mt-10 flex items-center justify-center gap-4">
75
- <Button size="lg" asChild>
76
- <a href="/dashboard">
77
- Get Started <ArrowRight className="ml-2 h-4 w-4" />
78
- </a>
79
- </Button>
80
- <Button size="lg" variant="outline">
81
- Learn More
82
- </Button>
83
- </div>
84
- </motion.div>
85
- </section>
86
17
 
87
- {/* Features Section */}
88
- <section className="container mx-auto px-4 py-24">
89
- <div className="text-center">
90
- <h2 className="text-3xl font-bold tracking-tight">
91
- Everything you need
92
- </h2>
93
- <p className="mt-4 text-muted-foreground">
94
- Powerful features to help you build and scale.
95
- </p>
96
- </div>
97
- <div className="mt-16 grid gap-8 sm:grid-cols-2 lg:grid-cols-3">
98
- {features.map((feature, index) => (
99
- <motion.div
100
- key={feature.title}
101
- initial={{ opacity: 0, y: 20 }}
102
- animate={{ opacity: 1, y: 0 }}
103
- transition={{ duration: 0.5, delay: index * 0.1 }}
18
+ <div className="flex gap-4 justify-center mb-16">
19
+ <Link
20
+ href="/sign-in"
21
+ className="px-8 py-3 bg-black text-white text-sm font-medium rounded-full hover:bg-zinc-800 transition-colors"
104
22
  >
105
- <Card className="h-full">
106
- <CardHeader>
107
- <div className="mb-4 flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10">
108
- <feature.icon className="h-6 w-6 text-primary" />
109
- </div>
110
- <CardTitle>{feature.title}</CardTitle>
111
- </CardHeader>
112
- <CardContent>
113
- <CardDescription>{feature.description}</CardDescription>
114
- </CardContent>
115
- </Card>
116
- </motion.div>
117
- ))}
118
- </div>
119
- </section>
23
+ Sign In
24
+ </Link>
25
+ <Link
26
+ href="/sign-up"
27
+ className="px-8 py-3 border border-zinc-200 text-black text-sm font-medium rounded-full hover:bg-zinc-50 transition-colors"
28
+ >
29
+ Sign Up
30
+ </Link>
31
+ </div>
120
32
 
121
- {/* Footer */}
122
- <footer className="border-t py-12">
123
- <div className="container mx-auto px-4 text-center text-sm text-muted-foreground">
124
- Built with Lux. Edit <code className="rounded bg-muted px-1 py-0.5">app/page.tsx</code> to get started.
33
+ <div className="flex items-center justify-center gap-8 text-sm text-zinc-400">
34
+ <span>Next.js</span>
35
+ <span className="w-1 h-1 bg-zinc-300 rounded-full" />
36
+ <span>Auth</span>
37
+ <span className="w-1 h-1 bg-zinc-300 rounded-full" />
38
+ <span>Database</span>
39
+ <span className="w-1 h-1 bg-zinc-300 rounded-full" />
40
+ <span>Tailwind</span>
41
+ </div>
125
42
  </div>
43
+ </main>
44
+
45
+ <footer className="py-6 text-center text-sm text-zinc-400">
46
+ <p>Ready to ship.</p>
126
47
  </footer>
127
48
  </div>
128
49
  );
@@ -0,0 +1,71 @@
1
+ "use client";
2
+
3
+ import { useSession, signOut } from "@/lib/auth-client";
4
+ import { useRouter } from "next/navigation";
5
+ import { useEffect } from "react";
6
+
7
+ export default function SettingsPage() {
8
+ const { data: session, isPending } = useSession();
9
+ const router = useRouter();
10
+
11
+ useEffect(() => {
12
+ if (!isPending && !session) {
13
+ router.push("/sign-in");
14
+ }
15
+ }, [session, isPending, router]);
16
+
17
+ if (isPending) {
18
+ return (
19
+ <div className="min-h-screen flex items-center justify-center">
20
+ <div className="text-gray-500">Loading...</div>
21
+ </div>
22
+ );
23
+ }
24
+
25
+ if (!session) {
26
+ return null;
27
+ }
28
+
29
+ const handleSignOut = async () => {
30
+ await signOut();
31
+ router.push("/sign-in");
32
+ };
33
+
34
+ return (
35
+ <div className="min-h-screen bg-gray-50">
36
+ <div className="max-w-2xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
37
+ <div className="mb-8">
38
+ <h1 className="text-3xl font-bold text-gray-900">Settings</h1>
39
+ <p className="mt-2 text-sm text-gray-600">
40
+ Manage your account settings
41
+ </p>
42
+ </div>
43
+
44
+ <div className="bg-white shadow rounded-lg p-6">
45
+ <h2 className="text-lg font-medium text-gray-900 mb-4">Account</h2>
46
+
47
+ <div className="space-y-4">
48
+ <div>
49
+ <label className="block text-sm font-medium text-gray-700">Name</label>
50
+ <p className="mt-1 text-sm text-gray-900">{session.user.name}</p>
51
+ </div>
52
+
53
+ <div>
54
+ <label className="block text-sm font-medium text-gray-700">Email</label>
55
+ <p className="mt-1 text-sm text-gray-900">{session.user.email}</p>
56
+ </div>
57
+ </div>
58
+
59
+ <div className="mt-6 pt-6 border-t border-gray-200">
60
+ <button
61
+ onClick={handleSignOut}
62
+ className="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors"
63
+ >
64
+ Sign Out
65
+ </button>
66
+ </div>
67
+ </div>
68
+ </div>
69
+ </div>
70
+ );
71
+ }
@@ -0,0 +1,19 @@
1
+ import { SignInForm } from "@/components/auth/sign-in-form";
2
+
3
+ export default function SignInPage() {
4
+ return (
5
+ <div className="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
6
+ <div className="max-w-md w-full space-y-8">
7
+ <div className="text-center">
8
+ <h2 className="text-3xl font-bold text-gray-900">Sign in</h2>
9
+ <p className="mt-2 text-sm text-gray-600">
10
+ Welcome back! Please sign in to continue.
11
+ </p>
12
+ </div>
13
+ <div className="mt-8 bg-white py-8 px-6 shadow rounded-lg">
14
+ <SignInForm />
15
+ </div>
16
+ </div>
17
+ </div>
18
+ );
19
+ }
@@ -0,0 +1,19 @@
1
+ import { SignUpForm } from "@/components/auth/sign-up-form";
2
+
3
+ export default function SignUpPage() {
4
+ return (
5
+ <div className="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
6
+ <div className="max-w-md w-full space-y-8">
7
+ <div className="text-center">
8
+ <h2 className="text-3xl font-bold text-gray-900">Create account</h2>
9
+ <p className="mt-2 text-sm text-gray-600">
10
+ Get started by creating your account
11
+ </p>
12
+ </div>
13
+ <div className="mt-8 bg-white py-8 px-6 shadow rounded-lg">
14
+ <SignUpForm />
15
+ </div>
16
+ </div>
17
+ </div>
18
+ );
19
+ }
@@ -0,0 +1,137 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { authClient } from "@/lib/auth-client";
5
+ import { useRouter } from "next/navigation";
6
+
7
+ export function SignInForm() {
8
+ const router = useRouter();
9
+ const [email, setEmail] = useState("");
10
+ const [password, setPassword] = useState("");
11
+ const [isLoading, setIsLoading] = useState(false);
12
+ const [error, setError] = useState("");
13
+
14
+ const handleEmailPassword = async (e: React.FormEvent) => {
15
+ e.preventDefault();
16
+ setError("");
17
+ setIsLoading(true);
18
+
19
+ try {
20
+ await authClient.signIn.email({
21
+ email,
22
+ password,
23
+ callbackURL: "/dashboard",
24
+ });
25
+
26
+ router.push("/dashboard");
27
+ } catch (err) {
28
+ setError("Invalid email or password");
29
+ } finally {
30
+ setIsLoading(false);
31
+ }
32
+ };
33
+
34
+ const handleGoogleSignIn = async () => {
35
+ setError("");
36
+ try {
37
+ await authClient.signIn.social({
38
+ provider: "google",
39
+ callbackURL: "/dashboard",
40
+ });
41
+ } catch (err) {
42
+ setError("Failed to sign in with Google");
43
+ }
44
+ };
45
+
46
+ return (
47
+ <div className="space-y-6">
48
+ {/* Google Sign In */}
49
+ <button
50
+ onClick={handleGoogleSignIn}
51
+ className="w-full flex items-center justify-center gap-3 px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
52
+ >
53
+ <svg className="w-5 h-5" viewBox="0 0 24 24">
54
+ <path
55
+ fill="#4285F4"
56
+ d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
57
+ />
58
+ <path
59
+ fill="#34A853"
60
+ d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
61
+ />
62
+ <path
63
+ fill="#FBBC05"
64
+ d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
65
+ />
66
+ <path
67
+ fill="#EA4335"
68
+ d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
69
+ />
70
+ </svg>
71
+ Continue with Google
72
+ </button>
73
+
74
+ <div className="relative">
75
+ <div className="absolute inset-0 flex items-center">
76
+ <div className="w-full border-t border-gray-300" />
77
+ </div>
78
+ <div className="relative flex justify-center text-sm">
79
+ <span className="bg-white px-2 text-gray-500">Or continue with</span>
80
+ </div>
81
+ </div>
82
+
83
+ {/* Email/Password Form */}
84
+ <form onSubmit={handleEmailPassword} className="space-y-4">
85
+ <div>
86
+ <label className="block text-sm font-medium text-gray-700 mb-1">
87
+ Email
88
+ </label>
89
+ <input
90
+ type="email"
91
+ value={email}
92
+ onChange={(e) => setEmail(e.target.value)}
93
+ required
94
+ className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-gray-900 focus:border-gray-900"
95
+ placeholder="you@example.com"
96
+ />
97
+ </div>
98
+
99
+ <div>
100
+ <label className="block text-sm font-medium text-gray-700 mb-1">
101
+ Password
102
+ </label>
103
+ <input
104
+ type="password"
105
+ value={password}
106
+ onChange={(e) => setPassword(e.target.value)}
107
+ required
108
+ className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-gray-900 focus:border-gray-900"
109
+ placeholder="••••••••"
110
+ />
111
+ </div>
112
+
113
+ {error && (
114
+ <div className="text-sm text-red-600 bg-red-50 border border-red-200 rounded px-3 py-2">
115
+ {error}
116
+ </div>
117
+ )}
118
+
119
+ <button
120
+ type="submit"
121
+ disabled={isLoading}
122
+ className="w-full px-4 py-2 bg-gray-900 text-white rounded-lg hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
123
+ >
124
+ {isLoading ? "Signing in..." : "Sign in"}
125
+ </button>
126
+ </form>
127
+
128
+ {/* Sign Up Link */}
129
+ <div className="text-center text-sm text-gray-600">
130
+ Don&apos;t have an account?{" "}
131
+ <a href="/sign-up" className="text-gray-900 hover:underline font-medium">
132
+ Sign up
133
+ </a>
134
+ </div>
135
+ </div>
136
+ );
137
+ }
@@ -0,0 +1,225 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { authClient } from "@/lib/auth-client";
5
+ import { useRouter } from "next/navigation";
6
+
7
+ export function SignUpForm() {
8
+ const router = useRouter();
9
+ const [name, setName] = useState("");
10
+ const [email, setEmail] = useState("");
11
+ const [password, setPassword] = useState("");
12
+ const [confirmPassword, setConfirmPassword] = useState("");
13
+ const [showPassword, setShowPassword] = useState(false);
14
+ const [showConfirmPassword, setShowConfirmPassword] = useState(false);
15
+ const [isLoading, setIsLoading] = useState(false);
16
+ const [error, setError] = useState("");
17
+
18
+ const handleAccountSubmit = async (e: React.FormEvent) => {
19
+ e.preventDefault();
20
+ setError("");
21
+
22
+ if (password !== confirmPassword) {
23
+ setError("Passwords do not match");
24
+ return;
25
+ }
26
+
27
+ if (password.length < 8) {
28
+ setError("Password must be at least 8 characters");
29
+ return;
30
+ }
31
+
32
+ setIsLoading(true);
33
+
34
+ try {
35
+ await authClient.signUp.email({
36
+ email,
37
+ password,
38
+ name,
39
+ });
40
+
41
+ router.push("/dashboard");
42
+ } catch (err: any) {
43
+ if (err.message?.includes("already exists") || err.message?.includes("UNIQUE constraint")) {
44
+ setError("An account with this email already exists. Please sign in instead.");
45
+ } else {
46
+ setError(err.message || "Failed to create account");
47
+ }
48
+ } finally {
49
+ setIsLoading(false);
50
+ }
51
+ };
52
+
53
+ const handleGoogleSignUp = async () => {
54
+ setError("");
55
+ try {
56
+ await authClient.signIn.social({
57
+ provider: "google",
58
+ callbackURL: "/dashboard",
59
+ });
60
+ } catch (err) {
61
+ setError("Failed to sign up with Google");
62
+ }
63
+ };
64
+
65
+ return (
66
+ <div className="space-y-6">
67
+ {/* Google Sign Up */}
68
+ <button
69
+ onClick={handleGoogleSignUp}
70
+ className="w-full flex items-center justify-center gap-3 px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
71
+ >
72
+ <svg className="w-5 h-5" viewBox="0 0 24 24">
73
+ <path
74
+ fill="#4285F4"
75
+ d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
76
+ />
77
+ <path
78
+ fill="#34A853"
79
+ d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
80
+ />
81
+ <path
82
+ fill="#FBBC05"
83
+ d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
84
+ />
85
+ <path
86
+ fill="#EA4335"
87
+ d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
88
+ />
89
+ </svg>
90
+ Continue with Google
91
+ </button>
92
+
93
+ <div className="relative">
94
+ <div className="absolute inset-0 flex items-center">
95
+ <div className="w-full border-t border-gray-300" />
96
+ </div>
97
+ <div className="relative flex justify-center text-sm">
98
+ <span className="bg-white px-2 text-gray-500">Or sign up with email</span>
99
+ </div>
100
+ </div>
101
+
102
+ {/* Sign Up Form */}
103
+ <form onSubmit={handleAccountSubmit} className="space-y-4">
104
+ <div>
105
+ <label className="block text-sm font-medium text-gray-700 mb-1">
106
+ Name
107
+ </label>
108
+ <input
109
+ type="text"
110
+ value={name}
111
+ onChange={(e) => setName(e.target.value)}
112
+ required
113
+ className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-gray-900 focus:border-gray-900"
114
+ placeholder="John Doe"
115
+ />
116
+ </div>
117
+
118
+ <div>
119
+ <label className="block text-sm font-medium text-gray-700 mb-1">
120
+ Email
121
+ </label>
122
+ <input
123
+ type="email"
124
+ value={email}
125
+ onChange={(e) => setEmail(e.target.value)}
126
+ required
127
+ className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-gray-900 focus:border-gray-900"
128
+ placeholder="you@example.com"
129
+ />
130
+ </div>
131
+
132
+ <div>
133
+ <label className="block text-sm font-medium text-gray-700 mb-1">
134
+ Password
135
+ </label>
136
+ <div className="relative">
137
+ <input
138
+ type={showPassword ? "text" : "password"}
139
+ value={password}
140
+ onChange={(e) => setPassword(e.target.value)}
141
+ required
142
+ minLength={8}
143
+ className="w-full px-3 py-2 pr-10 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-gray-900 focus:border-gray-900"
144
+ placeholder="••••••••"
145
+ />
146
+ <button
147
+ type="button"
148
+ onClick={() => setShowPassword(!showPassword)}
149
+ className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-gray-600"
150
+ >
151
+ {showPassword ? (
152
+ <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
153
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21" />
154
+ </svg>
155
+ ) : (
156
+ <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
157
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
158
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
159
+ </svg>
160
+ )}
161
+ </button>
162
+ </div>
163
+ <p className="mt-1 text-xs text-gray-500">
164
+ Must be at least 8 characters
165
+ </p>
166
+ </div>
167
+
168
+ <div>
169
+ <label className="block text-sm font-medium text-gray-700 mb-1">
170
+ Confirm Password
171
+ </label>
172
+ <div className="relative">
173
+ <input
174
+ type={showConfirmPassword ? "text" : "password"}
175
+ value={confirmPassword}
176
+ onChange={(e) => setConfirmPassword(e.target.value)}
177
+ required
178
+ minLength={8}
179
+ className="w-full px-3 py-2 pr-10 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-gray-900 focus:border-gray-900"
180
+ placeholder="••••••••"
181
+ />
182
+ <button
183
+ type="button"
184
+ onClick={() => setShowConfirmPassword(!showConfirmPassword)}
185
+ className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-gray-600"
186
+ >
187
+ {showConfirmPassword ? (
188
+ <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
189
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21" />
190
+ </svg>
191
+ ) : (
192
+ <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
193
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
194
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
195
+ </svg>
196
+ )}
197
+ </button>
198
+ </div>
199
+ </div>
200
+
201
+ {error && (
202
+ <div className="text-sm text-red-600 bg-red-50 border border-red-200 rounded px-3 py-2">
203
+ {error}
204
+ </div>
205
+ )}
206
+
207
+ <button
208
+ type="submit"
209
+ disabled={isLoading}
210
+ className="w-full px-4 py-2 bg-gray-900 text-white rounded-lg hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
211
+ >
212
+ {isLoading ? "Creating account..." : "Sign Up"}
213
+ </button>
214
+ </form>
215
+
216
+ {/* Sign In Link */}
217
+ <div className="text-center text-sm text-gray-600">
218
+ Already have an account?{" "}
219
+ <a href="/sign-in" className="text-gray-900 hover:underline font-medium">
220
+ Sign in
221
+ </a>
222
+ </div>
223
+ </div>
224
+ );
225
+ }