Skip to content

Commit 21c6811

Browse files
committed
login and Signup with validation and axios api setup
1 parent 31f1a43 commit 21c6811

File tree

13 files changed

+303
-26
lines changed

13 files changed

+303
-26
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ node_modules
1111
dist
1212
dist-ssr
1313
*.local
14-
14+
.env
1515
# Editor directories and files
1616
.vscode/*
1717
!.vscode/extensions.json

package-lock.json

Lines changed: 61 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,20 @@
1717
"@radix-ui/react-slot": "^1.0.2",
1818
"@reduxjs/toolkit": "^2.2.2",
1919
"@types/jest": "^29.5.12",
20+
"axios": "^1.6.8",
2021
"class-variance-authority": "^0.7.0",
2122
"clsx": "^2.1.0",
2223
"jest-environment-jsdom": "^29.6.2",
2324
"lucide-react": "^0.350.0",
2425
"react": "^18.2.0",
2526
"react-dom": "^18.2.0",
27+
"react-hook-form": "^7.51.2",
2628
"react-redux": "^9.1.0",
2729
"react-router-dom": "^6.22.3",
2830
"tailwind-merge": "^2.2.1",
2931
"tailwindcss-animate": "^1.0.7",
30-
"ts-jest": "^29.1.2"
32+
"ts-jest": "^29.1.2",
33+
"zod": "^3.22.4"
3134
},
3235
"devDependencies": {
3336
"@types/node": "^20.11.25",

src/api/axiosclient.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { LoginData, ResultData, SingupData } from '@/types';
2+
import axios from 'axios';
3+
class AxiosClient {
4+
client;
5+
constructor() {
6+
this.client = axios.create({
7+
baseURL: String(process.env.VITE_APP_BACKEND_BASE_URL),
8+
withCredentials: true,
9+
headers: {
10+
'Content-Type': ['application/json', 'application/x-www-form-urlencoded'],
11+
},
12+
});
13+
}
14+
async post(url: string, content: SingupData | LoginData | ResultData) {
15+
try{
16+
const {data}=await this.client.post(url, content);
17+
return data;
18+
}catch(e){
19+
return e;
20+
}
21+
}
22+
async get(url: string) {
23+
try{
24+
const {data}=await this.client.get(url);
25+
return data;
26+
}catch(e){
27+
return e;
28+
}
29+
}
30+
async patch(url:string){
31+
try{
32+
const {data}=await this.client.patch(url);
33+
return data;
34+
}catch(e){
35+
return e;
36+
}
37+
}
38+
}
39+
export const axiosClient = new AxiosClient();

src/approutes.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import SignupScreen from './pages/SignupScreen/SignupScreen';
55
import LoginScreen from './pages/LoginScreen/LoginScreen';
66
import EmailVerification from './pages/EmailVerification/EmailVerification';
77
import Protected from './components/Protected';
8+
import TypingTest from './pages/Typingtest/TypingTest';
9+
import DashBoard from './pages/Dashboard/DashBoard';
810
const router = createBrowserRouter([
911
{
1012
path: "/",
@@ -27,5 +29,13 @@ const router = createBrowserRouter([
2729
}
2830
],
2931
},
32+
{
33+
path:'/test',
34+
element:<Protected authentication={true}><TypingTest/></Protected>
35+
},
36+
{
37+
path:'/dashboard/:id',
38+
element:<Protected authentication={true}><DashBoard/></Protected>
39+
}
3040
]);
3141
export default router;

src/components/Protected.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,24 @@ interface Props {
88
}
99
const Protected = ({ children, authentication }: Props) => {
1010
const authStatus = useSelector((state: AuthState) => state.auth.status);
11+
const isEmailVerified=true; //dummy value for test;
1112
const navigate = useNavigate();
1213
const [loading, setloading] = useState<boolean>(true);
1314
useEffect(() => {
1415
if (authentication && authStatus != true) {
1516
navigate("/login");
1617
}
18+
if(authStatus && authentication && !isEmailVerified){
19+
navigate("/verify");
20+
}
21+
if(authStatus && authentication && isEmailVerified){
22+
navigate("/test");
23+
}
1724
if (!authentication && authStatus === true) {
18-
navigate("/");
25+
navigate("/test");
1926
}
2027
setloading(false);
21-
}, [authStatus, authentication, navigate]);
28+
}, [authStatus, authentication, navigate,isEmailVerified]);
2229
return loading ? null : <>{children}</>;
2330
};
2431

src/pages/Dashboard/DashBoard.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import React from 'react'
2+
3+
const DashBoard = () => {
4+
return (
5+
<div>
6+
Dashboard Page
7+
</div>
8+
)
9+
}
10+
11+
export default DashBoard

src/pages/EmailVerification/EmailVerification.tsx

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,32 @@
1-
import { useAuthStatus } from "@/hooks/useAuthStatus";
1+
import { Label } from "@/components/ui/label"
2+
import { Input } from "@/components/ui/input"
3+
import { Button } from "@/components/ui/button"
24

3-
const EmailVerification = () => {
4-
const [authStatus] = useAuthStatus();
5-
return <div>EmailVerification for {authStatus ? "Verified User" : ""}</div>;
6-
};
5+
export default function Component() {
6+
return (
7+
<div className="mx-auto space-y-6 max-w-sm">
8+
<div className="space-y-2 text-center">
9+
<h1 className="text-3xl font-bold">Verify your email</h1>
10+
<p className="text-gray-500 dark:text-gray-400">
11+
Enter the verification code we sent to
12+
<strong>m@example.com</strong>
13+
</p>
14+
</div>
15+
<div className="space-y-2">
16+
<div className="space-y-2">
17+
<Label htmlFor="email">Email</Label>
18+
<Input disabled id="email" type="email" value="m@example.com" />
19+
</div>
20+
<div className="space-y-2">
21+
<Label htmlFor="code">Verification code</Label>
22+
<Input id="code" placeholder="Enter your code" required />
23+
</div>
24+
<Button className="w-full">Verify</Button>
25+
<Button className="w-full" variant="outline">
26+
Resend code
27+
</Button>
28+
</div>
29+
</div>
30+
)
31+
}
732

8-
export default EmailVerification;

src/pages/LoginScreen/LoginScreen.tsx

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
11
import { Label } from "@/components/ui/label";
22
import { Input } from "@/components/ui/input";
33
import { Button } from "@/components/ui/button";
4-
import React from "react";
4+
import React, { useState } from "react";
55
import { Link } from "react-router-dom";
6+
import { useForm } from "react-hook-form";
7+
import { EyeIcon, EyeOffIcon } from "lucide-react";
8+
import { LoginData } from "@/types";
69

710
const LoginScreen: React.FC = () => {
11+
const [showPassword, setshowPassword] = useState<boolean>(false);
12+
const {
13+
register,
14+
formState: { errors },
15+
handleSubmit
16+
} = useForm<LoginData>();
17+
const submitHandler= async function (data:LoginData){
18+
console.log(data);
19+
}
820
return (
921
<div className="flex flex-col min-h-screen items-stretch">
1022
<header className="p-4 flex items-center">
@@ -20,32 +32,59 @@ const LoginScreen: React.FC = () => {
2032
Enter your email below to login to your account
2133
</p>
2234
</div>
23-
<div className="space-y-4">
35+
<form className="space-y-4" onSubmit={handleSubmit(submitHandler)}>
2436
<div className="space-y-2">
2537
<Label htmlFor="email">Email</Label>
2638
<Input
2739
id="email"
2840
placeholder="m@example.com"
2941
required
3042
type="email"
43+
{...register("email")}
3144
/>
45+
{errors.email && (
46+
<span className="text-red">{errors.email.message}</span>
47+
)}
3248
</div>
33-
<div className="space-y-2">
49+
<div className="space-y-2 relative">
3450
<div className="flex items-center">
3551
<Label htmlFor="password">Password</Label>
3652
{/* <Link className="ml-auto inline-block text-sm underline" to="#">
3753
Forgot your password?
3854
</Link> */}
3955
</div>
40-
<Input id="password" required type="password" />
56+
<Input
57+
id="password"
58+
required
59+
type={showPassword ? "text" : "password"}
60+
{...register("password")}
61+
/>
62+
{errors.password && (
63+
<span className="text-red">{errors.password.message}</span>
64+
)}
65+
<Button
66+
type="button"
67+
variant="ghost"
68+
size="sm"
69+
className="absolute top-4 right-0 bg-gray-200"
70+
onClick={() => {
71+
setshowPassword(!showPassword);
72+
}}
73+
>
74+
{showPassword ? (
75+
<EyeOffIcon className="h-4 w-4" aria-hidden="true" />
76+
) : (
77+
<EyeIcon className="h-4 w-4" aria-hidden="true" />
78+
)}
79+
</Button>
4180
</div>
4281
<Button className="w-full" type="submit">
4382
Login
4483
</Button>
4584
{/* <Button className="w-full" variant="outline">
4685
Login with Google
4786
</Button> */}
48-
</div>
87+
</form>
4988
<div className="mt-4 text-center text-sm">
5089
Don't have an account?
5190
<Link className="underline" to="/signup">

0 commit comments

Comments
 (0)