55 lines
1.5 KiB
TypeScript
55 lines
1.5 KiB
TypeScript
import React, { useState } from 'react'
|
|
|
|
interface Props {
|
|
onLogin: (username: string, password: string) => Promise<void>
|
|
}
|
|
|
|
export default function LoginPage({ onLogin }: Props) {
|
|
const [username, setUsername] = useState('')
|
|
const [password, setPassword] = useState('')
|
|
const [error, setError] = useState('')
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault()
|
|
setError('')
|
|
setLoading(true)
|
|
try {
|
|
await onLogin(username, password)
|
|
} catch {
|
|
setError('Login failed. Check username and password.')
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="login-page">
|
|
<div className="login-card">
|
|
<h1>⚓ HarborForge</h1>
|
|
<p className="subtitle">Agent/Human collaborative task management platform</p>
|
|
<form onSubmit={handleSubmit}>
|
|
<input
|
|
type="text"
|
|
placeholder="Username"
|
|
value={username}
|
|
onChange={(e) => setUsername(e.target.value)}
|
|
required
|
|
/>
|
|
<input
|
|
type="password"
|
|
placeholder="Password"
|
|
value={password}
|
|
onChange={(e) => setPassword(e.target.value)}
|
|
required
|
|
/>
|
|
{error && <p className="error">{error}</p>}
|
|
<button type="submit" disabled={loading}>
|
|
{loading ? 'Signing in...' : 'Sign in'}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|