Skip to content

Commit f797aa5

Browse files
authored
Merge pull request #42 from indrasuthar07/adding-login-page
feat: adding user login page ui(with error handling)
2 parents de37ed8 + 51d81e1 commit f797aa5

File tree

2 files changed

+130
-0
lines changed

2 files changed

+130
-0
lines changed

frontend/src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
44
import { BrowserRouter, Routes, Route } from "react-router-dom";
55
import Index from "./pages/Index";
66
import SignUp from "./pages/Signup";
7+
import SignIn from "./pages/Signin";
78
const queryClient = new QueryClient();
89

910
const App = () => (
@@ -14,6 +15,7 @@ const App = () => (
1415
<Routes>
1516
<Route path="/" element={<Index />} />
1617
<Route path="/signup" element={<SignUp />} />
18+
<Route path="/signin" element={<SignIn />} />
1719
</Routes>
1820
</BrowserRouter>
1921
</TooltipProvider>

frontend/src/pages/Signin.tsx

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import { useState } from "react";
2+
import { useForm } from "react-hook-form";
3+
import { Link } from "react-router-dom";
4+
import { Button } from "../components/ui/button";
5+
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "../components/ui/card";
6+
import { InputField } from "../components/InputField";
7+
import { toast } from "../hooks/use-toast";
8+
import { mockLogin, SignInData } from "../lib/api";
9+
import { Loader2 } from "lucide-react";
10+
11+
const SignIn = () => {
12+
const [isLoading, setIsLoading] = useState(false);
13+
const {
14+
register,
15+
handleSubmit,
16+
formState: { errors },
17+
reset,
18+
} = useForm<SignInData>();
19+
20+
const onSubmit = async (data: SignInData) => {
21+
setIsLoading(true);
22+
try {
23+
const response = await mockLogin(data);
24+
toast({
25+
title: "Welcome back!",
26+
description: response.message,
27+
});
28+
reset();
29+
// Navigate to dashboard or home page here
30+
} catch (error: any) {
31+
toast({
32+
title: "Error",
33+
description: error.message || "Login failed. Please try again.",
34+
variant: "destructive",
35+
});
36+
} finally {
37+
setIsLoading(false);
38+
}
39+
};
40+
41+
return (
42+
<div className="relative min-h-screen flex items-center justify-center overflow-hidden px-4 py-8">
43+
44+
{/* Background floating blobs */}
45+
<div className="absolute inset-0 -z-10">
46+
<div className="absolute -top-32 -left-32 w-96 h-96 bg-blue-400 rounded-full opacity-30 bg-gradient-to-br from-blue-400 via-blue-100 to-blue-400 blur-3xl animate-blob"></div>
47+
<div className="absolute top-0 -right-32 w-96 h-96 bg-blue-400 rounded-full opacity-30 blur-3xl animate-blob animation-delay-2000"></div>
48+
<div className="absolute bottom-0 left-1/4 w-96 h-96 bg-blue-400 rounded-full opacity-30 blur-2xl animate-blob animation-delay-4000"></div>
49+
</div>
50+
51+
{/* Glassmorphic Card */}
52+
<Card className="w-full max-w-md bg-transparent backdrop-blur-md border border-white/30 shadow-lg rounded-xl p-6">
53+
<CardHeader className="space-y-1 text-center">
54+
<CardTitle className="text-3xl font-extrabold text-blue-700">
55+
UniLoot
56+
</CardTitle>
57+
<CardDescription className="text-gray-700 text-base">
58+
Sign in to your account
59+
</CardDescription>
60+
</CardHeader>
61+
62+
<CardContent>
63+
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
64+
<InputField
65+
label="Email"
66+
id="email"
67+
type="email"
68+
placeholder="[email protected]"
69+
{...register("email", {
70+
required: "Email is required",
71+
pattern: {
72+
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
73+
message: "Invalid email address",
74+
},
75+
})}
76+
error={errors.email?.message}
77+
/>
78+
79+
<InputField
80+
label="Password"
81+
id="password"
82+
type="password"
83+
placeholder="••••••••"
84+
{...register("password", {
85+
required: "Password is required",
86+
minLength: {
87+
value: 6,
88+
message: "Password must be at least 6 characters",
89+
},
90+
})}
91+
error={errors.password?.message}
92+
/>
93+
94+
<Button
95+
type="submit"
96+
className="w-full bg-blue-700 text-white hover:bg-blue-800 transition-all rounded-lg py-3 font-semibold flex justify-center items-center gap-2"
97+
size="lg"
98+
disabled={isLoading}
99+
>
100+
{isLoading ? (
101+
<>
102+
<Loader2 className="h-5 w-5 animate-spin" />
103+
Signing in...
104+
</>
105+
) : (
106+
"Sign In"
107+
)}
108+
</Button>
109+
</form>
110+
</CardContent>
111+
112+
<CardFooter className="flex flex-col space-y-2">
113+
<div className="text-sm text-gray-700 text-center">
114+
Don't have an account?{" "}
115+
<Link
116+
to="/signup"
117+
className="text-blue-700 hover:text-blue-800 hover:underline font-medium transition-colors"
118+
>
119+
Sign Up
120+
</Link>
121+
</div>
122+
</CardFooter>
123+
</Card>
124+
</div>
125+
);
126+
};
127+
128+
export default SignIn;

0 commit comments

Comments
 (0)