frontend_nyla/src/pages/Register.tsx
2026-03-30 23:03:33 +02:00

231 lines
8.8 KiB
TypeScript

import { useState, useEffect } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import { FaEnvelopeOpenText } from 'react-icons/fa';
import styles from './Register.module.css';
import { useRegister, useMsalRegister, useUsernameAvailability } from '../hooks/useAuthentication';
import { generateAndStoreCSRFToken } from '../utils/csrfUtils';
import { PENDING_INVITATION_KEY } from './InvitePage';
interface RegisterFormData {
username: string;
email: string;
fullName: string;
}
function Register() {
const navigate = useNavigate();
const location = useLocation();
const { register, error: registerError, isLoading } = useRegister();
const { error: msalError } = useMsalRegister();
const { checkAvailability, isChecking, error: availabilityError } = useUsernameAvailability();
const invitationUsername = (location.state as any)?.invitationUsername || '';
const invitationEmail = (location.state as any)?.invitationEmail || '';
const [formData, setFormData] = useState<RegisterFormData>({
username: invitationUsername,
email: invitationEmail,
fullName: ''
});
const [validationError, setValidationError] = useState<string | null>(null);
const [successMessage, setSuccessMessage] = useState<string | null>(null);
const [usernameFocused, setUsernameFocused] = useState(false);
const [emailFocused, setEmailFocused] = useState(false);
const [fullNameFocused, setFullNameFocused] = useState(false);
const [usernameHighlight, setUsernameHighlight] = useState(false);
const pendingInvitationToken = localStorage.getItem(PENDING_INVITATION_KEY);
const hasPendingInvitation = !!pendingInvitationToken;
useEffect(() => {
document.title = "PowerOn AI Platform - Registrieren";
generateAndStoreCSRFToken();
}, []);
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
setValidationError(null);
if (name === 'username') {
setUsernameHighlight(false);
}
};
const _validateForm = (): boolean => {
if (!formData.username || !formData.email || !formData.fullName) {
setValidationError('Bitte füllen Sie alle Pflichtfelder aus.');
return false;
}
if (!formData.email.includes('@')) {
setValidationError('Bitte geben Sie eine gültige E-Mail-Adresse ein.');
return false;
}
return true;
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!_validateForm()) {
return;
}
try {
const availabilityResult = await checkAvailability(formData.username, 'local');
if (!availabilityResult.available) {
const errorMessage = availabilityResult.message || 'Username is not available';
if (errorMessage === 'Username is already taken') {
setValidationError('Benutzername ist bereits vergeben');
setUsernameHighlight(true);
} else {
setValidationError('Benutzername ist nicht verfügbar');
}
return;
}
await register({ ...formData, registrationType: 'personal' });
let message = 'Registrierung erfolgreich! Bitte prüfen Sie Ihre E-Mail (auch den Spam-Ordner) für den Link zum Setzen Ihres Passworts.';
if (hasPendingInvitation) {
message += ' Nach dem Setzen Ihres Passworts können Sie sich anmelden und Ihre Einladung annehmen.';
}
setSuccessMessage(message);
setTimeout(() => {
navigate('/login', {
state: {
registered: true,
message: 'Registrierung erfolgreich. Bitte prüfen Sie Ihre E-Mail für den Passwort-Link.',
...(location.state || {})
}
});
}, 5000);
} catch (err) {
console.error('Registration failed:', err);
}
};
const _getErrorMessage = () => {
if (validationError) return validationError;
if (registerError) return typeof registerError === 'string' ? registerError : 'Registration failed';
if (msalError) return typeof msalError === 'string' ? msalError : 'Microsoft registration failed';
if (availabilityError) return typeof availabilityError === 'string' ? availabilityError : 'Username availability check failed';
return null;
};
return (
<div className={styles.container}>
<div className={styles.mainContent}>
<div className={styles.logo}>
<img
src="/logos/poweron-logo.png"
alt="PowerOn"
className={styles.logoImage}
/>
</div>
<div className={styles.loginSection}>
<div className={styles.loginBox}>
<div className={styles.loginForm}>
{hasPendingInvitation && !successMessage && (
<div className={styles.invitationNotice}>
<FaEnvelopeOpenText className={styles.invitationIcon} />
<span>Sie haben eine ausstehende Einladung. Nach der Registrierung und Anmeldung können Sie diese annehmen.</span>
</div>
)}
{_getErrorMessage() && (
<div className={styles.error}>{_getErrorMessage()}</div>
)}
{successMessage && (
<div className={styles.success}>{successMessage}</div>
)}
{!successMessage && (
<>
<div className={styles.floatingLabelInput}>
<input
type="text"
name="username"
placeholder=" "
value={formData.username}
onChange={handleInputChange}
onFocus={() => setUsernameFocused(true)}
onBlur={() => setUsernameFocused(false)}
className={`${styles.input} ${usernameFocused || formData.username ? styles.focused : ''} ${usernameHighlight ? styles.usernameError : ''}`}
/>
<label className={usernameFocused || formData.username ? styles.focusedLabel : styles.label}>Benutzername</label>
</div>
<div className={styles.floatingLabelInput}>
<input
type="email"
name="email"
placeholder=" "
value={formData.email}
onChange={handleInputChange}
onFocus={() => setEmailFocused(true)}
onBlur={() => setEmailFocused(false)}
className={`${styles.input} ${emailFocused || formData.email ? styles.focused : ''}`}
/>
<label className={emailFocused || formData.email ? styles.focusedLabel : styles.label}>E-Mail</label>
</div>
<div className={styles.floatingLabelInput}>
<input
type="text"
name="fullName"
placeholder=" "
value={formData.fullName}
onChange={handleInputChange}
onFocus={() => setFullNameFocused(true)}
onBlur={() => setFullNameFocused(false)}
className={`${styles.input} ${fullNameFocused || formData.fullName ? styles.focused : ''}`}
/>
<label className={fullNameFocused || formData.fullName ? styles.focusedLabel : styles.label}>Vollständiger Name</label>
</div>
<div className={styles.infoMessage}>
<p>Nach der Registrierung erhalten Sie eine E-Mail mit einem Link zum Setzen Ihres Passworts.</p>
</div>
<div className={styles.disclaimer}>
<p>
Mit der Registrierung stimmen Sie unseren Datenschutzbestimmungen zur KI-Nutzung zu.
</p>
</div>
<button
className={`${styles.button} ${styles.loginButton}`}
onClick={handleSubmit}
disabled={isLoading || isChecking}
>
{isLoading ? "Registrierung läuft..." : isChecking ? "Benutzername wird geprüft..." : 'Kostenlos registrieren'}
</button>
</>
)}
<div className={styles.registerLink}>
<span>Bereits registriert?</span>
<button
className={styles.textButton}
onClick={() => navigate("/login", { state: location.state })}
>
Jetzt anmelden
</button>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
export default Register;