Initial commit
All checks were successful
Docker Build and Publish / build-and-push (push) Successful in 2m17s
All checks were successful
Docker Build and Publish / build-and-push (push) Successful in 2m17s
This commit is contained in:
76
frontend/src/components/Auth/Login.jsx
Normal file
76
frontend/src/components/Auth/Login.jsx
Normal file
@@ -0,0 +1,76 @@
|
||||
import React, { useState, useContext } from 'react';
|
||||
import { AuthContext } from '../../contexts/AuthContext';
|
||||
import { useNavigate, Link } from 'react-router-dom';
|
||||
import GoCLayout from '../Layout/GoCLayout';
|
||||
|
||||
const Login = () => {
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const { login, loading } = useContext(AuthContext);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setError('');
|
||||
try {
|
||||
await login(email, password);
|
||||
navigate('/');
|
||||
} catch (err) {
|
||||
setError('Failed to log in. Please check your credentials.');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<GoCLayout>
|
||||
<div className="flex items-center justify-center p-4">
|
||||
<div className="bg-white p-8 rounded-lg shadow-md w-full max-w-md border border-gray-200">
|
||||
<h2 className="text-2xl font-bold mb-6 text-center text-gray-800">Sign In</h2>
|
||||
{error && <div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">{error}</div>}
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="mb-4">
|
||||
<label className="block text-gray-700 text-sm font-bold mb-2" htmlFor="email">
|
||||
Email
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-6">
|
||||
<label className="block text-gray-700 text-sm font-bold mb-2" htmlFor="password">
|
||||
Password
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
id="password"
|
||||
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between flex-col gap-4">
|
||||
<button
|
||||
className="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline w-full transition duration-300"
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? 'Logging in...' : 'Sign In'}
|
||||
</button>
|
||||
<Link to="/register" className="text-blue-600 hover:text-blue-800 text-sm">
|
||||
Don't have an account? Register here
|
||||
</Link>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</GoCLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Login;
|
||||
127
frontend/src/components/Auth/Register.jsx
Normal file
127
frontend/src/components/Auth/Register.jsx
Normal file
@@ -0,0 +1,127 @@
|
||||
import React, { useState, useContext } from 'react';
|
||||
import { AuthContext } from '../../contexts/AuthContext';
|
||||
import { useNavigate, Link } from 'react-router-dom';
|
||||
import { pb } from '../../lib/pocketbase';
|
||||
import GoCLayout from '../Layout/GoCLayout';
|
||||
|
||||
const Register = () => {
|
||||
const [name, setName] = useState('');
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [passwordConfirm, setPasswordConfirm] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const { login } = useContext(AuthContext);
|
||||
const navigate = useNavigate();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setError('');
|
||||
|
||||
if (password !== passwordConfirm) {
|
||||
return setError('Passwords do not match');
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
// Create user
|
||||
const data = {
|
||||
"username": `user_${Math.random().toString(36).slice(2, 12)}`, // Generate random username if required or let PB handle it if not
|
||||
"email": email,
|
||||
"emailVisibility": true,
|
||||
"password": password,
|
||||
"passwordConfirm": passwordConfirm,
|
||||
"name": name
|
||||
};
|
||||
|
||||
await pb.collection('users').create(data);
|
||||
|
||||
// Log in immediately after creation
|
||||
await login(email, password);
|
||||
navigate('/');
|
||||
} catch (err) {
|
||||
console.error("Registration error:", err);
|
||||
setError('Failed to create account. Please try again.');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<GoCLayout>
|
||||
<div className="flex items-center justify-center p-4">
|
||||
<div className="bg-white p-8 rounded-lg shadow-md w-full max-w-md border border-gray-200">
|
||||
<h2 className="text-2xl font-bold mb-6 text-center text-gray-800">Create Account</h2>
|
||||
{error && <div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">{error}</div>}
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="mb-4">
|
||||
<label className="block text-gray-700 text-sm font-bold mb-2" htmlFor="name">
|
||||
Name
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<label className="block text-gray-700 text-sm font-bold mb-2" htmlFor="email">
|
||||
Email
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<label className="block text-gray-700 text-sm font-bold mb-2" htmlFor="password">
|
||||
Password
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
id="password"
|
||||
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-6">
|
||||
<label className="block text-gray-700 text-sm font-bold mb-2" htmlFor="passwordConfirm">
|
||||
Confirm Password
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
id="passwordConfirm"
|
||||
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
value={passwordConfirm}
|
||||
onChange={(e) => setPasswordConfirm(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between flex-col gap-4">
|
||||
<button
|
||||
className="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline w-full transition duration-300"
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? 'Creating Account...' : 'Register'}
|
||||
</button>
|
||||
<Link to="/" className="text-blue-600 hover:text-blue-800 text-sm">
|
||||
Already have an account? Sign In
|
||||
</Link>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</GoCLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Register;
|
||||
Reference in New Issue
Block a user