All files / src/components/features/auth/Signin index.tsx

97.28% Statements 179/184
83.33% Branches 15/18
50% Functions 2/4
97.28% Lines 179/184

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 1851x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 5x 5x 5x 5x 5x 5x 5x 5x 5x 2x 2x 5x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 5x           37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 2x 2x 2x 2x 2x 2x 2x 2x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 37x 1x 1x  
"use client";
 
import { GithubIcon, GoogleIcon } from "@/components";
import Link from "next/link";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { signIn } from "next-auth/react";
import { useRouter } from "next/navigation";
import toast from "react-hot-toast";
import { signinSchema, type SigninInput } from "@/lib/validation-schemas";
import { eventTracking } from "@/lib/event-tracking";
import { Button, Input } from "@/components/ui";
import { useFunnelSteps } from "@/hooks/useFunnelTracking";
 
const Signin = () => {
  const router = useRouter();
  const [error, setError] = useState<string | null>(null);
  const { trackStep } = useFunnelSteps();
 
  const {
    register,
    handleSubmit,
    formState: { errors, isSubmitting }} = useForm<SigninInput>({
    resolver: zodResolver(signinSchema)});
 
  const onSubmit = async (data: SigninInput) => {
    setError(null);
 
    try {
      const result = await signIn("credentials", {
        email: data.email,
        password: data.password,
        redirect: false});
 
      if (result?.error) {
        setError(result.error);
        toast.error(result.error);
      } else if (result?.ok) {
        // Track successful sign in (legacy)
        eventTracking.signIn("credentials");
 
        // Track login_completed for engagement funnel
        trackStep('engagement', 'login_completed', {
          method: 'credentials'});
 
        toast.success("Signed in successfully!");
        router.push("/");
      }
    } catch (err) {
      const message = "Failed to sign in";
      setError(message);
      toast.error(message);
      return err;
    }
  };
 
  return (
    <>
      <section className="overflow-hidden pt-[140px] pb-20 bg-gray-2 dark:bg-gray-900">
        <div className="max-w-[1170px] w-full mx-auto px-4 sm:px-8 xl:px-0">
          <div className="max-w-[570px] w-full mx-auto rounded-xl bg-white dark:bg-gray-800 shadow-1 p-4 sm:p-7.5 xl:p-11">
            <div className="text-center mb-11">
              <h2 className="font-semibold text-xl sm:text-2xl xl:text-heading-5 text-dark dark:text-gray-100 mb-1.5">
                Sign In to Your Account
              </h2>
              <p className="dark:text-gray-400">Enter your detail below</p>
            </div>
 
            <div>
              <form onSubmit={handleSubmit(onSubmit)} aria-label="Sign in form">
                {error && (
                  <div
                    id="signin-error"
                    role="alert"
                    aria-live="polite"
                    className="mb-5 p-3 rounded-lg bg-red-100 text-red-700 text-sm"
                  >
                    {error}
                  </div>
                )}
 
                <div className="mb-5">
                  <Input
                    label="Email"
                    type="email"
                    id="email"
                    placeholder="Enter your email"
                    disabled={isSubmitting}
                    state={errors.email ? 'error' : 'default'}
                    error={errors.email?.message}
                    fullWidth
                    className="rounded-lg border-gray-3 bg-gray-1 placeholder:text-dark-5 py-3 px-5 focus:shadow-input"
                    {...register("email")}
                  />
                </div>
 
                <div className="mb-5">
                  <Input
                    label="Password"
                    type="password"
                    id="password"
                    placeholder="Enter your password"
                    autoComplete="current-password"
                    disabled={isSubmitting}
                    state={errors.password ? 'error' : 'default'}
                    error={errors.password?.message}
                    fullWidth
                    className="rounded-lg border-gray-3 bg-gray-1 placeholder:text-dark-5 py-3 px-5 focus:shadow-input"
                    {...register("password")}
                  />
                </div>
 
                <Button
                  type="submit"
                  variant="primary"
                  size="md"
                  fullWidth
                  loading={isSubmitting}
                  aria-label={isSubmitting ? "Signing in, please wait" : "Sign in to your account"}
                  className="mt-7.5"
                >
                  Sign in to account
                </Button>
 
                <a
                  href="#"
                  className="block text-center text-dark-4 dark:text-gray-400 mt-4.5 ease-out duration-200 hover:text-dark dark:hover:text-gray-200"
                >
                  Forget your password?
                </a>
 
                <span className="relative z-1 block font-medium text-center mt-4.5 dark:text-gray-300">
                  <span className="block absolute -z-1 left-0 top-1/2 h-px w-full bg-gray-3 dark:bg-gray-600"></span>
                  <span className="inline-block px-3 bg-white dark:bg-gray-800">Or</span>
                </span>
 
                <div className="flex flex-col gap-4.5 mt-4.5">
                  <Button
                    type="button"
                    variant="outline"
                    size="md"
                    fullWidth
                    leftIcon={<GoogleIcon />}
                    loading={isSubmitting}
                    aria-label="Sign in with Google"
                    onClick={() => signIn("google")}
                  >
                    Sign In with Google
                  </Button>
 
                  <Button
                    type="button"
                    variant="outline"
                    size="md"
                    fullWidth
                    leftIcon={<GithubIcon />}
                    loading={isSubmitting}
                    aria-label="Sign in with Github"
                    onClick={() => signIn("github")}
                  >
                    Sign In with Github
                  </Button>
                </div>
 
                <p className="text-center mt-6 dark:text-gray-300">
                  Don&apos;t have an account?
                  <Link
                    href="/signup"
                    className="text-dark dark:text-gray-100 ease-out duration-200 hover:text-blue pl-2"
                  >
                    Sign Up Now!
                  </Link>
                </p>
              </form>
            </div>
          </div>
        </div>
      </section>
    </>
  );
};
 
export default Signin;