'use client' import { useState } from 'react' import { useRouter } from 'next/navigation' import Link from 'next/link' import { createClient } from '@/lib/supabase/client' import { oauthProviders, anyOAuthProvider } from '@/lib/auth-config' export default function LoginPage() { const router = useRouter() const oauth = oauthProviders() const showOAuth = anyOAuthProvider(oauth) const [email, setEmail] = useState('') const [password, setPassword] = useState('') const [error, setError] = useState('') const [loading, setLoading] = useState(false) const [oauthLoading, setOauthLoading] = useState<'github' | 'google' | null>(null) // Honor ?next= so a signed-out visitor to a public_auth form (or any gated // link) returns where they were headed. Only same-origin absolute paths are // allowed — never an external URL or protocol-relative // — to avoid an // open-redirect. function safeNext(): string { if (typeof window === 'undefined') return '/dashboard' const raw = new URLSearchParams(window.location.search).get('next') if (raw && raw.startsWith('/') && !raw.startsWith('//') && !raw.startsWith('/\\')) { return raw } return '/dashboard' } async function handleSubmit(e: React.FormEvent) { e.preventDefault() setLoading(true) setError('') const supabase = createClient() const { error } = await supabase.auth.signInWithPassword({ email, password }) if (error) { setError(error.message) setLoading(false) } else { router.push(safeNext()) router.refresh() } } async function handleOAuthLogin(provider: 'github' | 'google') { setOauthLoading(provider) setError('') const supabase = createClient() const { error } = await supabase.auth.signInWithOAuth({ provider, options: { redirectTo: `${window.location.origin}/auth/callback?next=${encodeURIComponent(safeNext())}`, }, }) if (error) { setError(error.message) setOauthLoading(null) } } return (
{/* Grid background */}
{/* Yellow top border */}
{/* Logo */}
{'{'} hackerspace.sh {'}'}

Sign in to your space

setEmail(e.target.value)} placeholder="moheeb@heatsyncabs.org" required className="w-full bg-[#232323] border border-[#333] text-white font-mono text-sm rounded px-3 py-2.5 placeholder:text-zinc-600 focus:outline-none focus:border-[#d4f53c] transition" />
setPassword(e.target.value)} placeholder="············" required className="w-full bg-[#232323] border border-[#333] text-white font-mono text-sm rounded px-3 py-2.5 placeholder:text-zinc-600 focus:outline-none focus:border-[#d4f53c] transition" />
{error && (

{error}

)}
{showOAuth && ( <>
OR
{oauth.github && ( )} {oauth.google && ( )}
)}

no account?{' '} create or join a space →

← Back to homepage

) }