forked from jamesp/sasa-membership
d024bf7fa3
- ui has been made 'kinda better' (after making it worse for a while lol - ESP rfid readers are now supported [ill upload the code for them in another repo later] - admin system has been secured a bit better and seems to be working well
134 lines
4.1 KiB
TypeScript
134 lines
4.1 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { useNavigate, Link } from 'react-router-dom';
|
|
import { authService, LoginData } from '../services/membershipService';
|
|
|
|
const Login: React.FC = () => {
|
|
const navigate = useNavigate();
|
|
const [formData, setFormData] = useState<LoginData>({
|
|
email: '',
|
|
password: ''
|
|
});
|
|
const [error, setError] = useState('');
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
setFormData({
|
|
...formData,
|
|
[e.target.name]: e.target.value
|
|
});
|
|
};
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setError('');
|
|
setLoading(true);
|
|
|
|
try {
|
|
await authService.login(formData);
|
|
navigate('/dashboard');
|
|
} catch (err: any) {
|
|
console.error('Login error:', err.response?.data); // Debug log
|
|
const errorDetail = err.response?.data?.detail;
|
|
if (typeof errorDetail === 'string') {
|
|
setError(errorDetail);
|
|
} else if (errorDetail && typeof errorDetail === 'object') {
|
|
// Handle validation error objects
|
|
setError('Login failed. Please check your credentials.');
|
|
} else {
|
|
setError('Login failed. Please check your credentials.');
|
|
}
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="auth-shell">
|
|
<header className="auth-topbar">
|
|
<div className="portal-brand">
|
|
<div className="portal-mark">S</div>
|
|
<div className="portal-brand-text">
|
|
<h1>SASA Member Portal</h1>
|
|
<div className="portal-subtitle">Member access and admin control room</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<main className="auth-container">
|
|
<section className="auth-welcome-card">
|
|
<div className="auth-kicker">Community Access</div>
|
|
<h2>Welcome to SASA</h2>
|
|
<p>
|
|
Swansea Airport Stakeholder's Association manages member access, events, and operations from one shared platform.
|
|
</p>
|
|
<div className="auth-feature-list">
|
|
<div className="auth-feature-item">Manage your membership, payments, and events in one place</div>
|
|
<div className="auth-feature-item">Keep profile and contact details current without admin help</div>
|
|
<div className="auth-feature-item">Admin users can switch into a separate operations workspace after login</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section className="auth-card">
|
|
<div className="auth-card-head">
|
|
<h2>Sign In</h2>
|
|
<span>Secure session</span>
|
|
</div>
|
|
|
|
<div className="auth-card-body">
|
|
{error && <div className="alert alert-error">{error}</div>}
|
|
|
|
<form onSubmit={handleSubmit}>
|
|
<div className="form-group">
|
|
<label htmlFor="email">Email Address</label>
|
|
<input
|
|
type="email"
|
|
id="email"
|
|
name="email"
|
|
value={formData.email}
|
|
onChange={handleChange}
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label htmlFor="password">Password</label>
|
|
<input
|
|
type="password"
|
|
id="password"
|
|
name="password"
|
|
value={formData.password}
|
|
onChange={handleChange}
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<button
|
|
type="submit"
|
|
className="btn btn-primary auth-submit"
|
|
disabled={loading}
|
|
>
|
|
{loading ? 'Signing In...' : 'Sign In'}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div className="form-footer auth-footer">
|
|
<div>
|
|
<Link to="/forgot-password">Forgot your password?</Link>
|
|
</div>
|
|
<button
|
|
type="button"
|
|
className="btn btn-secondary auth-submit"
|
|
onClick={() => navigate('/register')}
|
|
>
|
|
Join SASA
|
|
</button>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Login;
|