2025-11-23 13:29:31 +00:00
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
|
|
|
import { useRouter } from "next/navigation";
|
|
|
|
|
import { useCallback } from "react";
|
|
|
|
|
import {
|
|
|
|
|
loginUser,
|
|
|
|
|
registerUser,
|
|
|
|
|
verifyOtp,
|
|
|
|
|
resendOtp,
|
|
|
|
|
forgotPassword,
|
|
|
|
|
verifyPasswordResetOtp,
|
|
|
|
|
resetPassword,
|
|
|
|
|
refreshToken,
|
|
|
|
|
getStoredTokens,
|
|
|
|
|
getStoredUser,
|
|
|
|
|
storeTokens,
|
|
|
|
|
storeUser,
|
|
|
|
|
clearAuthData,
|
|
|
|
|
} from "@/lib/actions/auth";
|
|
|
|
|
import type {
|
|
|
|
|
LoginInput,
|
|
|
|
|
RegisterInput,
|
|
|
|
|
VerifyOtpInput,
|
|
|
|
|
ResendOtpInput,
|
|
|
|
|
ForgotPasswordInput,
|
|
|
|
|
VerifyPasswordResetOtpInput,
|
|
|
|
|
ResetPasswordInput,
|
|
|
|
|
} from "@/lib/schema/auth";
|
|
|
|
|
import type { User } from "@/lib/models/auth";
|
|
|
|
|
|
|
|
|
|
export function useAuth() {
|
|
|
|
|
const router = useRouter();
|
|
|
|
|
const queryClient = useQueryClient();
|
|
|
|
|
|
|
|
|
|
// Get current user from storage
|
|
|
|
|
const { data: user } = useQuery<User | null>({
|
|
|
|
|
queryKey: ["auth", "user"],
|
|
|
|
|
queryFn: () => getStoredUser(),
|
|
|
|
|
staleTime: Infinity,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Check if user is authenticated
|
|
|
|
|
const isAuthenticated = !!user && !!getStoredTokens().access;
|
|
|
|
|
|
2025-11-23 21:13:18 +00:00
|
|
|
// Check if user is admin (check multiple possible field names)
|
|
|
|
|
const isAdmin =
|
|
|
|
|
user?.is_admin === true ||
|
|
|
|
|
(user as any)?.isAdmin === true ||
|
|
|
|
|
(user as any)?.is_staff === true ||
|
|
|
|
|
(user as any)?.isStaff === true ||
|
|
|
|
|
(user as any)?.is_superuser === true ||
|
|
|
|
|
(user as any)?.isSuperuser === true;
|
2025-11-23 13:29:31 +00:00
|
|
|
|
|
|
|
|
// Login mutation
|
|
|
|
|
const loginMutation = useMutation({
|
|
|
|
|
mutationFn: (input: LoginInput) => loginUser(input),
|
|
|
|
|
onSuccess: (data) => {
|
|
|
|
|
if (data.tokens && data.user) {
|
|
|
|
|
storeTokens(data.tokens);
|
|
|
|
|
storeUser(data.user);
|
|
|
|
|
queryClient.setQueryData(["auth", "user"], data.user);
|
|
|
|
|
queryClient.invalidateQueries({ queryKey: ["auth"] });
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Register mutation
|
|
|
|
|
const registerMutation = useMutation({
|
|
|
|
|
mutationFn: (input: RegisterInput) => registerUser(input),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Verify OTP mutation
|
|
|
|
|
const verifyOtpMutation = useMutation({
|
|
|
|
|
mutationFn: (input: VerifyOtpInput) => verifyOtp(input),
|
|
|
|
|
onSuccess: (data) => {
|
|
|
|
|
if (data.tokens && data.user) {
|
|
|
|
|
storeTokens(data.tokens);
|
|
|
|
|
storeUser(data.user);
|
|
|
|
|
queryClient.setQueryData(["auth", "user"], data.user);
|
|
|
|
|
queryClient.invalidateQueries({ queryKey: ["auth"] });
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Resend OTP mutation
|
|
|
|
|
const resendOtpMutation = useMutation({
|
|
|
|
|
mutationFn: (input: ResendOtpInput) => resendOtp(input),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Forgot password mutation
|
|
|
|
|
const forgotPasswordMutation = useMutation({
|
|
|
|
|
mutationFn: (input: ForgotPasswordInput) => forgotPassword(input),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Verify password reset OTP mutation
|
|
|
|
|
const verifyPasswordResetOtpMutation = useMutation({
|
|
|
|
|
mutationFn: (input: VerifyPasswordResetOtpInput) => verifyPasswordResetOtp(input),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Reset password mutation
|
|
|
|
|
const resetPasswordMutation = useMutation({
|
|
|
|
|
mutationFn: (input: ResetPasswordInput) => resetPassword(input),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Refresh token mutation
|
|
|
|
|
const refreshTokenMutation = useMutation({
|
|
|
|
|
mutationFn: (refresh: string) => refreshToken({ refresh }),
|
|
|
|
|
onSuccess: (tokens) => {
|
|
|
|
|
storeTokens(tokens);
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Logout function
|
|
|
|
|
const logout = useCallback(() => {
|
|
|
|
|
clearAuthData();
|
|
|
|
|
queryClient.clear();
|
2025-11-23 21:13:18 +00:00
|
|
|
// Don't redirect here - let components handle redirect as needed
|
|
|
|
|
}, [queryClient]);
|
2025-11-23 13:29:31 +00:00
|
|
|
|
|
|
|
|
// Login function
|
|
|
|
|
const login = useCallback(
|
|
|
|
|
async (input: LoginInput) => {
|
|
|
|
|
try {
|
|
|
|
|
const result = await loginMutation.mutateAsync(input);
|
|
|
|
|
return result;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
[loginMutation]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Register function
|
|
|
|
|
const register = useCallback(
|
|
|
|
|
async (input: RegisterInput) => {
|
|
|
|
|
try {
|
|
|
|
|
const result = await registerMutation.mutateAsync(input);
|
|
|
|
|
return result;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
[registerMutation]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Verify OTP function
|
|
|
|
|
const verifyOtpCode = useCallback(
|
|
|
|
|
async (input: VerifyOtpInput) => {
|
|
|
|
|
try {
|
|
|
|
|
const result = await verifyOtpMutation.mutateAsync(input);
|
|
|
|
|
return result;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
[verifyOtpMutation]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
// State
|
|
|
|
|
user,
|
|
|
|
|
isAuthenticated,
|
|
|
|
|
isAdmin,
|
|
|
|
|
isLoading: loginMutation.isPending || registerMutation.isPending,
|
|
|
|
|
|
|
|
|
|
// Actions
|
|
|
|
|
login,
|
|
|
|
|
register,
|
|
|
|
|
logout,
|
|
|
|
|
verifyOtp: verifyOtpCode,
|
|
|
|
|
|
|
|
|
|
// Mutations (for direct access if needed)
|
|
|
|
|
loginMutation,
|
|
|
|
|
registerMutation,
|
|
|
|
|
verifyOtpMutation,
|
|
|
|
|
resendOtpMutation,
|
|
|
|
|
forgotPasswordMutation,
|
|
|
|
|
verifyPasswordResetOtpMutation,
|
|
|
|
|
resetPasswordMutation,
|
|
|
|
|
refreshTokenMutation,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|