Skip to main content

login-form

This code defines a login form component using React, Next.js, and several libraries such as react-hook-form, Zod, and Framer Motion. The form accepts user credentials (SID and PIN), sends an authentication request, and manages the login state.

Key Features and Breakdown:


1. Component Setup:

State Management (useState):

const [isLoading, setIsLoading] = useState(false);
tip

The isLoading state is used to manage the loading indicator during the login process, preventing multiple submissions and providing a visual cue to the user.

  • isLoading is used to manage the loading state of the form during the login process (shows a loading indicator or disables the button).

Form Handling (react-hook-form and zod):

const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
sid: "",
pin: "",
},
});
tip

The form is managed using react-hook-form for state management and Zod for schema validation. This combination ensures smooth validation and form handling.

  • The form state and validation are managed using react-hook-form and Zod. The zodResolver integrates Zod schema validation into the form.

Zod Schema Validation:

const formSchema = z.object({
sid: z.string().regex(/^\d{6}$/, {message: "User ID must be a 6-digit numeric code."}).min(1, {message: "Please enter a valid user ID."}),
pin: z.string().max(4).min(4, {"message": "pin must be min 4 digits long"}),
});
warning

Ensure that the sid field is strictly validated to be exactly 6 digits, and the pin field must be a 4-digit number. This prevents potential errors from invalid data.

  • The schema validates:
    • sid: A 6-digit user ID.
    • pin: A 4-digit numeric pin.

Input OTP (One-Time Password):

<InputOTP maxLength={4} {...field} pattern={REGEXP_ONLY_DIGITS}>
<InputOTPGroup>
<InputOTPSlot index={0}/>
<InputOTPSlot index={1}/>
<InputOTPSlot index={2}/>
<InputOTPSlot index={3}/>
</InputOTPGroup>
</InputOTP>

The OTP input is implemented using InputOTP, where each digit is separated into individual slots. This creates a smooth UX for PIN entry.

  • This custom component handles OTP (one-time password) input using InputOTP, where each digit is entered separately in individual slots.

2. Login Flow:

Form Submission (onSubmit function):

async function onSubmit(values: z.infer<typeof formSchema>) {
setIsLoading(true);
let json: Record<string, unknown> = {};
let username = "";
try {
await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate delay

// Authentication API call
const response = await fetch(apilink + '/trylogUser', {
method: 'GET',
headers: {
'accept': 'application/json',
'sid': values.sid,
'pin': values.pin
}
});

// Handle success or failure
if (response.status !== 200) throw new Error("Invalid credentials");

const response2 = await fetch(apilink + '/getUser', {
method: 'GET',
headers: {
'accept': 'application/json',
'sid': values.sid,
'pin': values.pin
}
});
json = await response2.json();
username = (json.data as { fullname: string }).fullname;

if (response2.status !== 200) throw new Error("Error getting name");

// Toast on successful login
toast({
title: "Login Successful",
description: "You have been logged in successfully, " + username
});

// Store credentials and username as cookies
document.cookie = `user=${JSON.stringify(values)}; max-age=${3 * 60 * 60}; path=/`;
document.cookie = `name=${JSON.stringify(username)}; max-age=${3 * 60 * 60}; path=/`;

// Trigger page refresh
router.refresh();
} catch {
toast({
title: "Login Failed",
description: "There was an error logging in. Please try again.",
variant: "destructive",
});
} finally {
setIsLoading(false);
}
}
tip

The onSubmit function handles the form submission, validates the credentials, manages the login process, and handles success/failure with appropriate feedback. Make sure to handle errors properly to guide users effectively.

  • Authentication Process:

    • The form submits sid (user ID) and pin (PIN) to the API for validation.
    • If valid, the user’s name is fetched and stored in cookies along with login credentials.
  • Error Handling:

    • If the credentials are invalid or if there's an error, a toast notification is shown to inform the user.
  • Cookie Management:

    • The login credentials and username are stored in cookies (document.cookie), making them accessible on the client-side for subsequent requests.
  • Page Refresh:

    • After a successful login, the router.refresh() is called to trigger a re-render or refresh the current page.

3. Component Rendering (Form UI):

Form Layout:

<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField control={form.control} name="sid" render={({field}) => (
<FormItem>
<FormLabel>Names</FormLabel>
<FormControl>
<Input placeholder="0XXXXX" {...field} />
</FormControl>
<FormDescription>
Enter your name.
</FormDescription>
<FormMessage/>
</FormItem>
)} />

<FormField control={form.control} name="pin" render={({field}) => (
<FormItem>
<FormLabel>
<div className="flex justify-between">
<span>Pin</span>
<span className="text-sm text-gray-500 dark:text-gray-400">4 digits</span>
</div>
</FormLabel>
<FormControl>
<div className="flex justify-center">
<InputOTP maxLength={4} {...field} pattern={REGEXP_ONLY_DIGITS}>
<InputOTPGroup>
<InputOTPSlot index={0}/>
<InputOTPSlot index={1}/>
<InputOTPSlot index={2}/>
<InputOTPSlot index={3}/>
</InputOTPGroup>
</InputOTP>
</div>
</FormControl>
<FormDescription>
Please enter the code that you found in the envelope.
</FormDescription>
<FormMessage/>
</FormItem>
)} />

<Button type="submit" className="w-full" disabled={isLoading}>
{isLoading ? "Logging in..." : "Log in"}
</Button>
</form>
</Form>
warning

Ensure that the button is disabled while the form is loading, preventing multiple submissions. The loading state helps avoid errors caused by user impatience.

  • Form Fields:

    • sid (user ID) and pin (4-digit PIN) fields are rendered.
    • InputOTP is used for entering the PIN, broken into individual slots for each digit.
  • Button:

    • The button is disabled when the form is in a loading state (e.g., while waiting for API responses).

Summary:

This code implements a login form that:

  • Collects a 6-digit user ID (sid) and a 4-digit PIN (pin).
  • Sends the credentials to an API for validation and fetches the user's full name.
  • Stores login information in cookies.
  • Uses react-hook-form for managing form state and Zod for schema validation.
  • Displays toast notifications for success or failure.

The form also provides a custom OTP input (InputOTP) for entering the PIN, enhancing the UX by separating each digit in its own input slot.