63 lines
2.1 KiB
TypeScript
63 lines
2.1 KiB
TypeScript
import { useState } from "react";
|
|
|
|
export default function Login() {
|
|
const [username, setUsername] = useState("");
|
|
const [password, setPassword] = useState("");
|
|
const [error, setError] = useState("");
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setError("");
|
|
const res = await fetch("/api/login", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ username, password }),
|
|
});
|
|
if (res.ok) {
|
|
const { token } = await res.json();
|
|
localStorage.setItem("sf-auth-token", token);
|
|
window.location.href = "/";
|
|
} else {
|
|
setError("Invalid credentials");
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="flex min-h-screen items-center justify-center bg-background px-6 text-foreground">
|
|
<form
|
|
onSubmit={handleSubmit}
|
|
className="w-full max-w-sm space-y-4 rounded border border-border bg-card p-6 shadow-sm"
|
|
>
|
|
<div>
|
|
<h1 className="text-lg font-semibold">Sign in to SF</h1>
|
|
<p className="mt-1 text-sm text-muted-foreground">
|
|
Enter the local web credentials for this server.
|
|
</p>
|
|
</div>
|
|
<input
|
|
type="text"
|
|
placeholder="Username"
|
|
value={username}
|
|
autoComplete="username"
|
|
onChange={(e) => setUsername(e.target.value)}
|
|
className="h-10 w-full rounded border border-input bg-background px-3 text-sm outline-none ring-offset-background placeholder:text-muted-foreground focus-visible:ring-2 focus-visible:ring-ring"
|
|
/>
|
|
<input
|
|
type="password"
|
|
placeholder="Password"
|
|
value={password}
|
|
autoComplete="current-password"
|
|
onChange={(e) => setPassword(e.target.value)}
|
|
className="h-10 w-full rounded border border-input bg-background px-3 text-sm outline-none ring-offset-background placeholder:text-muted-foreground focus-visible:ring-2 focus-visible:ring-ring"
|
|
/>
|
|
<button
|
|
type="submit"
|
|
className="h-10 w-full rounded bg-primary px-4 text-sm font-medium text-primary-foreground hover:bg-primary/90"
|
|
>
|
|
Sign In
|
|
</button>
|
|
{error && <div className="text-sm text-destructive">{error}</div>}
|
|
</form>
|
|
</div>
|
|
);
|
|
}
|