feat(auth): enable submit buttons only when form fields are complete
All checks were successful
Deploy Waveform to VPS / deploy (push) Successful in 1m11s
All checks were successful
Deploy Waveform to VPS / deploy (push) Successful in 1m11s
This commit is contained in:
parent
5a387609b4
commit
e0f2b22bb3
2 changed files with 63 additions and 26 deletions
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* @file app/(auth)/login/page.tsx
|
||||
* @description Client component rendering the login page, handling form submission, authentication requests, error messaging, autocomplete, and redirection.
|
||||
* @description Client component rendering the login page, handling form submission, authentication requests, error messaging, autocomplete, form validation, and redirection.
|
||||
*/
|
||||
|
||||
"use client";
|
||||
|
|
@ -11,7 +11,7 @@ import Link from "next/link";
|
|||
import Image from "next/image";
|
||||
import { loginAsGuest, loginUser } from "@/lib/services/auth.service";
|
||||
|
||||
/** Renders the user login interface and manages authentication form state. */
|
||||
/** Renders the user login interface, manages authentication form state, and enforces input completion for form submission. */
|
||||
export default function LoginPage() {
|
||||
const router = useRouter();
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
|
@ -19,6 +19,11 @@ export default function LoginPage() {
|
|||
"credentials" | "guest" | null
|
||||
>(null);
|
||||
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
|
||||
const isFormValid = email.trim() !== "" && password.trim() !== "";
|
||||
|
||||
/** Clears active error state when user modifies input fields. */
|
||||
const handleInputChange = () => {
|
||||
if (error) {
|
||||
|
|
@ -27,24 +32,22 @@ export default function LoginPage() {
|
|||
};
|
||||
|
||||
/** Form submit handler extracting credentials from FormData and delegating to handleSignIn. */
|
||||
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
const handleSubmit = async (e: React.SubmitEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.currentTarget);
|
||||
const email = formData.get("email") as string;
|
||||
const password = formData.get("password") as string;
|
||||
if (!isFormValid) return;
|
||||
await handleSignIn(email, password, "credentials");
|
||||
};
|
||||
|
||||
/** Authenticates the user with provided credentials and redirects upon success. */
|
||||
const handleSignIn = async (
|
||||
email: string,
|
||||
password: string,
|
||||
loginEmail: string,
|
||||
loginPass: string,
|
||||
type: "credentials" | "guest",
|
||||
) => {
|
||||
setError(null);
|
||||
setLoadingType(type);
|
||||
|
||||
const result = await loginUser(email, password);
|
||||
const result = await loginUser(loginEmail, loginPass);
|
||||
|
||||
if (result.error) {
|
||||
setError(result.error);
|
||||
|
|
@ -98,11 +101,7 @@ export default function LoginPage() {
|
|||
)}
|
||||
|
||||
{/* Form */}
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
onChange={handleInputChange}
|
||||
className="space-y-4"
|
||||
>
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div>
|
||||
<label
|
||||
htmlFor="email"
|
||||
|
|
@ -115,6 +114,11 @@ export default function LoginPage() {
|
|||
name="email"
|
||||
type="email"
|
||||
required
|
||||
value={email}
|
||||
onChange={(e) => {
|
||||
setEmail(e.target.value);
|
||||
handleInputChange();
|
||||
}}
|
||||
placeholder="name@example.com"
|
||||
maxLength={255}
|
||||
autoComplete="email"
|
||||
|
|
@ -134,6 +138,11 @@ export default function LoginPage() {
|
|||
name="password"
|
||||
type="password"
|
||||
required
|
||||
value={password}
|
||||
onChange={(e) => {
|
||||
setPassword(e.target.value);
|
||||
handleInputChange();
|
||||
}}
|
||||
placeholder="•••••••••••••"
|
||||
maxLength={72}
|
||||
autoComplete="current-password"
|
||||
|
|
@ -143,8 +152,8 @@ export default function LoginPage() {
|
|||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loadingType !== null}
|
||||
className="w-full bg-accent hover:opacity-90 transition-opacity text-white font-medium py-2.5 rounded-lg text-sm disabled:opacity-50 mt-2 cursor-pointer"
|
||||
disabled={loadingType !== null || !isFormValid}
|
||||
className="w-full bg-accent hover:opacity-90 transition-opacity text-white font-medium py-2.5 rounded-lg text-sm disabled:opacity-50 mt-2 cursor-pointer disabled:cursor-not-allowed"
|
||||
>
|
||||
{loadingType === "credentials" ? "Signing in..." : "Sign In"}
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* @file app/(auth)/register/page.tsx
|
||||
* @description Client component rendering the registration page, handling form submissions, client-side validation, error handling, autocomplete, and navigation.
|
||||
* @description Client component rendering the registration page, handling form submissions, client-side validation, form completion checks, error handling, autocomplete, and navigation.
|
||||
*/
|
||||
|
||||
"use client";
|
||||
|
|
@ -11,12 +11,23 @@ import Link from "next/link";
|
|||
import Image from "next/image";
|
||||
import { registerUser } from "@/lib/services/auth.service";
|
||||
|
||||
/** Renders the user registration interface and manages sign-up form state. */
|
||||
/** Renders the user registration interface, manages sign-up form state, and requires all fields to be filled before submission. */
|
||||
export default function RegisterPage() {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const router = useRouter();
|
||||
|
||||
const [username, setUsername] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [confirmPassword, setConfirmPassword] = useState("");
|
||||
|
||||
const isFormValid =
|
||||
username.trim() !== "" &&
|
||||
email.trim() !== "" &&
|
||||
password.trim() !== "" &&
|
||||
confirmPassword.trim() !== "";
|
||||
|
||||
/** Clears active error state when user modifies input fields. */
|
||||
const handleInputChange = () => {
|
||||
if (error) {
|
||||
|
|
@ -25,8 +36,9 @@ export default function RegisterPage() {
|
|||
};
|
||||
|
||||
/** Handles user registration form submission, validates form data via auth service, and navigates to summary page on successful registration. */
|
||||
async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
|
||||
async function handleSubmit(event: React.SubmitEvent<HTMLFormElement>) {
|
||||
event.preventDefault();
|
||||
if (!isFormValid) return;
|
||||
setError(null);
|
||||
|
||||
const formData = new FormData(event.currentTarget);
|
||||
|
|
@ -79,11 +91,7 @@ export default function RegisterPage() {
|
|||
)}
|
||||
|
||||
{/* Form */}
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
onChange={handleInputChange}
|
||||
className="space-y-4"
|
||||
>
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div>
|
||||
<label
|
||||
htmlFor="username"
|
||||
|
|
@ -96,6 +104,11 @@ export default function RegisterPage() {
|
|||
name="username"
|
||||
type="text"
|
||||
required
|
||||
value={username}
|
||||
onChange={(e) => {
|
||||
setUsername(e.target.value);
|
||||
handleInputChange();
|
||||
}}
|
||||
placeholder="username"
|
||||
maxLength={50}
|
||||
autoComplete="username"
|
||||
|
|
@ -115,6 +128,11 @@ export default function RegisterPage() {
|
|||
name="email"
|
||||
type="email"
|
||||
required
|
||||
value={email}
|
||||
onChange={(e) => {
|
||||
setEmail(e.target.value);
|
||||
handleInputChange();
|
||||
}}
|
||||
placeholder="name@example.com"
|
||||
maxLength={255}
|
||||
autoComplete="email"
|
||||
|
|
@ -134,6 +152,11 @@ export default function RegisterPage() {
|
|||
name="password"
|
||||
type="password"
|
||||
required
|
||||
value={password}
|
||||
onChange={(e) => {
|
||||
setPassword(e.target.value);
|
||||
handleInputChange();
|
||||
}}
|
||||
placeholder="•••••••••••••"
|
||||
maxLength={72}
|
||||
autoComplete="new-password"
|
||||
|
|
@ -153,6 +176,11 @@ export default function RegisterPage() {
|
|||
name="confirmPassword"
|
||||
type="password"
|
||||
required
|
||||
value={confirmPassword}
|
||||
onChange={(e) => {
|
||||
setConfirmPassword(e.target.value);
|
||||
handleInputChange();
|
||||
}}
|
||||
placeholder="•••••••••••••"
|
||||
maxLength={72}
|
||||
autoComplete="new-password"
|
||||
|
|
@ -162,8 +190,8 @@ export default function RegisterPage() {
|
|||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="w-full bg-accent hover:opacity-90 transition-opacity text-white font-medium py-2.5 rounded-lg text-sm disabled:opacity-50 mt-2 cursor-pointer"
|
||||
disabled={loading || !isFormValid}
|
||||
className="w-full bg-accent hover:opacity-90 transition-opacity text-white font-medium py-2.5 rounded-lg text-sm disabled:opacity-50 mt-2 cursor-pointer disabled:cursor-not-allowed"
|
||||
>
|
||||
{loading ? "Register..." : "Register"}
|
||||
</button>
|
||||
|
|
|
|||
Loading…
Reference in a new issue