"use client"; import { zodResolver } from "@hookform/resolvers/zod"; import { useTranslations } from "next-intl"; import Image from "next/image"; import { useRouter, useSearchParams } from "next/navigation"; import { useEffect, useState } from "react"; import { SubmitHandler, useForm } from "react-hook-form"; import { z } from "zod"; import { getPublicApiUrl } from "../../../../client/config"; import { errorToast, FormInput, FormPassword } from "../../../../components"; import { useI18nRouter, usePageUrlGenerator } from "../../../../hooks"; import { isDiscordAuthEnabled, isGoogleAuthEnabled, isInternalAuthEnabled } from "../../../../login"; import { Button, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Form, Link, } from "../../../../shadcnui"; import { UserInterface } from "../../../user"; import { useCurrentUserContext } from "../../../user/contexts"; import { useAuthContext } from "../../contexts"; import { AuthService } from "../../data/auth.service"; import { TwoFactorChallengeInterface } from "../../data/two-factor-challenge.interface"; import { AuthComponent } from "../../enums"; import { GoogleSignInButton } from "../buttons/GoogleSignInButton"; export function Login() { const t = useTranslations(); const { setUser } = useCurrentUserContext(); const { setComponentType, setPendingTwoFactor } = useAuthContext(); const generateUrl = usePageUrlGenerator(); const i18nRouter = useI18nRouter(); const nativeRouter = useRouter(); // For URLs that already include locale const searchParams = useSearchParams(); const callbackUrl = searchParams.get("callbackUrl"); // Read referral code from cookie on mount const [referralCode, setReferralCode] = useState(null); useEffect(() => { const cookies = document.cookie.split("; "); for (const cookie of cookies) { const [name, value] = cookie.split("="); if (name === "referral_code" && value) { setReferralCode(decodeURIComponent(value)); break; } } }, []); // Helper function to build OAuth URL with referral const buildDiscordOAuthUrl = (): string => { const baseUrl = `${getPublicApiUrl()}auth/discord`; if (!referralCode) return baseUrl; return `${baseUrl}?referral=${encodeURIComponent(referralCode)}`; }; const formSchema = z.object({ email: z.string().email({ message: t(`common.errors.invalid_email`), }), password: z.string().min(3, { message: t(`auth.errors.password_too_short`) }), }); const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { email: "", password: "", }, }); const onSubmit: SubmitHandler> = async (values: z.infer) => { try { const response = await AuthService.login({ email: values.email, password: values.password, }); // Check if 2FA is required (response is TwoFactorChallengeInterface) if ("pendingToken" in response) { const challenge = response as TwoFactorChallengeInterface; setPendingTwoFactor(challenge); setComponentType(AuthComponent.TwoFactorChallenge); return; } // Normal login flow const user = response as UserInterface; setUser(user); // Redirect to callback URL if present, otherwise go to home // Use native router for callbackUrl since it already includes the locale prefix // Use i18n router for normal navigation which adds locale automatically if (callbackUrl) { nativeRouter.replace(callbackUrl); } else { i18nRouter.replace(generateUrl({ page: `/` })); } } catch (e) { console.error("[Login] Login error:", e); errorToast({ title: t(`common.errors.error`), error: e, }); } }; return ( <> Logo {t("auth.buttons.login")} {t(`auth.login_description`)}
{isInternalAuthEnabled() && ( <> )} {isGoogleAuthEnabled() && } {isDiscordAuthEnabled() && ( )}
setComponentType(AuthComponent.Register)} > {t(`auth.buttons.register`)} setComponentType(AuthComponent.ForgotPassword)} data-testid="form-login-link-forgot-password" > {t(`auth.buttons.forgot_password`)}
); }