{{#if framework == "nextjs"}} "use client"; import { setTokens } from "@/features/auth/services/auth.service"; {{/if}} import { forgetPasswordRequest, loginRequest, logoutRequest, registerRequest, resendOTPRequest, resetPasswordRequest, verifyEmailRequest, } from "@/features/auth/services/auth.api"; import { envVars } from "@/lib/env"; import { useMutation, useQueryClient } from "@tanstack/react-query"; {{#if framework == "nextjs"}} import { useRouter } from "next/navigation"; {{else}} import { useNavigate } from "react-router"; {{/if}} import { toast } from "sonner"; import type { ILoginResponse, SocialProvider } from "../types/auth.type"; import type { ILoginPayload } from "../validators/login.validator"; import { ChangePassword, updateProfile } from "../services/auth.api"; export const AUTH_QUERY_KEYS = { me: ["auth", "me"] as const, }; export const AUTH_MUTATION_KEYS = { register: ["auth", "register"] as const, login: ["auth", "login"] as const, forgotPassword: ["auth", "forgot-password"] as const, resetPassword: ["auth", "reset-password"] as const, verifyEmail: ["auth", "verify-email"] as const, resendOTP: ["auth", "resend-otp"] as const, socialLogin: ["auth", "social-login"] as const, changePassword: ["auth", "change-password"] as const, updateProfile: ["auth", "update-profile"] as const, logout: ["auth", "logout"] as const, }; export const useRegisterMutation = () => { {{#if framework == "nextjs"}} const router = useRouter(); const navigate = (path: string) => router.push(path); {{else}} const navigate = useNavigate(); {{/if}} return useMutation({ mutationKey: AUTH_MUTATION_KEYS.register, mutationFn: registerRequest, onSuccess: (_data, variables) => { navigate(`/verify-email?email=${encodeURIComponent(variables.email)}`); toast.success("Registration successful! Please verify your email."); }, onError: (error: unknown) => { toast.error( error instanceof Error ? error.message : "Registration failed. Please check your details and try again.", ); }, }); }; export const useLoginMutation = () => { {{#if framework == "nextjs"}} const router = useRouter(); const navigate = (path: string) => router.push(path); {{else}} const navigate = useNavigate(); {{/if}} const queryClient = useQueryClient(); return useMutation( { mutationKey: AUTH_MUTATION_KEYS.login, mutationFn: loginRequest, onSuccess: async (data, variables) => { toast.success("Login successful!"); {{#if framework == "nextjs"}} try { await setTokens({ accessToken: data?.accessToken, refreshToken: data?.refreshToken, token: data?.token, }); } catch (err) { console.error("Failed to set tokens in cookies:", err); } {{/if}} await queryClient.invalidateQueries({ queryKey: AUTH_QUERY_KEYS.me }); await new Promise((resolve) => setTimeout(resolve, 100)); const defaultRoute = data?.user?.role === "ADMIN" ? "/dashboard/admin" : "/dashboard"; navigate(variables?.redirectPath || defaultRoute); }, onError: (error: unknown, variables) => { if (error instanceof Error && error.message === "Email not verified") { navigate(`/verify-email?email=${encodeURIComponent(variables.email)}`); return; } toast.error( error instanceof Error ? error.message : "Login failed. Please check your credentials and try again.", ); }, }, ); }; export const useForgotPasswordMutation = () => { {{#if framework == "nextjs"}} const router = useRouter(); const navigate = (path: string) => router.push(path); {{else}} const navigate = useNavigate(); {{/if}} const queryClient = useQueryClient(); return useMutation({ mutationKey: AUTH_MUTATION_KEYS.forgotPassword, mutationFn: forgetPasswordRequest, onSuccess: async () => { toast.success("Password reset OTP sent to your email!"); await queryClient.invalidateQueries({ queryKey: AUTH_QUERY_KEYS.me }); navigate("/reset-password"); }, onError: (error: Error) => { toast.error( error.message || "Failed to send password reset email. Please check the email and try again.", ); }, }); }; export const useResetPasswordMutation = () => { {{#if framework == "nextjs"}} const router = useRouter(); const navigate = (path: string) => router.push(path); {{else}} const navigate = useNavigate(); {{/if}} return useMutation({ mutationKey: AUTH_MUTATION_KEYS.resetPassword, mutationFn: resetPasswordRequest, onSuccess: () => { toast.success("Password reset successful! Please log in with your new password."); navigate("/login"); }, onError: (error: Error) => { toast.error( error.message || "Failed to reset password. Please check your details and try again.", ); }, }); }; export const useVerifyEmailMutation = () => { {{#if framework == "nextjs"}} const router = useRouter(); const navigate = (path: string) => router.push(path); {{else}} const navigate = useNavigate(); {{/if}} return useMutation({ mutationKey: AUTH_MUTATION_KEYS.verifyEmail, mutationFn: verifyEmailRequest, onSuccess: () => { toast.success("Email verified successfully! Please log in."); navigate("/login"); }, onError: (error: Error) => { toast.error( error.message || "Email verification failed. Please check your details and try again.", ); }, }); }; export const useResendOTPMutation = () => { return useMutation({ mutationKey: AUTH_MUTATION_KEYS.resendOTP, mutationFn: resendOTPRequest, onSuccess: () => { toast.success("Verification OTP resent successfully!"); }, onError: (error) => { toast.error( error.message || "Failed to resend verification OTP. Please check the email and try again.", ); }, }); }; export const useSocialLoginMutation = () => { return useMutation({ mutationKey: AUTH_MUTATION_KEYS.socialLogin, mutationFn: async (provider) => { const payloadRes = await fetch( `${envVars.API_URL}/v1/auth/login/${provider}?redirect=/dashboard`, ); if (!payloadRes.ok) throw new Error("Failed to initiate social login."); const { data: payload } = await payloadRes.json(); const authRes = await fetch( `${envVars.BETTER_AUTH_URL}${payload.signInEndpoint}`, { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify({ provider: payload.provider, callbackURL: payload.callbackURL, }), }, ); if (!authRes.ok) throw new Error("Social login request failed."); const json = await authRes.json(); const redirectUrl: string | undefined = json?.url || json?.redirectUrl; if (!redirectUrl) throw new Error("No redirect URL returned from social login."); return redirectUrl; }, onSuccess: (redirectUrl) => { window.location.href = redirectUrl; }, onError: (error) => { toast.error(error.message || "Social login failed. Please try again."); }, }); }; export const useChangePasswordMutation = () => { {{#if framework == "nextjs"}} const router = useRouter(); const navigate = (path: string) => router.push(path); {{else}} const navigate = useNavigate(); {{/if}} return useMutation({ mutationKey: AUTH_MUTATION_KEYS.changePassword, mutationFn: ChangePassword, onSuccess: () => { toast.success( "Password changed successfully! Please log in with your new password.", ); navigate("/login"); }, onError: (error) => { toast.error( error.message || "Failed to change password. Please check your details and try again.", ); }, }); }; export const useUpdateProfileMutation = () => { const queryClient = useQueryClient(); return useMutation({ mutationKey: AUTH_MUTATION_KEYS.updateProfile, mutationFn: updateProfile, onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: AUTH_QUERY_KEYS.me }); toast.success("Profile updated successfully"); }, onError: (error) => { toast.error( error.message || "Failed to update profile. Please check your details and try again.", ); }, }); }; export const useLogoutMutation = () => { {{#if framework == "nextjs"}} const router = useRouter(); const navigate = (path: string) => router.push(path); {{else}} const navigate = useNavigate(); {{/if}} const queryClient = useQueryClient(); return useMutation({ mutationKey: AUTH_MUTATION_KEYS.logout, mutationFn: logoutRequest, onSuccess: async () => { queryClient.clear(); toast.success("Logged out successfully"); navigate("/login"); }, onError: (error: Error) => { toast.error(error.message || "Failed to log out. Please try again."); }, }); };