| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- "use client";
- import Image from "next/image";
- import Link from "next/link";
- import { SubmitHandler, useForm } from "react-hook-form";
- import InputText from "@components/common/form/Input";
- import { useCustomToast } from "@/utils/hooks/useToast";
- import { useRouter } from "next/navigation";
- import { EMAIL_REGEX, SIGNUP_IMG, IS_VALID_INPUT } from "@utils/constants";
- import { createUser } from "@utils/actions";
- import { Button } from "@components/common/button/Button";
- export type RegisterInputs = {
- firstName: string;
- lastName: string;
- email: string;
- password: string;
- passwordConfirmation: string;
- };
- export default function RegistrationForm() {
- const router = useRouter();
- const {
- register,
- handleSubmit,
- formState: { errors, isSubmitting },
- } = useForm<RegisterInputs>({
- mode: "onSubmit",
- reValidateMode: "onChange",
- });
- const { showToast } = useCustomToast();
- /**
- *
- * 浏览器端 RegistrationForm.onSubmit
- → 调用 createUser(data) ← 这是一个 Server Action
- → 发送 POST 请求到当前页面路由(或者由框架生成的端点)
- → 服务端执行 createUser 函数(访问 cookies、session、数据库等)
- → 返回结果给浏览器
- */
- const onSubmit: SubmitHandler<RegisterInputs> = async (data) => {
- console.log('register run where?--------');
- if (data.password !== data.passwordConfirmation) {
- showToast("The Passwords do not match.", "warning");
- return;
- }
- await createUser(data)
- .then((res) => {
- if (res?.success) {
- showToast("User created successfully", "success");
- router.replace("/customer/login");
- } else {
- showToast(res?.error?.message || "Failed to create user", "warning");
- }
- })
- .catch((error) => {
- showToast(error.message || "An error occurred", "warning");
- });
- };
- return (
- <div className="mt-5 md:my-8 md:mt-0 flex w-full items-center w-full max-w-screen-2xl mx-auto px-4 xss:px-7.5 justify-between gap-0 md:gap-4 lg:my-16 xl:my-28">
- <div className="relative flex w-full max-w-[583px] flex-col gap-y-4 lg:gap-y-12">
- <div className="font-outfit">
- <h2 className="py-1 text-2xl font-semibold sm:text-4xl">
- Become User
- </h2>
- <p className="mt-2 text-base md:text-lg font-normal text-black/[60%] dark:text-neutral-400 sm:mt-2">
- You are new to our store, we are glad to have you as a member.
- </p>
- </div>
- <form
- noValidate
- className="flex flex-col gap-y-5 lg:gap-y-12"
- onSubmit={handleSubmit(onSubmit)}
- >
- <div className="flex flex-col gap-y-2.5 lg:gap-[18px]">
- <div className="flex w-full gap-2.5 lg:gap-[18px]">
- <InputText
- {...register("firstName", {
- required: "First name is required",
- pattern: {
- value: IS_VALID_INPUT,
- message: "Invalid First Name",
- },
- })}
- className="w-full"
- errorMsg={
- errors.firstName?.message
- ? [errors.firstName.message]
- : undefined
- }
- label="First Name"
- labelPlacement="outside"
- name="firstName"
- placeholder="Enter first name"
- size="lg"
- />
- <InputText
- {...register("lastName",
- {
- required: "Last name is required",
- pattern: {
- value: IS_VALID_INPUT,
- message: "Invalid Last Name",
- },
- })}
- className="w-full"
- errorMsg={
- errors.lastName?.message
- ? [errors.lastName.message]
- : undefined
- }
- label="Last Name"
- labelPlacement="outside"
- name="lastName"
- placeholder="Enter last name"
- size="lg"
- />
- </div>
- <InputText
- {...register("email", {
- required: "Email is required",
- pattern: {
- value: EMAIL_REGEX,
- message: "Please enter a valid email.",
- },
- })}
- errorMsg={errors.email?.message}
- label="Email"
- labelPlacement="outside"
- name="email"
- placeholder="Enter email address"
- size="lg"
- />
- <InputText
- {...register("password", {
- required: "Password is required",
- minLength: {
- value: 8,
- message: "Must be at least 8 characters",
- },
- validate: (val) => {
- if (!/[A-Z]/.test(val))
- return "Must contain at least one uppercase letter";
- if (!/[a-z]/.test(val))
- return "Must contain at least one lowercase letter";
- if (!/[0-9]/.test(val))
- return "Must contain at least one number";
- if (/\s/.test(val)) return "Cannot contain spaces";
- return true;
- },
- })}
- label="Password"
- labelPlacement="outside"
- name="password"
- placeholder="Enter password"
- typeName="password"
- size="lg"
- errorMsg={
- errors.password?.message ? [errors.password.message] : undefined
- }
- />
- <InputText
- {...register("passwordConfirmation", {
- required: "Please confirm your password",
- })}
- label="Confirm Password"
- labelPlacement="outside"
- name="passwordConfirmation"
- placeholder="Enter confirm password"
- size="lg"
- typeName="password"
- />
- </div>
- <div className="flex flex-col gap-y-3 mb-8 lg:mb-0">
- <Button
- disabled={isSubmitting}
- loading={isSubmitting}
- title="Sign Up"
- type="submit"
- />
- <span className="mx-auto md:mx-0 font-outfit">
- Already have an account?{" "}
- <Link className="text-blue-600 underline" href="/customer/login" aria-label="Go to sign in page">
- Sign In
- </Link>
- </span>
- </div>
- </form>
- </div>
- <div className="relative hidden aspect-[0.9] max-h-[692px] w-full max-w-[790px] sm:block md:aspect-[1.14]">
- <Image
- fill
- priority
- alt="Sign Up Image"
- className="h-full w-full object-cover transition duration-300 ease-in-out group-hover:scale-105"
- sizes="(min-width: 768px) 66vw, 100vw"
- src={SIGNUP_IMG}
- />
- </div>
- </div>
- );
- }
|