"use client"; import { zodResolver } from "@hookform/resolvers/zod"; import { authClient } from "@repo/auth/client"; import { config } from "@repo/config"; import { useAuthErrorMessages } from "@saas/auth/hooks/errors-messages"; import { sessionQueryKey } from "@saas/auth/lib/api"; import { OrganizationInvitationAlert } from "@saas/organizations/components/OrganizationInvitationAlert"; import { useRouter } from "@shared/hooks/router"; import { useQueryClient } from "@tanstack/react-query"; import { Alert, AlertDescription, AlertTitle } from "@ui/components/alert"; import { Button } from "@ui/components/button"; import { Form, FormControl, FormField, FormItem, FormLabel, } from "@ui/components/form"; import { Input } from "@ui/components/input"; import { AlertTriangleIcon, ArrowRightIcon, EyeIcon, EyeOffIcon, KeyIcon, MailboxIcon, } from "lucide-react"; import { useTranslations } from "next-intl"; import Link from "next/link"; import { useSearchParams } from "next/navigation"; import { useEffect, useState } from "react"; import type { SubmitHandler } from "react-hook-form"; import { useForm } from "react-hook-form"; import { withQuery } from "ufo"; import { z } from "zod"; import { type OAuthProvider, oAuthProviders, } from "../constants/oauth-providers"; import { useSession } from "../hooks/use-session"; import { LoginModeSwitch } from "./LoginModeSwitch"; import { SocialSigninButton } from "./SocialSigninButton"; const formSchema = z.union([ z.object({ mode: z.literal("magic-link"), email: z.string().email(), }), z.object({ mode: z.literal("password"), email: z.string().email(), password: z.string().min(1), }), ]); type FormValues = z.infer; export function LoginForm() { const t = useTranslations(); const { getAuthErrorMessage } = useAuthErrorMessages(); const router = useRouter(); const queryClient = useQueryClient(); const searchParams = useSearchParams(); const { user, loaded: sessionLoaded } = useSession(); const [showPassword, setShowPassword] = useState(false); const invitationId = searchParams.get("invitationId"); const email = searchParams.get("email"); const redirectTo = searchParams.get("redirectTo"); const form = useForm({ resolver: zodResolver(formSchema), defaultValues: { email: email ?? "", password: "", mode: config.auth.enablePasswordLogin ? "password" : "magic-link", }, }); const redirectPath = invitationId ? `/app/organization-invitation/${invitationId}` : (redirectTo ?? config.auth.redirectAfterSignIn); useEffect(() => { if (sessionLoaded && user) { router.replace(redirectPath); } }, [user, sessionLoaded]); const onSubmit: SubmitHandler = async (values) => { try { if (values.mode === "password") { const { data, error } = await authClient.signIn.email({ ...values, }); if (error) { throw error; } if ((data as any).twoFactorRedirect) { router.replace( withQuery( "/auth/verify", Object.fromEntries(searchParams.entries()), ), ); return; } queryClient.invalidateQueries({ queryKey: sessionQueryKey, }); router.replace(redirectPath); } else { const { error } = await authClient.signIn.magicLink({ ...values, callbackURL: redirectPath, }); if (error) { throw error; } } } catch (e) { form.setError("root", { message: getAuthErrorMessage( e && typeof e === "object" && "code" in e ? (e.code as string) : undefined, ), }); } }; const signInWithPasskey = async () => { try { await authClient.signIn.passkey(); router.replace(redirectPath); } catch (e) { form.setError("root", { message: getAuthErrorMessage( e && typeof e === "object" && "code" in e ? (e.code as string) : undefined, ), }); } }; const signinMode = form.watch("mode"); return (

{t("auth.login.title")}

{t("auth.login.subtitle")}

{form.formState.isSubmitSuccessful && signinMode === "magic-link" ? ( {t("auth.login.hints.linkSent.title")} {t("auth.login.hints.linkSent.message")} ) : ( <> {invitationId && ( )}
{config.auth.enableMagicLink && config.auth.enablePasswordLogin && ( form.setValue( "mode", mode as typeof signinMode, ) } /> )} {form.formState.isSubmitted && form.formState.errors.root?.message && ( {form.formState.errors.root.message} )} ( {t("auth.signup.email")} )} /> {config.auth.enablePasswordLogin && signinMode === "password" && ( (
{t( "auth.signup.password", )} {t( "auth.login.forgotPassword", )}
)} /> )} {(config.auth.enablePasskeys || (config.auth.enableSignup && config.auth.enableSocialLogin)) && ( <>

{t("auth.login.continueWith")}

{config.auth.enableSignup && config.auth.enableSocialLogin && Object.keys(oAuthProviders).map( (providerId) => ( ), )} {config.auth.enablePasskeys && ( )}
)} {config.auth.enableSignup && (
{t("auth.login.dontHaveAnAccount")}{" "} {t("auth.login.createAnAccount")}
)} )}
); }