Skip to content

Commit 66b9b9a

Browse files
authored
Merge pull request #70 from codersforcauses/issue-50-Update_login/signup_frontend
Issue 50 update login/signup frontend
2 parents 0d848d3 + f540bb5 commit 66b9b9a

File tree

6 files changed

+119
-16
lines changed

6 files changed

+119
-16
lines changed

client/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"prepare": "cd .. && husky client/.husky"
1616
},
1717
"dependencies": {
18+
"@hookform/resolvers": "^5.1.1",
1819
"@radix-ui/react-slot": "^1.2.3",
1920
"@tanstack/react-query": "^5.80.7",
2021
"@tanstack/react-query-devtools": "^5.80.7",
@@ -28,8 +29,10 @@
2829
"next": "15.3.3",
2930
"react": "19.1.0",
3031
"react-dom": "19.1.0",
32+
"react-hook-form": "^7.60.0",
3133
"tailwind-merge": "^3.3.1",
3234
"tailwindcss-animate": "^1.0.7",
35+
"zod": "^4.0.5",
3336
"zustand": "^5.0.6"
3437
},
3538
"devDependencies": {

client/src/components/ui/Forms/signup.tsx

Lines changed: 69 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,49 @@
11
import Link from "next/link";
2+
import { useForm } from "react-hook-form";
3+
4+
import { useRegister } from "@/hooks/useRegister";
25

36
import { Button } from "../button";
47
import { GoogleIcon } from "../google-icon";
58
import { Input } from "../input";
69
import { Separator } from "../separator";
710

11+
type SignUpFormData = {
12+
firstName: string;
13+
lastName: string;
14+
email: string;
15+
password: string;
16+
confirmPassword: string;
17+
};
18+
819
const SignUpForm = () => {
20+
const {
21+
register,
22+
handleSubmit,
23+
watch,
24+
formState: { errors },
25+
} = useForm<SignUpFormData>();
26+
27+
const password = watch("password");
28+
29+
const onSubmit = (data: SignUpFormData) => {
30+
registerHook({
31+
email: data.email,
32+
password1: data.password,
33+
password2: data.confirmPassword,
34+
first_name: data.firstName,
35+
last_name: data.lastName,
36+
});
37+
};
38+
39+
const { mutate: registerHook } = useRegister({
40+
onSuccess: () => {
41+
alert("Sign up was successful.");
42+
},
43+
onError: () => {
44+
alert("An error occured. Sign Up was unsuccessful.");
45+
},
46+
});
947
return (
1048
<div className="flex min-h-screen flex-col items-center justify-center">
1149
<div className="mx-4 flex max-w-xs flex-col items-center justify-center py-16 sm:mx-0 sm:max-w-3xl sm:rounded-2xl sm:border sm:border-border sm:px-20">
@@ -25,61 +63,80 @@ const SignUpForm = () => {
2563
<p className="text-subtle">Sign up with your email address</p>
2664
</div>
2765

28-
<form className="flex w-full flex-col">
66+
<form
67+
onSubmit={handleSubmit(onSubmit)}
68+
className="flex w-full flex-col"
69+
>
2970
<div className="flex flex-col gap-4 pb-10 md:flex-row md:gap-x-8 md:pb-12">
3071
<Input
3172
id="firstname"
3273
type="text"
33-
name="firstname"
3474
label="First Name"
3575
placeholder="Enter First Name"
3676
containerClassName="flex-1"
37-
required
77+
error={errors.firstName?.message}
78+
{...register("firstName", {
79+
required: "First name is required",
80+
})}
3881
/>
3982
<Input
4083
id="lastname"
4184
type="text"
42-
name="lastname"
4385
label="Last Name"
4486
placeholder="Enter Last Name"
4587
containerClassName="flex-1"
46-
required
88+
error={errors.lastName?.message}
89+
{...register("lastName", {
90+
required: "Last name is required",
91+
})}
4792
/>
4893
</div>
4994

5095
<div className="pb-4 sm:pb-8">
5196
<Input
5297
id="email"
5398
type="email"
54-
name="email"
5599
label="Email"
56100
placeholder="Enter Email"
57101
className="text-base"
58-
required
102+
error={errors.email?.message}
103+
{...register("email", {
104+
required: "Email is required",
105+
})}
59106
/>
60107
</div>
61108

62109
<div className="flex flex-col gap-4 pb-10 md:flex-row md:gap-x-8 md:pb-12">
63110
<Input
64111
id="password"
65112
type="password"
66-
name="password"
67113
label="Password"
68114
placeholder="Enter Password"
69115
containerClassName="flex-1"
70-
required
116+
error={errors.password?.message}
117+
{...register("password", {
118+
required: "Password is required",
119+
minLength: {
120+
value: 8,
121+
message: "Password must be at least 8 characters",
122+
},
123+
})}
71124
/>
125+
72126
<Input
73127
id="confirmPassword"
74128
type="password"
75-
name="confirmPassword"
76129
label="Confirm Password"
77130
placeholder="Confirm Password"
78131
containerClassName="flex-1"
79-
required
132+
error={errors.confirmPassword?.message}
133+
{...register("confirmPassword", {
134+
required: "Please confirm your password",
135+
validate: (value) =>
136+
value === password || "Passwords do not match",
137+
})}
80138
/>
81139
</div>
82-
83140
<Button
84141
className="h-12 w-full text-lg"
85142
variant="gradient"

client/src/hooks/useRegister.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { console } from "node:inspector/promises";
2+
3+
import { useMutation, UseMutationOptions } from "@tanstack/react-query";
4+
import { AxiosError } from "axios";
5+
6+
import api from "@/lib/api";
7+
8+
type RegistrationDetails = {
9+
email: string;
10+
password1: string;
11+
password2: string;
12+
first_name: string;
13+
last_name: string;
14+
};
15+
16+
export const useRegister = (
17+
args?: Omit<
18+
UseMutationOptions<
19+
unknown,
20+
AxiosError<{ [key: string]: unknown }>,
21+
RegistrationDetails
22+
>,
23+
"mutationKey" | "mutationFn"
24+
>,
25+
) => {
26+
return useMutation({
27+
...args,
28+
mutationKey: ["register"],
29+
mutationFn: (details: RegistrationDetails) => {
30+
return api.post("/users/register/", details);
31+
},
32+
});
33+
};

server/api/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
else []
3939
)
4040

41-
41+
CSRF_TRUSTED_ORIGINS = [FRONTEND_URL]
4242
# Application definition
4343

4444
INSTALLED_APPS = [

server/event_registration/tests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def check_data(
2929
"first_name": first_name,
3030
"last_name": last_name,
3131
}
32-
response = self.client.post(self.url, data)
32+
response = self.client.post(self.url, data, content_type="application/json")
3333
self.assertEqual(response.status_code, expected_status)
3434

3535
def test_valid_registration(self):

server/event_registration/views.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
11
from .forms import UserRegistrationForm
22
from django.http import HttpResponse
3+
from rest_framework.decorators import api_view, authentication_classes, permission_classes
4+
import json
35

46

7+
@api_view(["POST"])
8+
@authentication_classes([])
9+
@permission_classes([])
510
def register_user(request):
6-
form = UserRegistrationForm(request.POST)
7-
errors = form.errors.as_data()
11+
try:
12+
body_unicode = request.body.decode('utf-8')
13+
body = json.loads(body_unicode)
14+
form = UserRegistrationForm(body)
15+
errors = form.errors.as_data()
16+
except json.decoder.JSONDecodeError:
17+
return HttpResponse(status=400)
818

919
if errors.keys() == set(["email"]) and errors["email"][0].code == "unique":
1020
return HttpResponse(status=409)

0 commit comments

Comments
 (0)