"use client"; import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { useState } from "react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { InputOTP, InputOTPGroup, InputOTPSlot } from "@/components/ui/input-otp"; import { AxiosResponse } from "axios"; import { RequestPasswordResetBody, RequestPasswordResetResponse, ResetPasswordBody, ResetPasswordResponse } from "@server/routers/auth"; import { Loader2 } from "lucide-react"; import { Alert, AlertDescription } from "../../../components/ui/alert"; import { toast } from "@app/hooks/useToast"; import { useRouter } from "next/navigation"; import { formatAxiosError } from "@app/lib/api"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp"; import { passwordSchema } from "@server/auth/passwordSchema"; import { cleanRedirect } from "@app/lib/cleanRedirect"; import { useTranslations } from "next-intl"; const requestSchema = z.object({ email: z.string().email() }); export type ResetPasswordFormProps = { emailParam?: string; tokenParam?: string; redirect?: string; }; export default function ResetPasswordForm({ emailParam, tokenParam, redirect }: ResetPasswordFormProps) { const router = useRouter(); const [error, setError] = useState(null); const [successMessage, setSuccessMessage] = useState(null); const [isSubmitting, setIsSubmitting] = useState(false); const t = useTranslations(); function getState() { if (emailParam && !tokenParam) { return "request"; } if (emailParam && tokenParam) { return "reset"; } return "request"; } const [state, setState] = useState<"request" | "reset" | "mfa">(getState()); const api = createApiClient(useEnvContext()); const formSchema = z .object({ email: z.string().email({ message: t('emailInvalid') }), token: z.string().min(8, { message: t('tokenInvalid') }), password: passwordSchema, confirmPassword: passwordSchema }) .refine((data) => data.password === data.confirmPassword, { path: ["confirmPassword"], message: t('passwordNotMatch') }); const mfaSchema = z.object({ code: z.string().length(6, { message: t('pincodeInvalid') }) }); const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { email: emailParam || "", token: tokenParam || "", password: "", confirmPassword: "" } }); const mfaForm = useForm>({ resolver: zodResolver(mfaSchema), defaultValues: { code: "" } }); const requestForm = useForm>({ resolver: zodResolver(requestSchema), defaultValues: { email: emailParam || "" } }); async function onRequest(data: z.infer) { const { email } = data; setIsSubmitting(true); const res = await api .post>( "/auth/reset-password/request", { email } as RequestPasswordResetBody ) .catch((e) => { setError(formatAxiosError(e, t('errorOccurred'))); console.error(t('passwordErrorRequestReset'), e); setIsSubmitting(false); }); if (res && res.data?.data) { setError(null); setState("reset"); setIsSubmitting(false); form.setValue("email", email); } } async function onReset(data: any) { setIsSubmitting(true); const { password, email, token } = form.getValues(); const { code } = mfaForm.getValues(); const res = await api .post>( "/auth/reset-password", { email, token, newPassword: password, code } as ResetPasswordBody ) .catch((e) => { setError(formatAxiosError(e, t('errorOccurred'))); console.error(t('passwordErrorReset'), e); setIsSubmitting(false); }); console.log(res); if (res) { setError(null); if (res.data.data?.codeRequested) { setState("mfa"); setIsSubmitting(false); mfaForm.reset(); return; } setSuccessMessage(t('passwordResetSuccess')); setTimeout(() => { if (redirect) { const safe = cleanRedirect(redirect); router.push(safe); } else { router.push("/auth/login"); } setIsSubmitting(false); }, 1500); } } return (
{t('passwordReset')} {t('passwordResetDescription')}
{state === "request" && (
( {t('email')} {t('passwordResetSent')} )} /> )} {state === "reset" && (
( {t('email')} )} /> {!tokenParam && ( ( {t('passwordResetCode')} {t('passwordResetCodeDescription')} )} /> )} ( {t('passwordNew')} )} /> ( {t('passwordNewConfirm')} )} /> )} {state === "mfa" && (
( {t('pincodeAuth')}
)} /> )} {error && ( {error} )} {successMessage && ( {successMessage} )}
{(state === "reset" || state === "mfa") && ( )} {state === "request" && ( )} {state === "mfa" && ( )} {(state === "mfa" || state === "reset") && ( )}
); }