Files
manyangles/apps/web/src/app/sign-up/sign-up-form.tsx
T

145 lines
4.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useState } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { UserPlusIcon } from "lucide-react";
import { authClient } from "@/lib/auth-client";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { Field, FieldDescription, FieldGroup, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { Spinner } from "@/components/ui/spinner";
export function SignUpForm({
callbackURL,
requireInvite,
initialCode = "",
}: {
callbackURL: string;
requireInvite: boolean;
initialCode?: string;
}) {
const router = useRouter();
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [inviteCode, setInviteCode] = useState(initialCode);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
async function submit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
if (requireInvite && !inviteCode.trim()) {
setError("An invite code is required");
return;
}
setLoading(true);
setError(null);
const result = await authClient.signUp.email({
name,
email,
password,
callbackURL,
});
if (result.error) {
setError(result.error.message ?? "Unable to create account");
setLoading(false);
return;
}
if (inviteCode.trim()) {
router.push(`/invitations/${encodeURIComponent(inviteCode.trim())}`);
router.refresh();
return;
}
router.push(callbackURL);
router.refresh();
}
return (
<form className="flex flex-col gap-5" onSubmit={submit}>
<FieldGroup>
<Field>
<FieldLabel htmlFor="name">Name</FieldLabel>
<Input
id="name"
autoComplete="name"
required
value={name}
onChange={(event) => setName(event.target.value)}
className="tap-target"
/>
</Field>
<Field>
<FieldLabel htmlFor="email">Email</FieldLabel>
<Input
id="email"
type="email"
autoComplete="email"
required
value={email}
onChange={(event) => setEmail(event.target.value)}
className="tap-target"
/>
</Field>
<Field>
<FieldLabel htmlFor="password">Password</FieldLabel>
<Input
id="password"
type="password"
autoComplete="new-password"
minLength={process.env.NODE_ENV === "production" ? 10 : 5}
required
value={password}
onChange={(event) => setPassword(event.target.value)}
className="tap-target"
/>
<FieldDescription>Use at least 10 characters in production.</FieldDescription>
</Field>
{initialCode.length > 4 ? <p className="text-sm text-muted-foreground">Your invitation is attached. Youll confirm your access after creating your account.</p> : <Field>
<FieldLabel htmlFor="invite">
Invite code {requireInvite ? "" : "(optional)"}
</FieldLabel>
<Input
id="invite"
value={inviteCode}
onChange={(event) =>
setInviteCode(event.target.value.toUpperCase().replace(/[^A-Z0-9]/g, ""))
}
placeholder="AB12"
minLength={4}
maxLength={4}
pattern="[A-Za-z0-9]{4}"
autoCapitalize="characters"
autoCorrect="off"
className="tap-target"
required={requireInvite}
/>
</Field>}
</FieldGroup>
{error ? (
<Alert variant="destructive">
<AlertDescription>{error}</AlertDescription>
</Alert>
) : null}
<Button type="submit" size="lg" className="tap-target w-full" disabled={loading}>
{loading ? (
<Spinner data-icon="inline-start" />
) : (
<UserPlusIcon data-icon="inline-start" />
)}
Create account
</Button>
<p className="text-center text-sm text-muted-foreground">
Already have an account?{" "}
<Link
href={`/sign-in?callbackURL=${encodeURIComponent(callbackURL)}`}
className="font-medium text-primary hover:underline"
>
Sign in
</Link>
</p>
</form>
);
}