231 lines
8.8 KiB
TypeScript
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;
|