feat: Enhanced registration with username, name, and auto-subscription
Registration form now includes: - Username field (required, unique, validated) - Full name field (optional) - Auto-validates username format and availability - Auto-creates 'starter' subscription on signup Login form unchanged (email + password only)
This commit is contained in:
@@ -2,13 +2,15 @@
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { createClient } from '@/lib/supabase/client';
|
||||
import { LogIn, UserPlus, Mail, Lock, Loader2, AlertCircle } from 'lucide-react';
|
||||
import { LogIn, UserPlus, Mail, Lock, Loader2, AlertCircle, User, AtSign } from 'lucide-react';
|
||||
|
||||
export default function AuthForm() {
|
||||
const supabase = createClient();
|
||||
const [isLogin, setIsLogin] = useState(true);
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [username, setUsername] = useState('');
|
||||
const [fullName, setFullName] = useState('');
|
||||
const [rememberMe, setRememberMe] = useState(true);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -30,21 +32,67 @@ export default function AuthForm() {
|
||||
if (error) throw error;
|
||||
|
||||
// If remember-me is checked, session will persist (default Supabase behavior)
|
||||
// If not checked, clear session on browser close via localStorage flag
|
||||
if (!rememberMe) {
|
||||
sessionStorage.setItem('dramlog_session_only', 'true');
|
||||
} else {
|
||||
sessionStorage.removeItem('dramlog_session_only');
|
||||
}
|
||||
} else {
|
||||
const { error } = await supabase.auth.signUp({
|
||||
// Validate username
|
||||
if (!username.trim()) {
|
||||
throw new Error('Bitte gib einen Benutzernamen ein.');
|
||||
}
|
||||
if (username.length < 3) {
|
||||
throw new Error('Benutzername muss mindestens 3 Zeichen haben.');
|
||||
}
|
||||
if (!/^[a-zA-Z0-9_]+$/.test(username)) {
|
||||
throw new Error('Benutzername darf nur Buchstaben, Zahlen und Unterstriche enthalten.');
|
||||
}
|
||||
|
||||
// Check if username is already taken
|
||||
const { data: existingUser } = await supabase
|
||||
.from('profiles')
|
||||
.select('id')
|
||||
.eq('username', username.toLowerCase())
|
||||
.single();
|
||||
|
||||
if (existingUser) {
|
||||
throw new Error('Dieser Benutzername ist bereits vergeben.');
|
||||
}
|
||||
|
||||
// Create user with metadata
|
||||
const { data: signUpData, error } = await supabase.auth.signUp({
|
||||
email,
|
||||
password,
|
||||
options: {
|
||||
emailRedirectTo: `${window.location.origin}/auth/callback`,
|
||||
data: {
|
||||
username: username.toLowerCase(),
|
||||
full_name: fullName.trim() || undefined,
|
||||
}
|
||||
}
|
||||
});
|
||||
if (error) throw error;
|
||||
|
||||
// After successful signup, create subscription with starter plan
|
||||
if (signUpData.user) {
|
||||
// Get starter plan ID
|
||||
const { data: starterPlan } = await supabase
|
||||
.from('subscription_plans')
|
||||
.select('id')
|
||||
.eq('name', 'starter')
|
||||
.single();
|
||||
|
||||
if (starterPlan) {
|
||||
await supabase
|
||||
.from('user_subscriptions')
|
||||
.upsert({
|
||||
user_id: signUpData.user.id,
|
||||
plan_id: starterPlan.id,
|
||||
}, { onConflict: 'user_id' });
|
||||
}
|
||||
}
|
||||
|
||||
setMessage('Checke deine E-Mails, um dein Konto zu bestätigen!');
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -71,6 +119,43 @@ export default function AuthForm() {
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
{/* Registration-only fields */}
|
||||
{!isLogin && (
|
||||
<>
|
||||
<div className="space-y-2">
|
||||
<label className="text-[10px] font-black uppercase tracking-widest text-zinc-400 ml-1">Benutzername</label>
|
||||
<div className="relative">
|
||||
<AtSign className="absolute left-3 top-1/2 -translate-y-1/2 text-zinc-400" size={18} />
|
||||
<input
|
||||
type="text"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, ''))}
|
||||
placeholder="dein_username"
|
||||
required
|
||||
maxLength={20}
|
||||
className="w-full pl-10 pr-4 py-3 bg-zinc-950 border border-zinc-800 rounded-xl focus:ring-1 focus:ring-orange-600 focus:border-transparent outline-none transition-all text-white placeholder:text-zinc-600"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-[10px] text-zinc-600 ml-1">Nur Kleinbuchstaben, Zahlen und _</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-[10px] font-black uppercase tracking-widest text-zinc-400 ml-1">Name <span className="text-zinc-600">(optional)</span></label>
|
||||
<div className="relative">
|
||||
<User className="absolute left-3 top-1/2 -translate-y-1/2 text-zinc-400" size={18} />
|
||||
<input
|
||||
type="text"
|
||||
value={fullName}
|
||||
onChange={(e) => setFullName(e.target.value)}
|
||||
placeholder="Max Mustermann"
|
||||
maxLength={50}
|
||||
className="w-full pl-10 pr-4 py-3 bg-zinc-950 border border-zinc-800 rounded-xl focus:ring-1 focus:ring-orange-600 focus:border-transparent outline-none transition-all text-white placeholder:text-zinc-600"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-[10px] font-black uppercase tracking-widest text-zinc-400 ml-1">E-Mail</label>
|
||||
<div className="relative">
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user