"use client"; import { useState, useCallback, useEffect } from "react"; import { useRouter } from "@/components/navigation"; import { toast } from "sonner"; import Cookies from "js-cookie"; import { LoginFormData, ProfileData, AuthState, AuthContextType, EmailValidationData, OTPData, } from "@/types/auth"; import { login, getProfile, postEmailValidation, postSetupEmail, verifyOTPByUsername, doLogin, } from "@/service/auth"; import { setAuthCookies, setProfileCookies, clearAllCookies, isUserEligible, isProtectedRole, getNavigationPath, showAuthError, showAuthSuccess, loginRateLimiter, AUTH_CONSTANTS, } from "@/lib/auth-utils"; import { warning } from "@/lib/swal"; export const useAuth = (): AuthContextType => { const router = useRouter(); const [state, setState] = useState({ isAuthenticated: false, user: null, loading: false, error: null, }); // Check if user is authenticated on mount useEffect(() => { const checkAuth = async () => { try { setState((prev) => ({ ...prev, loading: true })); // Add logic to check if user is authenticated // This could check for valid tokens, etc. } catch (error) { setState((prev) => ({ ...prev, isAuthenticated: false, user: null, error: "Authentication check failed", })); } finally { setState((prev) => ({ ...prev, loading: false })); } }; checkAuth(); }, []); const login = useCallback( async (credentials: LoginFormData): Promise => { try { setState((prev) => ({ ...prev, loading: true, error: null })); // Check rate limiting if (!loginRateLimiter.canAttempt(credentials.username)) { const remainingTime = loginRateLimiter.getRemainingTime( credentials.username ); const minutes = Math.ceil(remainingTime / (60 * 1000)); throw new Error( `Too many login attempts. Please try again in ${minutes} minutes.` ); } // Attempt login const response = await doLogin({ ...credentials, }); if (response?.error) { loginRateLimiter.recordAttempt(credentials.username); throw new Error("Invalid username or password"); } const { access_token, refresh_token } = response?.data?.data || {}; if (!access_token || !refresh_token) { throw new Error("Invalid response from server"); } // Set auth cookies setAuthCookies(access_token, refresh_token); // Get user profile const profileResponse = await getProfile(access_token); const profile = profileResponse?.data?.data; if (!profile) { throw new Error("Failed to fetch user profile"); } const dateTime: any = new Date(); const newTime: any = dateTime.getTime() + 10 * 60 * 1000; Cookies.set("access_token", response?.data?.data?.access_token, { expires: 1, }); Cookies.set("refresh_token", response?.data?.data?.refresh_token, { expires: 1, }); Cookies.set("time_refresh", newTime, { expires: 1, }); if (response?.data?.data?.approvalWorkflowInfo?.hasWorkflowSetup) { Cookies.set("default_workflow", response?.data?.data?.approvalWorkflowInfo?.defaultWorkflowId, { expires: 1, }); } Cookies.set("is_first_login", "true", { secure: true, sameSite: "strict", }); // await saveActivity( // { // activityTypeId: 1, // url: "https://dev.mikulnews.com/auth", // userId: profile?.data?.data?.id, // }, // response?.data?.data?.access_token // ); Cookies.set("profile_picture", profile?.profilePictureUrl, { expires: 1, }); Cookies.set("uie", profile?.id, { expires: 1, }); Cookies.set("ufne", profile?.fullname, { expires: 1, }); Cookies.set("ulie", profile?.userLevelGroup, { expires: 1, }); Cookies.set("username", profile?.username, { expires: 1, }); Cookies.set("urie", profile?.roleId, { expires: 1, }); Cookies.set("roleName", profile?.roleName, { expires: 1, }); Cookies.set("masterPoldaId", profile?.masterPoldaId, { expires: 1, }); Cookies.set("ulne", profile?.userLevelId, { expires: 1, }); Cookies.set("urce", profile?.roleCode, { expires: 1, }); Cookies.set("email", profile?.email, { expires: 1, }); Cookies.set("status", "login", { expires: 1, }); // Reset rate limiter on successful login loginRateLimiter.resetAttempts(credentials.username); router.push("/admin/dashboard"); } catch (error: any) { const errorMessage = error?.message || "Login failed"; setState((prev) => ({ ...prev, loading: false, error: errorMessage, })); showAuthError(error, "Login failed"); } }, [router] ); const logout = useCallback((): void => { clearAllCookies(); setState({ isAuthenticated: false, user: null, loading: false, error: null, }); router.push("/auth"); }, [router]); const refreshToken = useCallback(async (): Promise => { try { setState((prev) => ({ ...prev, loading: true })); // Add token refresh logic here // This would typically call an API to refresh the access token } catch (error) { logout(); } finally { setState((prev) => ({ ...prev, loading: false })); } }, [logout]); return { ...state, login, logout, refreshToken, }; }; // Hook for email validation step export const useEmailValidation = () => { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const validateEmail = useCallback( async (credentials: LoginFormData): Promise => { try { setLoading(true); setError(null); const response = await postEmailValidation(credentials); if (response?.error) { throw new Error(response?.message || "Email validation failed"); } const message = response?.data?.messages[0]; switch (message) { case "Continue to setup email": return "setup"; case "Email is valid and OTP has been sent": return "otp"; case "Username & password valid": return "success"; case "Skip to login": return "skip"; default: return "login"; } } catch (error: any) { const errorMessage = error?.message || "Email validation failed"; setError(errorMessage); showAuthError(error, "Email validation failed"); throw error; } finally { setLoading(false); } }, [] ); return { validateEmail, loading, error, }; }; // Hook for email setup step export const useEmailSetup = () => { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const setupEmail = useCallback( async ( credentials: LoginFormData, emailData: EmailValidationData ): Promise => { try { setLoading(true); setError(null); const data = { username: credentials.username, password: credentials.password, oldEmail: emailData.oldEmail, newEmail: emailData.newEmail, }; const response = await postSetupEmail(data); if (response?.error) { throw new Error(response.message || "Email setup failed"); } const message = response?.data?.message; switch (message) { case "Email is valid and OTP has been sent": return "otp"; case "The old email is not same": throw new Error("Email is invalid"); default: return "success"; } } catch (error: any) { const errorMessage = error?.message || "Email setup failed"; setError(errorMessage); showAuthError(error, "Email setup failed"); throw error; } finally { setLoading(false); } }, [] ); return { setupEmail, loading, error, }; }; // Hook for OTP verification export const useOTPVerification = () => { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const verifyOTP = useCallback( async (username: string, otp: string): Promise => { try { setLoading(true); setError(null); if (otp.length !== 6) { throw new Error("OTP must be exactly 6 digits"); } const data = { username: username, otpCode: otp, } const response = await verifyOTPByUsername(data); if (response?.error) { throw new Error(response.message || "OTP verification failed"); } return response?.message === "success"; } catch (error: any) { const errorMessage = error?.message || "OTP verification failed"; setError(errorMessage); showAuthError(error, "OTP verification failed"); throw error; } finally { setLoading(false); } }, [] ); return { verifyOTP, loading, error, }; };