← back to ClawCoder
src/app/login/page.tsx
145 lines
'use client'
import { useState, FormEvent, Suspense } from 'react'
import { useRouter, useSearchParams } from 'next/navigation'
function LoginForm() {
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [error, setError] = useState('')
const [loading, setLoading] = useState(false)
const router = useRouter()
const searchParams = useSearchParams()
async function handleSubmit(e: FormEvent) {
e.preventDefault()
setLoading(true)
setError('')
const res = await fetch('/api/auth', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
})
if (res.ok) {
const redirect = searchParams.get('redirect') || '/'
window.location.href = redirect
} else {
setError('Invalid credentials')
setLoading(false)
}
}
return (
<div style={{
minHeight: '100vh',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: '#0a0a0a',
fontFamily: 'system-ui, -apple-system, sans-serif',
}}>
<form onSubmit={handleSubmit} style={{
background: '#141414',
border: '1px solid #222',
borderRadius: '12px',
padding: '40px',
width: '100%',
maxWidth: '380px',
}}>
<h1 style={{
fontSize: '20px',
fontWeight: 600,
color: '#e5e5e5',
marginBottom: '8px',
}}>ClawCoder</h1>
<p style={{
fontSize: '13px',
color: '#666',
marginBottom: '28px',
}}>Sign in to continue</p>
{error && (
<div style={{
background: '#1a0000',
border: '1px solid #3a0000',
borderRadius: '6px',
padding: '10px 14px',
fontSize: '13px',
color: '#ff4444',
marginBottom: '20px',
}}>{error}</div>
)}
<label style={{ display: 'block', marginBottom: '16px' }}>
<span style={{ fontSize: '12px', color: '#888', display: 'block', marginBottom: '6px' }}>Username</span>
<input
type="text"
value={username}
onChange={e => setUsername(e.target.value)}
autoFocus
required
style={{
width: '100%',
padding: '10px 12px',
background: '#0a0a0a',
border: '1px solid #333',
borderRadius: '6px',
color: '#e5e5e5',
fontSize: '14px',
outline: 'none',
boxSizing: 'border-box',
}}
/>
</label>
<label style={{ display: 'block', marginBottom: '24px' }}>
<span style={{ fontSize: '12px', color: '#888', display: 'block', marginBottom: '6px' }}>Password</span>
<input
type="password"
value={password}
onChange={e => setPassword(e.target.value)}
required
style={{
width: '100%',
padding: '10px 12px',
background: '#0a0a0a',
border: '1px solid #333',
borderRadius: '6px',
color: '#e5e5e5',
fontSize: '14px',
outline: 'none',
boxSizing: 'border-box',
}}
/>
</label>
<button
type="submit"
disabled={loading}
style={{
width: '100%',
padding: '11px',
background: loading ? '#333' : '#e5e5e5',
color: '#0a0a0a',
border: 'none',
borderRadius: '6px',
fontSize: '14px',
fontWeight: 600,
cursor: loading ? 'wait' : 'pointer',
}}
>{loading ? 'Signing in...' : 'Sign In'}</button>
</form>
</div>
)
}
export default function LoginPage() {
return (
<Suspense>
<LoginForm />
</Suspense>
)
}