feat(auth): enhance registration form validation and autocomplete accessibility
All checks were successful
Deploy Flowstate to VPS / deploy (push) Successful in 1m31s
All checks were successful
Deploy Flowstate to VPS / deploy (push) Successful in 1m31s
This commit is contained in:
parent
5b749bb0ee
commit
c1e2dd4407
1 changed files with 30 additions and 5 deletions
|
|
@ -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"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue