106 lines
3.7 KiB
TypeScript
106 lines
3.7 KiB
TypeScript
"use client";
|
|
import { useState, type FormEvent } from "react";
|
|
import { useRouter } from "next/navigation";
|
|
|
|
export default function LoginPage() {
|
|
const router = useRouter();
|
|
const [user, setUser] = useState("");
|
|
const [password, setPassword] = useState("");
|
|
const [error, setError] = useState("");
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
async function onSubmit(e: FormEvent) {
|
|
e.preventDefault();
|
|
setLoading(true);
|
|
setError("");
|
|
try {
|
|
const res = await fetch("/api/auth/login", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ user, password }),
|
|
});
|
|
if (res.ok) {
|
|
router.push("/");
|
|
router.refresh();
|
|
} else {
|
|
setError("Zugangsdaten ungültig");
|
|
}
|
|
} catch {
|
|
setError("Zugangsdaten ungültig");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<main className="grid min-h-dvh place-items-center bg-wash px-5 py-9">
|
|
<div className="w-full max-w-[420px]">
|
|
<p className="mb-6 flex items-center gap-2.5 text-base font-semibold tracking-tight">
|
|
<span
|
|
aria-hidden="true"
|
|
className="grid h-9 w-9 place-items-center rounded-[11px] bg-brand-100 text-[17px] font-bold text-brand-800"
|
|
>
|
|
G
|
|
</span>
|
|
Gutscheintracker
|
|
</p>
|
|
<section
|
|
aria-labelledby="login-title"
|
|
className="rounded-card border border-line bg-white p-8 shadow-[0_16px_38px_rgba(32,55,39,0.07)]"
|
|
>
|
|
<h1 id="login-title" className="text-[27px] font-semibold leading-snug tracking-tight">
|
|
Willkommen zurück
|
|
</h1>
|
|
<p className="mb-7 mt-2 text-sm leading-relaxed text-muted">
|
|
Melde dich mit eurem gemeinsamen Haushaltszugang an.
|
|
</p>
|
|
<form onSubmit={onSubmit} className="grid gap-[18px]" noValidate>
|
|
<div className="grid gap-[7px]">
|
|
<label htmlFor="user" className="text-[13px] font-semibold">
|
|
Benutzername
|
|
</label>
|
|
<input
|
|
id="user"
|
|
value={user}
|
|
onChange={(e) => setUser(e.target.value)}
|
|
autoComplete="username"
|
|
placeholder="haushalt"
|
|
className="h-[46px] rounded-field border border-line bg-white px-3.5 text-sm text-ink placeholder:text-muted/70"
|
|
/>
|
|
</div>
|
|
<div className="grid gap-[7px]">
|
|
<label htmlFor="password" className="text-[13px] font-semibold">
|
|
Passwort
|
|
</label>
|
|
<input
|
|
id="password"
|
|
type="password"
|
|
value={password}
|
|
onChange={(e) => setPassword(e.target.value)}
|
|
autoComplete="current-password"
|
|
placeholder="Dein Passwort"
|
|
className="h-[46px] rounded-field border border-line bg-white px-3.5 text-sm text-ink placeholder:text-muted/70"
|
|
/>
|
|
</div>
|
|
{error && (
|
|
<p role="alert" className="rounded-field bg-danger/10 px-3.5 py-2.5 text-sm font-medium text-danger">
|
|
{error}
|
|
</p>
|
|
)}
|
|
<button
|
|
type="submit"
|
|
disabled={loading}
|
|
className="h-[47px] rounded-field bg-brand-600 text-sm font-semibold text-white transition-colors hover:bg-brand-700 active:scale-[0.99] disabled:opacity-60"
|
|
>
|
|
{loading ? "Wird geprüft …" : "Anmelden"}
|
|
</button>
|
|
</form>
|
|
<p className="mt-[19px] text-center text-xs text-muted">
|
|
Privater Zugang für euren Haushalt
|
|
</p>
|
|
</section>
|
|
</div>
|
|
</main>
|
|
);
|
|
}
|