Implement authentication features with login, registration, and user management; update service configurations for development and production environments.
This commit is contained in:
255
os-league-tools-master/src/components/AuthModal.js
Normal file
255
os-league-tools-master/src/components/AuthModal.js
Normal file
@@ -0,0 +1,255 @@
|
||||
import React, { useState } from 'react';
|
||||
import Modal from './Modal';
|
||||
import Spinner from './common/Spinner';
|
||||
import { login, register } from '../client/auth-client';
|
||||
|
||||
const VIEW = {
|
||||
LOGIN: 'login',
|
||||
REGISTER: 'register',
|
||||
};
|
||||
|
||||
export default function AuthModal({ isOpen, setIsOpen, onAuthSuccess }) {
|
||||
const [view, setView] = useState(VIEW.LOGIN);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
const [loginUsername, setLoginUsername] = useState('');
|
||||
const [loginPassword, setLoginPassword] = useState('');
|
||||
|
||||
const [registerUsername, setRegisterUsername] = useState('');
|
||||
const [registerEmail, setRegisterEmail] = useState('');
|
||||
const [registerPassword, setRegisterPassword] = useState('');
|
||||
const [registerConfirmPassword, setRegisterConfirmPassword] = useState('');
|
||||
|
||||
const resetForm = () => {
|
||||
setLoginUsername('');
|
||||
setLoginPassword('');
|
||||
setRegisterUsername('');
|
||||
setRegisterEmail('');
|
||||
setRegisterPassword('');
|
||||
setRegisterConfirmPassword('');
|
||||
setError('');
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
resetForm();
|
||||
setView(VIEW.LOGIN);
|
||||
};
|
||||
|
||||
const switchView = newView => {
|
||||
resetForm();
|
||||
setView(newView);
|
||||
};
|
||||
|
||||
const handleLogin = async e => {
|
||||
e.preventDefault();
|
||||
setError('');
|
||||
|
||||
if (!loginUsername || !loginPassword) {
|
||||
setError('Please fill in all fields');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
const result = await login(loginUsername, loginPassword);
|
||||
setIsLoading(false);
|
||||
|
||||
if (result.success) {
|
||||
resetForm();
|
||||
setIsOpen(false);
|
||||
if (onAuthSuccess) {
|
||||
onAuthSuccess(result.value);
|
||||
}
|
||||
} else {
|
||||
setError(result.error || 'Login failed');
|
||||
}
|
||||
};
|
||||
|
||||
const handleRegister = async e => {
|
||||
e.preventDefault();
|
||||
setError('');
|
||||
|
||||
if (!registerUsername || !registerEmail || !registerPassword || !registerConfirmPassword) {
|
||||
setError('Please fill in all fields');
|
||||
return;
|
||||
}
|
||||
|
||||
if (registerPassword !== registerConfirmPassword) {
|
||||
setError('Passwords do not match');
|
||||
return;
|
||||
}
|
||||
|
||||
if (registerPassword.length < 8) {
|
||||
setError('Password must be at least 8 characters');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
const result = await register(registerUsername, registerEmail, registerPassword);
|
||||
setIsLoading(false);
|
||||
|
||||
if (result.success) {
|
||||
resetForm();
|
||||
setIsOpen(false);
|
||||
if (onAuthSuccess) {
|
||||
onAuthSuccess(result.value);
|
||||
}
|
||||
} else {
|
||||
setError(result.error || 'Registration failed');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
setIsOpen={setIsOpen}
|
||||
onClose={handleClose}
|
||||
className='w-96 shadow shadow-primary rounded-md bg-primary-alt'
|
||||
>
|
||||
<Modal.Header className='text-center small-caps tracking-wide text-xl text-accent font-semibold'>
|
||||
{view === VIEW.LOGIN ? 'Login' : 'Create Account'}
|
||||
</Modal.Header>
|
||||
|
||||
<Modal.Body className='text-primary text-sm'>
|
||||
{view === VIEW.LOGIN ? (
|
||||
<form onSubmit={handleLogin} className='m-4 flex flex-col gap-3'>
|
||||
<label htmlFor='login-username' className='flex flex-col gap-1'>
|
||||
<span className='text-secondary text-xs'>Username</span>
|
||||
<input
|
||||
id='login-username'
|
||||
name='username'
|
||||
type='text'
|
||||
className='input-primary text-sm form-input'
|
||||
placeholder='Enter username'
|
||||
value={loginUsername}
|
||||
onChange={e => setLoginUsername(e.target.value)}
|
||||
disabled={isLoading}
|
||||
autoComplete='username'
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label htmlFor='login-password' className='flex flex-col gap-1'>
|
||||
<span className='text-secondary text-xs'>Password</span>
|
||||
<input
|
||||
id='login-password'
|
||||
name='password'
|
||||
type='password'
|
||||
className='input-primary text-sm form-input'
|
||||
placeholder='Enter password'
|
||||
value={loginPassword}
|
||||
onChange={e => setLoginPassword(e.target.value)}
|
||||
disabled={isLoading}
|
||||
autoComplete='current-password'
|
||||
/>
|
||||
</label>
|
||||
|
||||
{error && <div className='text-error text-xs text-center'>{error}</div>}
|
||||
|
||||
<button type='submit' className='button-filled py-2 mt-2' disabled={isLoading}>
|
||||
{isLoading ? (
|
||||
<span className='flex items-center justify-center gap-2'>
|
||||
<Spinner size={Spinner.SIZE.sm} /> Logging in...
|
||||
</span>
|
||||
) : (
|
||||
'Login'
|
||||
)}
|
||||
</button>
|
||||
</form>
|
||||
) : (
|
||||
<form onSubmit={handleRegister} className='m-4 flex flex-col gap-3'>
|
||||
<label htmlFor='register-username' className='flex flex-col gap-1'>
|
||||
<span className='text-secondary text-xs'>Username</span>
|
||||
<input
|
||||
id='register-username'
|
||||
name='username'
|
||||
type='text'
|
||||
className='input-primary text-sm form-input'
|
||||
placeholder='Choose a username'
|
||||
value={registerUsername}
|
||||
onChange={e => setRegisterUsername(e.target.value)}
|
||||
disabled={isLoading}
|
||||
autoComplete='username'
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label htmlFor='register-email' className='flex flex-col gap-1'>
|
||||
<span className='text-secondary text-xs'>Email</span>
|
||||
<input
|
||||
id='register-email'
|
||||
name='email'
|
||||
type='email'
|
||||
className='input-primary text-sm form-input'
|
||||
placeholder='Enter your email'
|
||||
value={registerEmail}
|
||||
onChange={e => setRegisterEmail(e.target.value)}
|
||||
disabled={isLoading}
|
||||
autoComplete='email'
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label htmlFor='register-password' className='flex flex-col gap-1'>
|
||||
<span className='text-secondary text-xs'>Password</span>
|
||||
<input
|
||||
id='register-password'
|
||||
name='new-password'
|
||||
type='password'
|
||||
className='input-primary text-sm form-input'
|
||||
placeholder='Create a password'
|
||||
value={registerPassword}
|
||||
onChange={e => setRegisterPassword(e.target.value)}
|
||||
disabled={isLoading}
|
||||
autoComplete='new-password'
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label htmlFor='register-confirm-password' className='flex flex-col gap-1'>
|
||||
<span className='text-secondary text-xs'>Confirm Password</span>
|
||||
<input
|
||||
id='register-confirm-password'
|
||||
name='confirm-password'
|
||||
type='password'
|
||||
className='input-primary text-sm form-input'
|
||||
placeholder='Confirm your password'
|
||||
value={registerConfirmPassword}
|
||||
onChange={e => setRegisterConfirmPassword(e.target.value)}
|
||||
disabled={isLoading}
|
||||
autoComplete='new-password'
|
||||
/>
|
||||
</label>
|
||||
|
||||
{error && <div className='text-error text-xs text-center'>{error}</div>}
|
||||
|
||||
<button type='submit' className='button-filled py-2 mt-2' disabled={isLoading}>
|
||||
{isLoading ? (
|
||||
<span className='flex items-center justify-center gap-2'>
|
||||
<Spinner size={Spinner.SIZE.sm} /> Creating account...
|
||||
</span>
|
||||
) : (
|
||||
'Create Account'
|
||||
)}
|
||||
</button>
|
||||
</form>
|
||||
)}
|
||||
</Modal.Body>
|
||||
|
||||
<Modal.Footer className='text-center text-sm'>
|
||||
{view === VIEW.LOGIN ? (
|
||||
<p className='text-secondary py-2'>
|
||||
Don't have an account?{' '}
|
||||
<button type='button' className='text-accent hover:underline' onClick={() => switchView(VIEW.REGISTER)}>
|
||||
Register
|
||||
</button>
|
||||
</p>
|
||||
) : (
|
||||
<p className='text-secondary py-2'>
|
||||
Already have an account?{' '}
|
||||
<button type='button' className='text-accent hover:underline' onClick={() => switchView(VIEW.LOGIN)}>
|
||||
Login
|
||||
</button>
|
||||
</p>
|
||||
)}
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user