← back to Hub
app/login/page.tsx
162 lines
'use client';
import { useState, FormEvent } from 'react';
import { useRouter } from 'next/navigation';
import { Loader2 } from 'lucide-react';
export default function LoginPage() {
const router = useRouter();
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
async function handleSubmit(e: FormEvent<HTMLFormElement>) {
e.preventDefault();
setError('');
setLoading(true);
try {
const res = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
});
const data = await res.json();
if (res.ok && data.success) {
router.push('/');
router.refresh();
} else {
setError(data.error ?? 'Login failed. Please try again.');
}
} catch {
setError('Network error. Please try again.');
} finally {
setLoading(false);
}
}
return (
<div
className="min-h-screen flex items-center justify-center px-4"
style={{ backgroundColor: 'var(--color-bg)' }}
>
<div className="w-full max-w-sm">
{/* Logo / Brand mark */}
<div className="text-center mb-8">
<div
className="inline-flex items-center justify-center w-14 h-14 rounded-2xl mb-4"
style={{
background: 'linear-gradient(135deg, #0891b2, #0e7490)',
boxShadow: '0 4px 20px rgba(8, 145, 178, 0.3)',
}}
>
<span className="text-2xl font-black" style={{ color: '#fff' }}>
H
</span>
</div>
<h1 className="text-2xl font-bold" style={{ color: 'var(--color-text)' }}>
Hub - SDCC Command Center
</h1>
<p className="mt-1 text-sm" style={{ color: 'var(--color-text-secondary)' }}>
Sign in to your account
</p>
</div>
{/* Card */}
<div
className="rounded-xl p-6"
style={{
backgroundColor: 'var(--color-surface)',
border: '1px solid var(--color-border)',
}}
>
<form onSubmit={handleSubmit} noValidate className="space-y-4">
{/* Error message */}
{error && (
<div
className="px-3 py-2.5 rounded-lg text-sm"
style={{
backgroundColor: 'rgba(239,68,68,0.1)',
border: '1px solid rgba(239,68,68,0.3)',
color: 'var(--color-error)',
}}
role="alert"
>
{error}
</div>
)}
{/* Username */}
<div>
<label
htmlFor="username"
className="block text-sm font-medium mb-1.5"
style={{ color: 'var(--color-text-secondary)' }}
>
Username
</label>
<input
id="username"
type="text"
autoComplete="username"
autoFocus
required
className="input"
placeholder="admin"
value={username}
onChange={(e) => setUsername(e.target.value)}
disabled={loading}
/>
</div>
{/* Password */}
<div>
<label
htmlFor="password"
className="block text-sm font-medium mb-1.5"
style={{ color: 'var(--color-text-secondary)' }}
>
Password
</label>
<input
id="password"
type="password"
autoComplete="current-password"
required
className="input"
placeholder="--------"
value={password}
onChange={(e) => setPassword(e.target.value)}
disabled={loading}
/>
</div>
{/* Submit */}
<button
type="submit"
disabled={loading || !username || !password}
className="btn btn-primary btn-lg w-full mt-2"
>
{loading ? (
<>
<Loader2 size={16} className="animate-spin" />
Signing in...
</>
) : (
'Sign In'
)}
</button>
</form>
</div>
<p className="text-center text-xs mt-6" style={{ color: 'var(--color-text-muted)' }}>
SDCC Ecosystem Command Center
</p>
</div>
</div>
);
}