AuthForm.tsx•2.72 kB
"use client";
import { useState } from "react";
import { fetch, useBoolean } from "@/utils";
import { CTAButton, Input } from "@/ui/elements";
import { LoadingIndicator } from '@/ui/App';
interface AuthFormPayload extends HTMLFormElement {
email: HTMLInputElement;
password: HTMLInputElement;
}
const errorsMap = {
LOGIN_BAD_CREDENTIALS: "Invalid username or password",
REGISTER_USER_ALREADY_EXISTS: "User already exists",
};
const defaultFormatPayload: (data: { email: string; password: string; }) => object = (data) => data;
export default function AuthForm({
submitButtonText = "Sign in",
authUrl = "/v1/auth/login",
formatPayload = defaultFormatPayload,
onSignInSuccess = () => window.location.href = "/",
}) {
const {
value: isSigningIn,
setTrue: disableSignIn,
setFalse: enableSignIn,
} = useBoolean(false);
const [signInError, setSignInError] = useState<string | null>(null);
const signIn = (event: React.FormEvent<AuthFormPayload>) => {
event.preventDefault();
const formElements = event.currentTarget;
// Backend expects username and password fields
const authCredentials = {
email: formElements.email.value,
password: formElements.password.value,
};
setSignInError(null);
disableSignIn();
const formattedPayload = formatPayload(authCredentials);
fetch(authUrl, {
method: "POST",
body: formattedPayload instanceof URLSearchParams ? formattedPayload.toString() : JSON.stringify(formattedPayload),
headers: {
"Content-Type": formattedPayload instanceof URLSearchParams ? "application/x-www-form-urlencoded" : "application/json",
},
})
.then(() => {
onSignInSuccess();
})
.catch(error => setSignInError(errorsMap[error.detail as keyof typeof errorsMap] || error.message))
.finally(() => enableSignIn());
};
return (
<form onSubmit={signIn} className="flex flex-col gap-4">
<label className="flex flex-col gap-1">
Email address*
<Input type="email" name="email" required placeholder="Email address*" defaultValue="default_user@example.com" />
</label>
<label className="flex flex-col gap-1">
Password*
<Input type="password" name="password" required placeholder="Password*" defaultValue="default_password" />
</label>
<CTAButton className="mt-6 mb-2" type="submit">
{submitButtonText}
{isSigningIn && <LoadingIndicator />}
</CTAButton>
{signInError && (
<span className="text-s text-red-500 mb-4">{signInError}</span>
)}
</form>
);
}