102 lines
2.9 KiB
TypeScript
102 lines
2.9 KiB
TypeScript
"use client";
|
||
import { useState } from "react";
|
||
import { useRouter } from "next/navigation";
|
||
|
||
export default function SignupForm() {
|
||
const router = useRouter();
|
||
const [email, setEmail] = useState("");
|
||
const [username, setUsername] = useState("");
|
||
const [password, setPassword] = useState("");
|
||
const [busy, setBusy] = useState(false);
|
||
const [error, setError] = useState("");
|
||
const [done, setDone] = useState<null | { admin: boolean }>(null);
|
||
|
||
async function submit(e: React.FormEvent) {
|
||
e.preventDefault();
|
||
setError("");
|
||
setBusy(true);
|
||
try {
|
||
const res = await fetch("/api/auth/signup", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ email, username, password }),
|
||
});
|
||
const data = await res.json();
|
||
if (!res.ok) {
|
||
setError(data.error || "Something went wrong.");
|
||
} else if (data.admin) {
|
||
router.push("/admin");
|
||
router.refresh();
|
||
} else {
|
||
setDone({ admin: false });
|
||
}
|
||
} catch {
|
||
setError("Network error. Please try again.");
|
||
} finally {
|
||
setBusy(false);
|
||
}
|
||
}
|
||
|
||
if (done) {
|
||
return (
|
||
<div className="notice ok" role="status">
|
||
🎉 Almost there! We sent a confirmation link to <strong>{email}</strong>. Open it to activate
|
||
your account, then log in. (No confirmation email service set up yet? The link is printed in
|
||
the server console.)
|
||
</div>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<form onSubmit={submit} noValidate>
|
||
{error && (
|
||
<div className="notice err" role="alert">
|
||
{error}
|
||
</div>
|
||
)}
|
||
<div className="field">
|
||
<label htmlFor="email">Email</label>
|
||
<input
|
||
id="email"
|
||
type="email"
|
||
autoComplete="email"
|
||
inputMode="email"
|
||
value={email}
|
||
onChange={(e) => setEmail(e.target.value)}
|
||
required
|
||
/>
|
||
</div>
|
||
<div className="field">
|
||
<label htmlFor="username">Username</label>
|
||
<input
|
||
id="username"
|
||
type="text"
|
||
autoComplete="username"
|
||
value={username}
|
||
onChange={(e) => setUsername(e.target.value)}
|
||
placeholder="3–24 letters, numbers, _ or -"
|
||
required
|
||
/>
|
||
</div>
|
||
<div className="field">
|
||
<label htmlFor="password">Password</label>
|
||
<input
|
||
id="password"
|
||
type="password"
|
||
autoComplete="new-password"
|
||
value={password}
|
||
onChange={(e) => setPassword(e.target.value)}
|
||
placeholder="At least 6 characters"
|
||
required
|
||
/>
|
||
</div>
|
||
<button className="btn big" type="submit" disabled={busy} style={{ width: "100%" }}>
|
||
{busy ? "Creating…" : "Create account"}
|
||
</button>
|
||
<p className="center muted" style={{ marginTop: 14 }}>
|
||
Already have an account? <a href="/login">Log in</a>
|
||
</p>
|
||
</form>
|
||
);
|
||
}
|