LoginForm.tsx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. "use client";
  2. import clsx from "clsx";
  3. import { signIn } from "next-auth/react";
  4. import Image from "next/image";
  5. import Link from "next/link";
  6. import { useRouter } from "next/navigation";
  7. import { SubmitHandler, useForm } from "react-hook-form";
  8. import { Button } from "@components/common/button/Button";
  9. import { EMAIL_REGEX, SIGNIN_IMG } from "@/utils/constants";
  10. import InputText from "@components/common/form/Input";
  11. import { useCustomToast } from "@/utils/hooks/useToast";
  12. import { mergeCartAction, deleteGuestCookieAction } from "@/actions";
  13. import { useAppSelector } from "@/store/hooks";
  14. import { useCartDetail } from "@utils/hooks/useCartDetail";
  15. type LoginFormInputs = {
  16. username: string;
  17. password: string;
  18. };
  19. export default function LoginForm() {
  20. const router = useRouter();
  21. const { showToast } = useCustomToast();
  22. const { getCartDetail } = useCartDetail()
  23. const cart = useAppSelector((state) => state.cartDetail.cart);
  24. const {
  25. register,
  26. handleSubmit,
  27. formState: { errors, isSubmitting },
  28. } = useForm<LoginFormInputs>({
  29. mode: "onSubmit",
  30. reValidateMode: "onChange",
  31. });
  32. const onSubmit: SubmitHandler<LoginFormInputs> = async (data) => {
  33. try {
  34. const result = await signIn("credentials", {
  35. redirect: false,
  36. ...data,
  37. callbackUrl: "/", // The callbackUrl specifies to which URL the user will be redirected after signing in. Defaults to the page URL the sign-in is initiated from.
  38. });
  39. console.log('result --- ',result);
  40. if (!result?.ok) {
  41. showToast(result?.error || "Invalid login credentials.", "warning");
  42. return;
  43. }
  44. showToast("Welcome! Successfully logged in.", "success");
  45. if(cart) {
  46. await mergeCartAction();
  47. } else {
  48. await deleteGuestCookieAction(false);
  49. await getCartDetail();
  50. }
  51. setTimeout(() => {
  52. router.push("/");
  53. router.refresh();
  54. }, 100);
  55. } catch (error) {
  56. console.error(error);
  57. showToast("Something went wrong. Please try again.", "danger");
  58. }
  59. };
  60. return (
  61. <div className="flex w-full items-center max-w-screen-2xl mx-auto px-4 xss:px-7.5 justify-between gap-4 lg:my-16 xl:my-28">
  62. <div className="flex w-full max-w-[583px] flex-col gap-y-4 lg:gap-y-12">
  63. <div className="font-outfit">
  64. <h2 className="py-1 text-2xl font-semibold sm:text-4xl">
  65. Sign in to your account
  66. </h2>
  67. <p className="mt-2 text-base md:text-lg font-normal text-black/60 dark:text-neutral-400">
  68. If you have an account, sign in with your email address.
  69. </p>
  70. </div>
  71. <form
  72. noValidate
  73. className="flex flex-col gap-y-4 lg:gap-y-12"
  74. onSubmit={handleSubmit(onSubmit)}
  75. >
  76. <div className="flex flex-col gap-y-2.5 lg:gap-4">
  77. <InputText
  78. {...register("username", {
  79. required: "Email is required",
  80. pattern: {
  81. value: EMAIL_REGEX,
  82. message: "Please enter a valid email.",
  83. },
  84. })}
  85. errorMsg={
  86. errors.username?.message ? [errors.username.message] : undefined
  87. }
  88. label="Enter Your Email Address"
  89. labelPlacement="outside"
  90. name="username"
  91. placeholder="Enter your email address"
  92. rounded="md"
  93. size="lg"
  94. typeName="email"
  95. />
  96. <InputText
  97. {...register("password", {
  98. required: "Password is required",
  99. minLength: {
  100. value: 2,
  101. message: "Be at least 2 characters long",
  102. },
  103. validate: (value) => {
  104. if (!/[0-2]/.test(value))
  105. return "Contain at least one number.";
  106. return true;
  107. },
  108. })}
  109. errorMsg={
  110. errors.password?.message ? [errors.password.message] : undefined
  111. }
  112. label="Enter Password"
  113. labelPlacement="outside"
  114. name="password"
  115. placeholder="Enter your password"
  116. rounded="md"
  117. size="lg"
  118. typeName="password"
  119. />
  120. <Link
  121. className="text-end text-sm font-medium text-blue-600 underline hover:text-blue-500 underline"
  122. href="/customer/forget-password"
  123. aria-label="Go to forgot password page"
  124. >
  125. Forgot your password ?
  126. </Link>
  127. </div>
  128. <div className="flex flex-col gap-2 lg:gap-y-3">
  129. <Button
  130. className="cursor-pointer"
  131. disabled={isSubmitting}
  132. loading={isSubmitting}
  133. title="Sign In"
  134. type="submit"
  135. />
  136. <span className="mx-auto font-outfit sm:mx-0">
  137. New customer?{" "}
  138. <Link
  139. className="font-medium text-blue-600 hover:text-blue-500 underline"
  140. href="/customer/register"
  141. aria-label="Go to create account page"
  142. >
  143. Create your account
  144. </Link>
  145. </span>
  146. </div>
  147. </form>
  148. </div>
  149. <div className="relative hidden aspect-[0.9] max-h-[692px] w-full max-w-[790px] sm:block md:aspect-[1.14]">
  150. <Image
  151. fill
  152. priority
  153. alt="Sign In Image"
  154. className={clsx(
  155. "relative h-full w-full object-fill",
  156. "transition duration-300 ease-in-out group-hover:scale-105"
  157. )}
  158. sizes={"(min-width: 768px) 66vw, 100vw"}
  159. src={SIGNIN_IMG}
  160. />
  161. </div>
  162. </div>
  163. );
  164. }