import { useState, useEffect } from "react"; import { useNavigate, useSearchParams, useLocation } from "react-router-dom"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import type { ReactElement } from "react"; import { authService } from "@/services/auth-service"; import { FormField } from "@/components/shared"; import { PrimaryButton } from "@/components/shared"; import { showToast } from "@/utils/toast"; import { AuthLayout } from "@/components/layout/AuthLayout"; // Zod validation schema - token is optional if provided in URL const createResetPasswordSchema = (hasTokenFromUrl: boolean) => z .object({ token: hasTokenFromUrl ? z.string().optional() : z.string().min(1, "Reset token is required"), password: z .string() .min(1, "Password is required") .min(8, "Password must be at least 8 characters"), confirmPassword: z.string().min(1, "Please confirm your password"), acceptTerms: z.boolean().refine((val) => val === true, { message: "You must accept the Terms of Service and Privacy Policy", }), }) .refine((data) => data.password === data.confirmPassword, { message: "Passwords do not match", path: ["confirmPassword"], }); const ResetPassword = (): ReactElement => { const navigate = useNavigate(); const [searchParams] = useSearchParams(); const location = useLocation(); const [isLoading, setIsLoading] = useState(false); const [success, setSuccess] = useState(""); const isTenantRoute = location.pathname.startsWith("/tenant"); const portalType = isTenantRoute ? "tenant" : "admin"; const tokenFromUrl = searchParams.get("token") || ""; const reason = searchParams.get("reason") || ""; const hasTokenFromUrl = Boolean(tokenFromUrl); const resetPasswordSchema = createResetPasswordSchema(hasTokenFromUrl); type ResetPasswordFormData = z.infer; const { register, handleSubmit, setValue, setError, formState: { errors }, clearErrors, } = useForm({ resolver: zodResolver(resetPasswordSchema), mode: "onBlur", defaultValues: { token: tokenFromUrl || undefined, password: "", confirmPassword: "", acceptTerms: false as any, }, }); // Set token from URL if available useEffect(() => { if (tokenFromUrl) { setValue("token", tokenFromUrl); } }, [tokenFromUrl, setValue]); const onSubmit = async (data: ResetPasswordFormData): Promise => { setSuccess(""); clearErrors(); setIsLoading(true); try { // Always use token from URL if available, otherwise use form data const tokenToUse = tokenFromUrl || data.token || ""; const response = await authService.resetPassword({ token: tokenToUse, password: data.password, }); if (response.success) { const message = response.message || response.data?.message || "Password reset successfully!"; const description = response.message || response.data?.message ? undefined : "You can now login with your new password"; const successMessage = response.message || response.data?.message || "Password reset successfully! You can now login with your new password."; setSuccess(successMessage); showToast.success(message, description); // Redirect to login after 2 seconds setTimeout(() => { navigate("/"); }, 2000); } } catch (err: any) { console.error("Reset password error:", err); // Handle validation errors from API if ( err?.response?.data?.details && Array.isArray(err.response.data.details) ) { const validationErrors = err.response.data.details; validationErrors.forEach( (detail: { path: string; message: string }) => { if (detail.path === "token" || detail.path === "password") { setError(detail.path as keyof ResetPasswordFormData, { type: "server", message: detail.message, }); } }, ); } else { // Handle general errors const errorMessage = err?.response?.data?.error?.message || err?.response?.data?.error || err?.response?.data?.message || err?.message || "Failed to reset password. Please try again."; setError("root", { type: "server", message: typeof errorMessage === "string" ? errorMessage : "Failed to reset password. Please try again.", }); } } finally { setIsLoading(false); } }; return ( {/* Header */}
{/* Heading */}

{reason === "FIRST_LOGIN" ? "Set Initial Password" : reason === "EXPIRED" ? "Update Expired Password" : "Set New Password"}

{/* Description */}

{reason === "FIRST_LOGIN" ? "Welcome to QAssure! Please set a new password for your account to continue." : reason === "EXPIRED" ? "Your password has expired. For security, please choose a new password." : "Create a strong password to activate your account and access the portal"}

{/* Success Message */} {success && (

{success}

)} {!success ? (
{ e.preventDefault(); e.stopPropagation(); handleSubmit(onSubmit)(e); }} className="flex flex-col w-full p-5 border border-gray-300 rounded-lg bg-white space-y-4" noValidate > {/* General Error Display */} {errors.root && (

{errors.root.message}

)} {/* Token Field - Only show if not in URL */} {!hasTokenFromUrl && ( )} {/* Password Field */} {/* Confirm Password Field */} {/* Accept Terms Checkbox */}
{errors.acceptTerms ? ( {errors.acceptTerms.message} ) : ( You must accept to continue. )}
{/* Submit Button */}
{isLoading ? "Resetting..." : "Login"}

You will be redirected to the Tenant Admin Portal after saving.

) : (
navigate("/")} className="w-full h-11 text-base font-semibold rounded-md shadow-sm" > Go to Login
)}
); }; export default ResetPassword;