Component Details
Auth Component 01

LoginPage.tsx
Next.js•Tailwind CSS
1// app/auth/login/page.tsx
2"use client";
3
4import type React from "react";
5import { useState } from "react";
6import { useRouter } from "next/navigation";
7import Link from "next/link";
8import { Button } from "@/components/ui/button";
9import { Input } from "@/components/ui/input";
10import {
11 Card,
12 CardContent,
13 CardHeader,
14 CardTitle,
15 CardDescription,
16} from "@/components/ui/card";
17import { Alert, AlertDescription } from "@/components/ui/alert";
18import { Checkbox } from "@/components/ui/checkbox";
19import { Label } from "@/components/ui/label";
20import {
21 AlertCircle,
22 Loader2,
23 LogIn,
24 Eye,
25 EyeOff,
26 Mail,
27 Lock,
28} from "lucide-react";
29
30export default function LoginPage() {
31 const [email, setEmail] = useState("");
32 const [password, setPassword] = useState("");
33 const [rememberMe, setRememberMe] = useState(false);
34 const [error, setError] = useState("");
35 const [isLoading, setIsLoading] = useState(false);
36 const [showPassword, setShowPassword] = useState(false);
37 const router = useRouter();
38
39 // Demo credentials for easy testing
40 const demoCredentials = {
41 email: "demo@example.com",
42 password: "demo123",
43 };
44
45 const handleLogin = async (e: React.FormEvent) => {
46 e.preventDefault();
47 setError("");
48 setIsLoading(true);
49
50 // Simulate API call delay
51 await new Promise((resolve) => setTimeout(resolve, 800));
52
53 // Basic validation
54 if (!email.trim()) {
55 setError("Please enter your email address");
56 setIsLoading(false);
57 return;
58 }
59
60 if (!password.trim()) {
61 setError("Please enter your password");
62 setIsLoading(false);
63 return;
64 }
65
66 // Email format validation
67 const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
68 if (!emailRegex.test(email)) {
69 setError("Please enter a valid email address");
70 setIsLoading(false);
71 return;
72 }
73
74 // Demo validation - accept demo credentials or any valid email
75 const isDemoLogin =
76 email === demoCredentials.email && password === demoCredentials.password;
77
78 if (isDemoLogin || (emailRegex.test(email) && password)) {
79 // Store demo session in localStorage
80 localStorage.setItem(
81 "userSession",
82 JSON.stringify({
83 email: email,
84 name: email.split("@")[0], // Extract name from email
85 isAuthenticated: true,
86 timestamp: new Date().toISOString(),
87 })
88 );
89
90 // Store remember me preference
91 if (rememberMe) {
92 localStorage.setItem("rememberMe", "true");
93 localStorage.setItem("userEmail", email);
94 } else {
95 localStorage.removeItem("userEmail");
96 }
97
98 // Redirect to home page
99 router.push("/");
100 } else {
101 setError("Invalid credentials. Try demo@example.com / demo123");
102 setPassword("");
103 }
104
105 setIsLoading(false);
106 };
107
108 const handleDemoLogin = () => {
109 setEmail(demoCredentials.email);
110 setPassword(demoCredentials.password);
111 setRememberMe(true);
112 };
113
114 return (
115 <div className="flex flex-col">
116 <main className="flex-1 flex items-center justify-center px-4 py-12">
117 <div className="w-full max-w-md">
118 <div className="text-center mb-8">
119 <h1 className="text-3xl font-bold mb-2">Welcome back</h1>
120 <p className="text-muted-foreground">
121 Sign in to your account to continue
122 </p>
123 </div>
124
125 <Card className="border shadow-sm">
126 <CardHeader className="space-y-1">
127 <CardTitle className="text-2xl">Sign in</CardTitle>
128 <CardDescription>
129 Enter your credentials to access your account
130 </CardDescription>
131 </CardHeader>
132
133 <CardContent>
134 <form onSubmit={handleLogin} className="space-y-4">
135 <div className="space-y-2">
136 <Label htmlFor="email">Email address</Label>
137 <div className="relative">
138 <Mail className="absolute left-3 top-3 h-4 w-4 text-muted-foreground" />
139 <Input
140 id="email"
141 type="email"
142 placeholder="name@example.com"
143 className="pl-10"
144 value={email}
145 onChange={(e) => setEmail(e.target.value)}
146 disabled={isLoading}
147 autoComplete="email"
148 />
149 </div>
150 </div>
151
152 <div className="space-y-2">
153 <div className="flex items-center justify-between">
154 <Label htmlFor="password">Password</Label>
155 <Link
156 href="/forgot-password"
157 className="text-sm text-primary hover:underline"
158 // onClick={(e) => {
159 // e.preventDefault();
160 // setError(
161 // "Forgot password feature is not implemented in this demo"
162 // );
163 // }}
164 >
165 Forgot password?
166 </Link>
167 </div>
168 <div className="relative">
169 <Lock className="absolute left-3 top-3 h-4 w-4 text-muted-foreground" />
170 <Input
171 id="password"
172 type={showPassword ? "text" : "password"}
173 placeholder="Enter your password"
174 className="pl-10 pr-10"
175 value={password}
176 onChange={(e) => setPassword(e.target.value)}
177 disabled={isLoading}
178 autoComplete="current-password"
179 />
180 <Button
181 type="button"
182 variant="ghost"
183 size="sm"
184 className="absolute right-0 top-0 h-full px-3"
185 onClick={() => setShowPassword(!showPassword)}
186 >
187 {showPassword ? (
188 <EyeOff className="h-4 w-4" />
189 ) : (
190 <Eye className="h-4 w-4" />
191 )}
192 </Button>
193 </div>
194 </div>
195
196 <div className="flex items-center space-x-2">
197 <Checkbox
198 id="remember"
199 checked={rememberMe}
200 onCheckedChange={(checked) =>
201 setRememberMe(checked as boolean)
202 }
203 disabled={isLoading}
204 />
205 <Label htmlFor="remember" className="text-sm font-normal">
206 Remember me for 30 days
207 </Label>
208 </div>
209
210 {error && (
211 <Alert variant="destructive">
212 <AlertCircle className="h-4 w-4" />
213 <AlertDescription>{error}</AlertDescription>
214 </Alert>
215 )}
216
217 <Button
218 type="submit"
219 className="w-full"
220 disabled={isLoading}
221 onClick={handleDemoLogin}
222 >
223 {isLoading ? (
224 <>
225 <Loader2 className="mr-2 h-4 w-4 animate-spin" />
226 Signing in...
227 </>
228 ) : (
229 <>
230 <LogIn className="mr-2 h-4 w-4" />
231 Sign in
232 </>
233 )}
234 </Button>
235
236 <div className="relative">
237 <div className="absolute inset-0 flex items-center">
238 <div className="w-full border-t"></div>
239 </div>
240 <div className="relative flex justify-center text-xs">
241 <span className="bg-background px-2 text-muted-foreground">
242 Don't have an account?
243 </span>
244 </div>
245 </div>
246
247 <Link href="/signup">
248 <Button
249 type="button"
250 variant="outline"
251 className="w-full"
252 disabled={isLoading}
253 >
254 Sign up to create an account
255 </Button>
256 </Link>
257 </form>
258 </CardContent>
259 </Card>
260 </div>
261 </main>
262 </div>
263 );
264}