/** biome-ignore-all lint/correctness/useExhaustiveDependencies: <> */ import { USER_NAME_MAX_LENGTH, USER_USERNAME_MAX_LENGTH } from "@sprint/shared"; import { AlertTriangle, X } from "lucide-react"; import { useEffect, useState } from "react"; import { useNavigate, useSearchParams } from "react-router-dom"; import Avatar from "@/components/avatar"; import { ServerConfigurationDialog } from "@/components/server-configuration-dialog"; import { useSession } from "@/components/session-provider"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; import { Field } from "@/components/ui/field"; import { Label } from "@/components/ui/label"; import { UploadAvatar } from "@/components/upload-avatar"; import { capitalise, cn, getServerURL, setCsrfToken } from "@/lib/utils"; const DEMO_USERS = [ { name: "User 1", username: "u1", password: "a" }, { name: "User 2", username: "u2", password: "a" }, ]; export default function LogInForm() { const navigate = useNavigate(); const [searchParams] = useSearchParams(); const { setUser } = useSession(); const [loginDetailsOpen, setLoginDetailsOpen] = useState(false); const [showWarning, setShowWarning] = useState(() => { return localStorage.getItem("hide-under-construction") !== "true"; }); const [mode, setMode] = useState<"login" | "register">("login"); const [name, setName] = useState(""); const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [avatarURL, setAvatarUrl] = useState(null); const [error, setError] = useState(""); const [submitAttempted, setSubmitAttempted] = useState(false); const logIn = () => { if (username.trim() === "" || password.trim() === "") { return; } fetch(`${getServerURL()}/auth/login`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username, password }), credentials: "include", }) .then(async (res) => { if (res.status === 200) { setError(""); const data = await res.json(); setCsrfToken(data.csrfToken); setUser(data.user); const next = searchParams.get("next") || "/app"; navigate(next, { replace: true }); } // unauthorized else if (res.status === 401) { setError("Either the username or password is incorrect"); } else { setError("An unknown error occured."); } }) .catch((err) => { console.error(err); setError(`${err.statusText}`); }); }; const register = () => { if (name.trim() === "" || username.trim() === "" || password.trim() === "") { return; } fetch(`${getServerURL()}/auth/register`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name, username, password, avatarURL, }), credentials: "include", }) .then(async (res) => { if (res.status === 200) { setError(""); const data = await res.json(); setCsrfToken(data.csrfToken); setUser(data.user); const next = searchParams.get("next") || "/app"; navigate(next, { replace: true }); } // bad request (probably a bad user input) else if (res.status === 400) { setError(await res.text()); } else { setError("An unknown error occured."); } }) .catch((err) => { console.error(err); setError(`${err.statusText}`); }); }; const focusFirstInput = () => { const firstInput = document.querySelector('input[type="text"]'); if (firstInput) { (firstInput as HTMLInputElement).focus(); } }; useEffect(() => { focusFirstInput(); }, []); const resetForm = () => { setError(""); setSubmitAttempted(false); setAvatarUrl(null); requestAnimationFrame(() => focusFirstInput()); }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); setSubmitAttempted(true); if (mode === "login") { logIn(); } else { register(); } }; return ( <> {/* under construction warning */} {showWarning && (

This application is currently under construction. Your data is very likely to be lost at some point.

 

It is not recommended for production use.

But you're more than welcome to have a look around! Login Details
{DEMO_USERS.map((user) => ( ))}

)}
{capitalise(mode)}
{mode === "register" && ( <> {avatarURL && ( )} setName(e.target.value)} validate={(v) => (v.trim() === "" ? "Cannot be empty" : undefined)} submitAttempted={submitAttempted} spellcheck={false} maxLength={USER_NAME_MAX_LENGTH} /> )} setUsername(e.target.value)} validate={(v) => (v.trim() === "" ? "Cannot be empty" : undefined)} submitAttempted={submitAttempted} spellcheck={false} maxLength={USER_USERNAME_MAX_LENGTH} showCounter={mode === "register"} /> setPassword(e.target.value)} validate={(v) => (v.trim() === "" ? "Cannot be empty" : undefined)} hidden={true} submitAttempted={submitAttempted} spellcheck={false} />
{mode === "login" ? ( <> ) : ( <> )}
{error !== "" ? ( ) : ( )}
); }