Skip to content

Commit b3b2da2

Browse files
Sebastian-Wlogikf
andauthored
feat: user registration form (freeCodeCamp-2025-Summer-Hackathon#72)
Co-authored-by: Krzysztof G. <[email protected]>
1 parent 36526d5 commit b3b2da2

File tree

3 files changed

+187
-0
lines changed

3 files changed

+187
-0
lines changed

frontend/src/App.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import IdeaSubmissionForm from './components/IdeaSubmissionForm';
88
import LandingPageContent from './components/LandingPageContent';
99
import HelpPage from './components/HelpPage';
1010
import LoginPage from './pages/LoginPage';
11+
import RegisterPage from './pages/RegisterPage';
1112
import { IdeaAddPage, IdeaEditPage, IdeaPage, IdeasPage } from './pages/Ideas';
1213
import './styles.css';
1314

@@ -36,6 +37,7 @@ function App() {
3637
/>
3738
<Route path='help' element={<HelpPage />} />
3839
<Route path='login' element={<LoginPage />} />
40+
<Route path='register' element={<RegisterPage />} />
3941
<Route path='ideas'>
4042
<Route index element={<IdeasPage />} />
4143
<Route path=':ideaId' element={<IdeaPage />} />
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
import { useRef } from 'react';
2+
import { useForm } from 'react-hook-form';
3+
import { useApi } from '../hooks/useApi';
4+
5+
const UserIcon = () => {
6+
return (
7+
<svg
8+
className='h-[1em] opacity-50'
9+
xmlns='http://www.w3.org/2000/svg'
10+
viewBox='0 0 24 24'
11+
>
12+
<g
13+
strokeLinejoin='round'
14+
strokeLinecap='round'
15+
strokeWidth='2.5'
16+
fill='none'
17+
stroke='currentColor'
18+
>
19+
<path d='M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2'></path>
20+
<circle cx='12' cy='7' r='4'></circle>
21+
</g>
22+
</svg>
23+
);
24+
};
25+
26+
const PasswordIcon = () => {
27+
return (
28+
<svg
29+
className='h-[1em] opacity-50'
30+
xmlns='http://www.w3.org/2000/svg'
31+
viewBox='0 0 24 24'
32+
>
33+
<g
34+
strokeLinejoin='round'
35+
strokeLinecap='round'
36+
strokeWidth='2.5'
37+
fill='none'
38+
stroke='currentColor'
39+
>
40+
<path d='M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z'></path>
41+
<circle cx='16.5' cy='7.5' r='.5' fill='currentColor'></circle>
42+
</g>
43+
</svg>
44+
);
45+
};
46+
47+
export function RegisterForm() {
48+
const formRef = useRef();
49+
const {
50+
formState: { errors },
51+
handleSubmit,
52+
register,
53+
getValues,
54+
} = useForm();
55+
56+
const { fetchFromApi } = useApi({ method: 'POST' });
57+
58+
const onSubmit = async () => {
59+
try {
60+
await fetchFromApi('/users', {
61+
method: 'POST',
62+
body: new FormData(formRef.current),
63+
});
64+
} catch (e) {
65+
console.log(e);
66+
}
67+
};
68+
69+
return (
70+
<form ref={formRef} onSubmit={handleSubmit(onSubmit)}>
71+
<label className='floating-label flex justify-between py-2'>
72+
Username:
73+
<label className='input input-sm'>
74+
<UserIcon />
75+
<input
76+
{...register('username', { required: true })}
77+
type='Text'
78+
placeholder='Username'
79+
className='input-validator'
80+
aria-invalid={!!errors.username}
81+
/>
82+
</label>
83+
</label>
84+
{errors.username?.type === 'required' && (
85+
<p role='alert' className='text-error'>
86+
The field "Username" is required.
87+
</p>
88+
)}
89+
90+
<label className='floating-label flex justify-between py-2'>
91+
Name:
92+
<label className='input input-sm'>
93+
<UserIcon />
94+
<input
95+
{...register('name', { required: true })}
96+
type='Text'
97+
placeholder='Name'
98+
className='input-validator'
99+
aria-invalid={!!errors.name}
100+
/>
101+
</label>
102+
</label>
103+
{errors.name?.type === 'required' && (
104+
<p role='alert' className='text-error'>
105+
The field "Name" is required.
106+
</p>
107+
)}
108+
109+
<label className='floating-label flex justify-between py-2'>
110+
Password:
111+
<label className='input input-sm'>
112+
<PasswordIcon />
113+
<input
114+
{...register('password', { required: true, minLength: 8 })}
115+
type='Password'
116+
placeholder='Password'
117+
className='input-validator'
118+
aria-invalid={!!errors.password}
119+
/>
120+
</label>
121+
</label>
122+
{errors.password?.type === 'required' ? (
123+
<p role='alert' className='text-error'>
124+
The field "Password" is required.
125+
</p>
126+
) : (
127+
errors.password?.type === 'minLength' && (
128+
<p role='alert' className='text-error'>
129+
Password needs to be at least 8 characters long.
130+
</p>
131+
)
132+
)}
133+
134+
<label className='floating-label flex justify-between py-2'>
135+
Repeat Password:
136+
<label className='input input-sm'>
137+
<PasswordIcon />
138+
<input
139+
{...register('repeatPassword', {
140+
required: true,
141+
validate: value => getValues('password') === value,
142+
})}
143+
type='password'
144+
placeholder='Password'
145+
title='Must match the password entered in the previous input field'
146+
aria-invalid={!!errors.repeatPassword}
147+
/>
148+
</label>
149+
</label>
150+
{errors.repeatPassword?.type === 'required' ? (
151+
<p role='alert' className='text-error'>
152+
The field "Repeat Password" is required.
153+
</p>
154+
) : (
155+
errors.repeatPassword?.type === 'validate' && (
156+
<p role='alert' className='text-error'>
157+
Both passwords need to match.
158+
</p>
159+
)
160+
)}
161+
162+
<div className='flex justify-center'>
163+
<button className='my-1 animated-button'>Register</button>
164+
</div>
165+
</form>
166+
);
167+
}
168+
169+
export default RegisterForm;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { RegisterForm } from '../components/RegisterForm';
2+
3+
function RegisterPage() {
4+
return (
5+
<div className='container-wrapper'>
6+
<section className='voting-section flex flex-col justify-center items-center'>
7+
<h3>Register as a new user:</h3>
8+
<div className='card bg-base-100 w-lg p-4 auto'>
9+
<RegisterForm />
10+
</div>
11+
</section>
12+
</div>
13+
);
14+
}
15+
16+
export default RegisterPage;

0 commit comments

Comments
 (0)