feat(auth): enhance registration form validation and autocomplete accessibility
All checks were successful
Deploy Flowstate to VPS / deploy (push) Successful in 1m31s

This commit is contained in:
Chneemann 2026-10-11 19:33:10 +02:00
parent 5b749bb0ee
commit c1e2dd4407
No known key found for this signature in database

View file

@ -1,6 +1,6 @@
/** /**
* @file app/(auth)/register/page.tsx * @file app/(auth)/register/page.tsx
* @description Client component providing a user registration interface utilizing the auth service. * @description Client component providing a user registration interface with client-side validation, autocomplete, and ActionButton integration.
*/ */
"use client"; "use client";
@ -14,8 +14,8 @@ import { Loader2, UserPlus } from "lucide-react";
import { JSX } from "react/jsx-runtime"; import { JSX } from "react/jsx-runtime";
/** /**
* Renders the registration page featuring a sign-up form, error feedback, * Renders the registration page featuring a sign-up form, client-side validation,
* and routing logic to the summary view upon successful account creation. * error feedback, and routing logic to the summary view upon successful account creation.
* *
* @returns {JSX.Element} The rendered registration page component. * @returns {JSX.Element} The rendered registration page component.
*/ */
@ -24,6 +24,15 @@ export default function RegisterPage(): JSX.Element {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const router = useRouter(); const router = useRouter();
/**
* Clears active error state when user modifies input fields.
*/
const handleInputChange = () => {
if (error) {
setError(null);
}
};
/** /**
* Handles user registration form submission, validates form data via auth service, * Handles user registration form submission, validates form data via auth service,
* and navigates to summary page on successful registration. * and navigates to summary page on successful registration.
@ -38,11 +47,18 @@ export default function RegisterPage(): JSX.Element {
): Promise<void> { ): Promise<void> {
event.preventDefault(); event.preventDefault();
setError(null); setError(null);
setLoading(true);
const formData = new FormData(event.currentTarget); const formData = new FormData(event.currentTarget);
const payload = Object.fromEntries(formData.entries()); const payload = Object.fromEntries(formData.entries());
// Client-side password matching check
if (payload.password !== payload.confirmPassword) {
setError("Passwords do not match.");
return;
}
setLoading(true);
const result = await registerUser(payload); const result = await registerUser(payload);
if (result.error) { if (result.error) {
@ -93,7 +109,11 @@ export default function RegisterPage(): JSX.Element {
</div> </div>
)} )}
<form onSubmit={handleSubmit} className="space-y-4"> <form
onSubmit={handleSubmit}
onChange={handleInputChange}
className="space-y-4"
>
<div className="grid grid-cols-2 gap-2.5"> <div className="grid grid-cols-2 gap-2.5">
<div className="space-y-1"> <div className="space-y-1">
<label <label
@ -108,6 +128,7 @@ export default function RegisterPage(): JSX.Element {
type="text" type="text"
placeholder="John" placeholder="John"
maxLength={50} maxLength={50}
autoComplete="given-name"
required required
className="w-full px-3.5 py-2.5 text-sm rounded-lg border border-border bg-background/50 text-foreground placeholder:text-foreground-muted/50 focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-colors" className="w-full px-3.5 py-2.5 text-sm rounded-lg border border-border bg-background/50 text-foreground placeholder:text-foreground-muted/50 focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-colors"
/> />
@ -126,6 +147,7 @@ export default function RegisterPage(): JSX.Element {
type="text" type="text"
placeholder="Doe" placeholder="Doe"
maxLength={50} maxLength={50}
autoComplete="family-name"
required required
className="w-full px-3.5 py-2.5 text-sm rounded-lg border border-border bg-background/50 text-foreground placeholder:text-foreground-muted/50 focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-colors" className="w-full px-3.5 py-2.5 text-sm rounded-lg border border-border bg-background/50 text-foreground placeholder:text-foreground-muted/50 focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-colors"
/> />
@ -145,6 +167,7 @@ export default function RegisterPage(): JSX.Element {
type="email" type="email"
placeholder="name@example.com" placeholder="name@example.com"
maxLength={255} maxLength={255}
autoComplete="email"
required required
className="w-full px-3.5 py-2.5 text-sm rounded-lg border border-border bg-background/50 text-foreground placeholder:text-foreground-muted/50 focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-colors" className="w-full px-3.5 py-2.5 text-sm rounded-lg border border-border bg-background/50 text-foreground placeholder:text-foreground-muted/50 focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-colors"
/> />
@ -163,6 +186,7 @@ export default function RegisterPage(): JSX.Element {
type="password" type="password"
placeholder="••••••••••••" placeholder="••••••••••••"
maxLength={72} maxLength={72}
autoComplete="new-password"
required required
className="w-full px-3.5 py-2.5 text-sm rounded-lg border border-border bg-background/50 text-foreground placeholder:text-foreground-muted/50 focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-colors" className="w-full px-3.5 py-2.5 text-sm rounded-lg border border-border bg-background/50 text-foreground placeholder:text-foreground-muted/50 focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-colors"
/> />
@ -181,6 +205,7 @@ export default function RegisterPage(): JSX.Element {
type="password" type="password"
placeholder="••••••••••••" placeholder="••••••••••••"
maxLength={72} maxLength={72}
autoComplete="new-password"
required required
className="w-full px-3.5 py-2.5 text-sm rounded-lg border border-border bg-background/50 text-foreground placeholder:text-foreground-muted/50 focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-colors" className="w-full px-3.5 py-2.5 text-sm rounded-lg border border-border bg-background/50 text-foreground placeholder:text-foreground-muted/50 focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-colors"
/> />