Skip to content

Commit

Permalink
Added validation to RegisterUserForm
Browse files Browse the repository at this point in the history
Added LoginPage and RegisterPage
Added MainLayout
  • Loading branch information
OomsOoms committed Oct 3, 2024
1 parent afb0b36 commit 549f9a7
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 73 deletions.
7 changes: 4 additions & 3 deletions client/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { UserProvider } from './context/userContext.jsx';
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Home from "./pages/home";
import { RegisterForm, LoginForm } from "./features/authentication";
import LoginPage from "./pages/LoginPage";
import RegisterPage from "./pages/RegisterPage";
import './assets/App.css'


Expand All @@ -12,8 +13,8 @@ export default function App() {
<Routes>
<Route path="/">
<Route index element={<Home />} />
<Route path="login" element={<LoginForm />} />
<Route path="register" element={<RegisterForm />} />
<Route path="login" element={<LoginPage />} />
<Route path="register" element={<RegisterPage />} />
</Route>
</Routes>
</BrowserRouter>
Expand Down
87 changes: 34 additions & 53 deletions client/src/features/authentication/components/RegisterForm.jsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,36 @@
import { useContext, useState } from 'react';

import Captcha from '../../../components/ui/Captcha.jsx';

import { UserContext } from '../../../context/userContext.jsx';
import { useRegister } from '../hooks/useRegister.js';
import validator from 'validator';
import { emailValidations, passwordValidations, usernameValidations } from '../validations/registerValidations.js';


const RegisterForm = () => {
const { user, loading } = useContext(UserContext);
// User context
const { user, loading: userContextLoading } = useContext(UserContext);

// Register service
const { handleRegister, loading: registerLoading, error } = useRegister();

// Form state
const [username, setUsername] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [captchaToken, setCaptchaToken] = useState('');
const { handleRegister, loading: registerLoading, error } = useRegister();

// Form validation
const [usernameError, setUsernameError] = useState('');
const [emailError, setEmailError] = useState('');
const [passwordError, setPasswordError] = useState('');

const handleUsernameChange = (e) => {
const username = e.target.value;
const username = e.target.value.toLowerCase();
setUsername(username);
setUsernameError(usernameValidations(username));
}

const handleEmailChange = (e) => {
const email = e.target.value;
setEmail(email);
Expand All @@ -32,58 +41,26 @@ const RegisterForm = () => {
setPassword(password);
setPasswordError(passwordValidations(password));
};
// TODO: make functions for each input field and move validations to another file
const usernameValidations = (username) => {
if (!validator.isLength(username, { min: 3 })) {
return 'Username is too short'
} else if (!validator.isLength(username, { max: 20 })) {
return 'Username is too long'
} else if (!validator.matches(username, /^[a-z0-9_.-]+$/)) {
return 'Username must contain only letters, numbers, and ._-'
}
}
const emailValidations = (email) => {
if (!validator.isEmail(email)) {
return 'Invalid email';
}
}
const passwordValidations = (password) => {
if (!validator.isLength(password, { min: 8 })) {
return 'Password is too short';
} else if (!/[a-z]/.test(password)) {
return 'Password must contain a lowercase letter';
} else if (!/[A-Z]/.test(password)) {
return 'Password must contain a capital letter';
} else if (!/\d/.test(password)) {
return 'Password must contain a number';
}
}

const onSubmit = (e) => {
e.preventDefault(); // Prevent the default form submission
if (!username) {
setUsernameError('Username is required');
return;
}
if (!email) {
setEmailError('Email is required');
return;
}
if (!password) {
setPasswordError('Password is required');
return;
}
if (!captchaToken) {
alert('Please complete the captcha');
setUsernameError(usernameValidations(username));
setEmailError(emailValidations(email));
setPasswordError(passwordValidations(password));

if (usernameValidations(username) || emailValidations(email) || passwordValidations(password) || !captchaToken) {
console.log('Form has errors');
return;
}
console.log('Form is valid');
const result = handleRegister(username, email, password, captchaToken);
// Reload captcha on fail
// Reload captcha on egister fail
if (result !== 201) {
console.log('reset captcha')
}
}

if (loading) {
if (userContextLoading) {
return <div>Loading...</div>;
}

Expand All @@ -94,34 +71,38 @@ const RegisterForm = () => {
return (
<form onSubmit={onSubmit}>
<div>
<label htmlFor="username">Username:</label>
<input
htmlFor="username"
placeholder='Username'
type="text"
id="username"
value={username}
onChange={handleUsernameChange}
//onBlur={() => checkUsernameAvailability(username)} // possibly check username availability while typing or on blur
/>
{usernameError && <div>{usernameError}</div>}
<div className="error-message">{usernameError || '\u00A0'}</div> {/* Non-breaking space */}
</div>
<div>
<label htmlFor="email">Email:</label>
<input
htmlFor="email"
placeholder='Email'
type="email"
id="email"
value={email}
onChange={handleEmailChange}
/>
{emailError && <div>{emailError}</div>}
<div className="error-message">{emailError || '\u00A0'}</div> {/* Non-breaking space */}
</div>
<div>
<label htmlFor="password">Password:</label>
<input
htmlFor="password"
placeholder='Password'
type="password"
id="password"
value={password}
onChange={handlePasswordChange}
/>
{passwordError && <div>{passwordError}</div>}
<div className="error-message">{passwordError || '\u00A0'}</div> {/* Non-breaking space */}
</div>
<Captcha show={true} onToken={setCaptchaToken} />
<button type="submit" disabled={registerLoading}>
Expand Down
14 changes: 8 additions & 6 deletions client/src/features/authentication/hooks/useRegister.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,20 @@ export const useRegister = () => {
setError(null);
try {
const response = await register(username, email, password, hCaptchaToken);
if (!response.ok) {
return response;
}
if (response.ok) {
if (response.status === 409) { // Username or email confict
setError(response.data.message);
} else if (response.status === 201) {
window.location.href = '/';
} else {
// Handle other errors like server errors or bad request errors
setError('An error occurred');
}
} catch (err) {
setError(err.message);
setError(err.message); // These errors will be network errors or server errors
} finally {
setLoading(false);
}
};

return { handleRegister, loading, error };
};
};
17 changes: 6 additions & 11 deletions client/src/features/authentication/services/registerService.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,13 @@ export const register = async (username, email, password, hCaptchaToken) => {
},
withCredentials: true
});

if (response.status === 201) {
window.location.href = '/';
} else {
return response.data;
}
return response;
} catch (error) {
console.error('There was a problem with the axios operation:', error);
if (error.response) {
return { ok: false, errors: error.response.data.errors };
if (error.response && error.response.status >= 400 && error.response.status < 500) {
return error.response;
} else {
return { ok: false, errors: [{ path: 'server', msg: 'Server error' }] };
console.error('Server error or network issue:', error);
}
console.error('There was a problem with the register user axios operation:');
}
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import validator from 'validator';

const usernameValidations = (username) => {
if (!username) {
return 'Username is required'
} else if (!validator.isLength(username, { min: 3 })) {
return 'Username is too short'
} else if (!validator.isLength(username, { max: 20 })) {
return 'Username is too long'
} else if (!/^[a-zA-Z0-9_]*$/.test(username)) {
return 'Please only use letters, numbers, underscores, hyphens or fullstops'; // dont have to specify the case of the letters because the input doesnt allow uppercase
}else {
return null;
}
}

const emailValidations = (email) => {
if (!email) {
return 'Email is required';
} else if (!validator.isEmail(email)) {
return 'Invalid email';
}
}

const passwordValidations = (password) => {
if (!password) {
return 'Password is required';
} else if (!validator.isLength(password, { min: 8 })) {
return 'Password is too short';
} else if (!/[a-z]/.test(password)) {
return 'Password must contain a lowercase letter';
} else if (!/[A-Z]/.test(password)) {
return 'Password must contain a capital letter';
} else if (!/\d/.test(password)) {
return 'Password must contain a number';
}
}

export {
usernameValidations,
emailValidations,
passwordValidations
};
11 changes: 11 additions & 0 deletions client/src/layouts/MainLayout.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const MainLayout = ({ children }) => {
return (
<div className="main-layout">
<header>Header Content</header>
<main>{children}</main>
<footer>Footer Content</footer>
</div>
);
};

export default MainLayout;
17 changes: 17 additions & 0 deletions client/src/pages/LoginPage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { LoginForm } from "../features/authentication";

import MainLayout from '../layouts/MainLayout.jsx';

const LoginPage = () => {
return (
<MainLayout>
<div className="login-page">
<h1>Login</h1>
<LoginForm />
<p>Don't have an account? <a href="/register">Register</a></p>
</div>
</MainLayout>
);
};

export default LoginPage;
17 changes: 17 additions & 0 deletions client/src/pages/RegisterPage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { RegisterForm } from "../features/authentication";

import MainLayout from '../layouts/MainLayout.jsx';

const LoginPage = () => {
return (
<MainLayout>
<div className="register-page">
<h1>Register</h1>
<RegisterForm />
<p>Already have an account? <a href="/login">Login</a></p>
</div>
</MainLayout>
);
};

export default LoginPage;

0 comments on commit 549f9a7

Please sign in to comment.