import { useState } from 'react'; import { useNavigate, useSearchParams } from 'react-router-dom'; import { Header } from '@/components/layout/Header'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { Separator } from '@/components/ui/separator'; import { Zap, Mail, Lock, User, AlertCircle, Eye, EyeOff } from 'lucide-react'; import { supabase } from '@/integrations/supabase/client'; import { useToast } from '@/hooks/use-toast'; import { TurnstileCaptcha } from '@/components/auth/TurnstileCaptcha'; import { notificationService } from '@/lib/notificationService'; export default function Auth() { const [searchParams] = useSearchParams(); const navigate = useNavigate(); const { toast } = useToast(); const [loading, setLoading] = useState(false); const [magicLinkLoading, setMagicLinkLoading] = useState(false); const [showPassword, setShowPassword] = useState(false); const [captchaToken, setCaptchaToken] = useState(null); const [captchaKey, setCaptchaKey] = useState(0); const [signInCaptchaToken, setSignInCaptchaToken] = useState(null); const [signInCaptchaKey, setSignInCaptchaKey] = useState(0); const [formData, setFormData] = useState({ email: '', password: '', confirmPassword: '', username: '', displayName: '' }); const defaultTab = searchParams.get('tab') || 'signin'; const handleInputChange = (e: React.ChangeEvent) => { setFormData(prev => ({ ...prev, [e.target.name]: e.target.value })); }; const handleSignIn = async (e: React.FormEvent) => { e.preventDefault(); setLoading(true); // Validate CAPTCHA if (!signInCaptchaToken) { toast({ variant: "destructive", title: "CAPTCHA required", description: "Please complete the CAPTCHA verification." }); setLoading(false); return; } try { const { data, error } = await supabase.auth.signInWithPassword({ email: formData.email, password: formData.password, options: { captchaToken: signInCaptchaToken } }); if (error) throw error; toast({ title: "Welcome back!", description: "You've been signed in successfully." }); const redirectTo = searchParams.get('redirect') || '/'; navigate(redirectTo); } catch (error: any) { // Reset CAPTCHA on error setSignInCaptchaToken(null); setSignInCaptchaKey(prev => prev + 1); toast({ variant: "destructive", title: "Sign in failed", description: error.message }); } finally { setLoading(false); } }; const handleSignUp = async (e: React.FormEvent) => { e.preventDefault(); setLoading(true); // Validate passwords match if (formData.password !== formData.confirmPassword) { toast({ variant: "destructive", title: "Passwords don't match", description: "Please make sure your passwords match." }); setLoading(false); return; } // Validate password length if (formData.password.length < 6) { toast({ variant: "destructive", title: "Password too short", description: "Password must be at least 6 characters long." }); setLoading(false); return; } // Validate CAPTCHA if (!captchaToken) { toast({ variant: "destructive", title: "CAPTCHA required", description: "Please complete the CAPTCHA verification." }); setLoading(false); return; } try { const { data, error } = await supabase.auth.signUp({ email: formData.email, password: formData.password, options: { captchaToken, data: { username: formData.username, display_name: formData.displayName } } }); if (error) throw error; // Register user with Novu (non-blocking) if (data.user) { notificationService.createSubscriber({ subscriberId: data.user.id, email: formData.email, firstName: formData.username, // Send username as firstName to Novu data: { username: formData.username, } }).catch(err => { console.error('Failed to register Novu subscriber:', err); // Don't block signup if Novu registration fails }); } toast({ title: "Welcome to ThrillWiki!", description: "Please check your email to verify your account." }); navigate('/'); } catch (error: any) { // Reset CAPTCHA on error setCaptchaToken(null); setCaptchaKey(prev => prev + 1); toast({ variant: "destructive", title: "Sign up failed", description: error.message }); } finally { setLoading(false); } }; const handleMagicLinkSignIn = async (email: string) => { if (!email) { toast({ variant: "destructive", title: "Email required", description: "Please enter your email address to receive a magic link." }); return; } setMagicLinkLoading(true); try { const { error } = await supabase.auth.signInWithOtp({ email, options: { emailRedirectTo: `${window.location.origin}/` } }); if (error) throw error; toast({ title: "Magic link sent!", description: "Check your email for a sign-in link." }); } catch (error: any) { toast({ variant: "destructive", title: "Failed to send magic link", description: error.message }); } finally { setMagicLinkLoading(false); } }; const handleSocialSignIn = async (provider: 'google' | 'discord') => { try { const { error } = await supabase.auth.signInWithOAuth({ provider, options: { redirectTo: `${window.location.origin}/auth/callback` } }); if (error) throw error; } catch (error: any) { toast({ variant: "destructive", title: "Social sign in failed", description: error.message }); } }; return

ThrillWiki

Join the ultimate theme park community

Sign In Sign Up Welcome back Sign in to your ThrillWiki account
{ console.log('Sign-in CAPTCHA success:', token); setSignInCaptchaToken(token); }} onError={(error) => { console.log('Sign-in CAPTCHA error:', error); setSignInCaptchaToken(null); }} onExpire={() => { console.log('Sign-in CAPTCHA expired'); setSignInCaptchaToken(null); }} siteKey={import.meta.env.VITE_TURNSTILE_SITE_KEY} theme="auto" />

Enter your email above and click to receive a sign-in link

Or continue with
Create account Join the ThrillWiki community today
setCaptchaToken(null)} onExpire={() => setCaptchaToken(null)} siteKey={import.meta.env.VITE_TURNSTILE_SITE_KEY} theme="auto" className="flex justify-center" />

Skip the password - just enter your email above

Or continue with
By signing up, you agree to our Terms of Service and Privacy Policy.
; }