Created
May 1, 2024 04:54
-
-
Save career-tokens/024d57156d774259d9e8aedbaf8c7646 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use client"; | |
import { ApiResponse } from "@/types/ApiResponse"; | |
import { zodResolver } from "@hookform/resolvers/zod"; | |
import Link from "next/link"; | |
import { useEffect, useState } from "react"; | |
import { useForm } from "react-hook-form"; | |
import { useDebounceCallback } from "usehooks-ts"; | |
import * as z from "zod"; | |
import { Button } from "@/components/ui/button"; | |
import { | |
Form, | |
FormField, | |
FormItem, | |
FormLabel, | |
FormMessage, | |
} from "@/components/ui/form"; | |
import { Input } from "@/components/ui/input"; | |
import { useToast } from "@/components/ui/use-toast"; | |
import axios, { AxiosError } from "axios"; | |
import { Loader2 } from "lucide-react"; | |
import { useRouter } from "next/navigation"; | |
import { signUpSchema } from "@/schemas/signUpSchema"; | |
export default function SignUpForm() { | |
//state for storing username | |
const [username, setUsername] = useState(""); | |
//state for storing the message received from the backend regarding whether | |
//username is unique or not | |
const [usernameMessage, setUsernameMessage] = useState(""); | |
//state for storing boolean regarding whether backend has responded yet or not | |
//helps in keeping the loader alive till then | |
const [isCheckingUsername, setIsCheckingUsername] = useState(false); | |
//similarly this is for the submition part | |
const [isSubmitting, setIsSubmitting] = useState(false); | |
//returns the debounced function which will update the username after 300ms and | |
//if the user is typing, the value username needs to change to that value and the final value | |
//it needs to change to keeps getting updated due to typing but once the typing stops for more than 300ms | |
//, the state's value changes to the final value and triggers a re-render | |
const debounced = useDebounceCallback(setUsername, 300); | |
const router = useRouter(); | |
const { toast } = useToast(); | |
const form = useForm<z.infer<typeof signUpSchema>>({ | |
//uses Zod resolver using the signUpSchema we made earlier | |
resolver: zodResolver(signUpSchema), | |
defaultValues: { | |
username: "", | |
email: "", | |
password: "", | |
}, | |
}); | |
useEffect(() => { | |
const checkUsernameUnique = async () => { | |
//since anyways the username is getting updated after 300ms so | |
if (username) { | |
//mark the start of the loader | |
setIsCheckingUsername(true); | |
setUsernameMessage(""); // Reset message | |
try { | |
//make a call to the API | |
const response = await axios.get<ApiResponse>( | |
`/api/check-username-unique?username=${username}` | |
); | |
//get the message | |
setUsernameMessage(response.data.message); | |
} catch (error) { | |
const axiosError = error as AxiosError<ApiResponse>; | |
setUsernameMessage( | |
axiosError.response?.data.message ?? "Error checking username" | |
); | |
} finally { | |
//either it works fine or error occurs, we gotta end the loader | |
setIsCheckingUsername(false); | |
} | |
} | |
}; | |
checkUsernameUnique(); | |
}, [username]); | |
const onSubmit = async (data: z.infer<typeof signUpSchema>) => { | |
//start the loader | |
setIsSubmitting(true); | |
try { | |
//make the POST request to the API with the data | |
const response = await axios.post<ApiResponse>("/api/sign-up", data); | |
toast({ | |
title: "Success", | |
description: response.data.message, | |
}); | |
//on success, go for verification | |
router.replace(`/verify/${username}`); | |
//end the loader | |
setIsSubmitting(false); | |
} catch (error) { | |
console.error("Error during sign-up:", error); | |
const axiosError = error as AxiosError<ApiResponse>; | |
// Default error message | |
let errorMessage = axiosError.response?.data.message; | |
("There was a problem with your sign-up. Please try again."); | |
toast({ | |
title: "Sign Up Failed", | |
description: errorMessage, | |
variant: "destructive", | |
}); | |
//end the loader | |
setIsSubmitting(false); | |
} | |
}; | |
return ( | |
<div className="flex justify-center items-center min-h-screen"> | |
<div className="w-full max-w-md p-8 space-y-8 bg-white rounded-lg shadow-md"> | |
<div className="text-center"> | |
<h1 className="text-3xl font-extrabold tracking-tight lg:text-5xl mb-6"> | |
Join the Community | |
</h1> | |
<p className="mb-4">Sign up to get started</p> | |
</div> | |
<Form {...form}> | |
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6"> | |
<FormField | |
name="username" | |
control={form.control} | |
render={({ field }) => ( | |
<FormItem> | |
<FormLabel>Username</FormLabel> | |
<Input | |
{...field} | |
onChange={(e: any) => { | |
field.onChange(e); | |
debounced(e.target.value); | |
}} | |
/> | |
{isCheckingUsername && <Loader2 className="animate-spin" />} | |
{!isCheckingUsername && usernameMessage && ( | |
<p | |
className={`text-sm ${ | |
usernameMessage === "Username is available" | |
? "text-green-500" | |
: "text-red-500" | |
}`} | |
> | |
{usernameMessage} | |
</p> | |
)} | |
<FormMessage /> | |
</FormItem> | |
)} | |
/> | |
<FormField | |
name="email" | |
control={form.control} | |
render={({ field }) => ( | |
<FormItem> | |
<FormLabel>Email</FormLabel> | |
<Input {...field} name="email" /> | |
<p className="text-muted text-gray-400 text-sm"> | |
We will send you a verification code | |
</p> | |
<FormMessage /> | |
</FormItem> | |
)} | |
/> | |
<FormField | |
name="password" | |
control={form.control} | |
render={({ field }) => ( | |
<FormItem> | |
<FormLabel>Password</FormLabel> | |
<Input type="password" {...field} name="password" /> | |
<FormMessage /> | |
</FormItem> | |
)} | |
/> | |
<Button type="submit" className="w-full" disabled={isSubmitting}> | |
{isSubmitting ? ( | |
<> | |
<Loader2 className="mr-2 h-4 w-4 animate-spin" /> | |
Please wait | |
</> | |
) : ( | |
"Sign Up" | |
)} | |
</Button> | |
</form> | |
</Form> | |
<div className="text-center mt-4"> | |
<p> | |
Already a member?{" "} | |
<Link href="/sign-in" className="text-blue-600 hover:text-blue-800"> | |
Sign in | |
</Link> | |
</p> | |
</div> | |
</div> | |
</div> | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment