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
233 lines
7.7 KiB
TypeScript
233 lines
7.7 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { Link, useNavigate } from 'react-router-dom';
|
|
import { authService, RegisterData } from '../services/membershipService';
|
|
|
|
const Register: React.FC = () => {
|
|
const navigate = useNavigate();
|
|
const [formData, setFormData] = useState<RegisterData>({
|
|
email: '',
|
|
password: '',
|
|
first_name: '',
|
|
last_name: '',
|
|
phone: '',
|
|
address: ''
|
|
});
|
|
const [confirmPassword, setConfirmPassword] = useState('');
|
|
const [passwordsMatch, setPasswordsMatch] = useState(true);
|
|
const [error, setError] = useState('');
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
|
setFormData({
|
|
...formData,
|
|
[e.target.name]: e.target.value
|
|
});
|
|
};
|
|
|
|
const handlePasswordChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
const { name, value } = e.target;
|
|
if (name === 'password') {
|
|
setFormData(prev => ({ ...prev, password: value }));
|
|
setPasswordsMatch(value === confirmPassword);
|
|
} else if (name === 'confirmPassword') {
|
|
setConfirmPassword(value);
|
|
setPasswordsMatch(formData.password === value);
|
|
}
|
|
};
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setError('');
|
|
|
|
// Validate password confirmation
|
|
if (formData.password !== confirmPassword) {
|
|
setError('Passwords do not match. Please try again.');
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
|
|
try {
|
|
// Register the user
|
|
await authService.register(formData);
|
|
|
|
// Automatically log in the user
|
|
await authService.login({
|
|
email: formData.email,
|
|
password: formData.password
|
|
});
|
|
|
|
// Redirect to dashboard
|
|
navigate('/dashboard');
|
|
} catch (err: any) {
|
|
setError(err.response?.data?.detail || 'Registration failed. Please try again.');
|
|
} 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">Membership registration and profile setup</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<main className="auth-container auth-container-wide">
|
|
<section className="auth-welcome-card">
|
|
<div className="auth-kicker">New Membership</div>
|
|
<h2>Join the SASA community</h2>
|
|
<p>
|
|
Create your account to manage your membership, respond to events, and keep your contact details up to date.
|
|
</p>
|
|
<div className="auth-feature-list">
|
|
<div className="auth-feature-item">Straightforward onboarding with automatic sign-in</div>
|
|
<div className="auth-feature-item">Membership tiers, payments, and event RSVPs in one place</div>
|
|
<div className="auth-feature-item">A separate admin workspace for staff users after login</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section className="auth-card auth-card-wide">
|
|
<div className="auth-card-head">
|
|
<h2>Create Account</h2>
|
|
<span>Step 1 of 1</span>
|
|
</div>
|
|
|
|
<div className="auth-card-body">
|
|
<p className="auth-card-copy">
|
|
Complete the essentials below. You can add or update the rest of your profile later from your dashboard.
|
|
</p>
|
|
|
|
{error && <div className="alert alert-error">{error}</div>}
|
|
|
|
<form onSubmit={handleSubmit} className="auth-form-grid">
|
|
<div className="form-group">
|
|
<label htmlFor="first_name">First Name *</label>
|
|
<input
|
|
type="text"
|
|
id="first_name"
|
|
name="first_name"
|
|
value={formData.first_name}
|
|
onChange={handleChange}
|
|
autoComplete="given-name"
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label htmlFor="last_name">Last Name *</label>
|
|
<input
|
|
type="text"
|
|
id="last_name"
|
|
name="last_name"
|
|
value={formData.last_name}
|
|
onChange={handleChange}
|
|
autoComplete="family-name"
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label htmlFor="email">Email Address *</label>
|
|
<input
|
|
type="email"
|
|
id="email"
|
|
name="email"
|
|
value={formData.email}
|
|
onChange={handleChange}
|
|
autoComplete="email"
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label htmlFor="phone">Phone</label>
|
|
<input
|
|
type="tel"
|
|
id="phone"
|
|
name="phone"
|
|
value={formData.phone}
|
|
onChange={handleChange}
|
|
autoComplete="tel"
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label htmlFor="password">Password *</label>
|
|
<input
|
|
type="password"
|
|
id="password"
|
|
name="password"
|
|
value={formData.password}
|
|
onChange={handlePasswordChange}
|
|
autoComplete="new-password"
|
|
minLength={8}
|
|
required
|
|
/>
|
|
<small className="form-hint">Minimum 8 characters.</small>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label htmlFor="confirmPassword">Confirm Password *</label>
|
|
<input
|
|
type="password"
|
|
id="confirmPassword"
|
|
name="confirmPassword"
|
|
value={confirmPassword}
|
|
onChange={handlePasswordChange}
|
|
autoComplete="new-password"
|
|
minLength={8}
|
|
className={confirmPassword ? (passwordsMatch ? 'field-success' : 'field-error') : ''}
|
|
required
|
|
/>
|
|
{confirmPassword ? (
|
|
<small className={passwordsMatch ? 'form-hint hint-success' : 'form-hint hint-error'}>
|
|
{passwordsMatch ? 'Passwords match.' : 'Passwords do not match.'}
|
|
</small>
|
|
) : (
|
|
<small className="form-hint">Re-enter your password to confirm it.</small>
|
|
)}
|
|
</div>
|
|
|
|
<div className="form-group form-group-full">
|
|
<label htmlFor="address">Address</label>
|
|
<textarea
|
|
id="address"
|
|
name="address"
|
|
value={formData.address}
|
|
onChange={handleChange}
|
|
rows={4}
|
|
autoComplete="street-address"
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-group-full">
|
|
<button
|
|
type="submit"
|
|
className="btn btn-primary auth-submit"
|
|
disabled={loading}
|
|
>
|
|
{loading ? 'Creating Account...' : 'Create Account & Sign In'}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<div className="auth-footer">
|
|
<div>
|
|
Already have an account? <Link to="/login">Log in</Link>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Register;
|