import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; export interface DashboardStats { totalUsers: number; activeClients: number; totalRevenue: number; revenueGrowth: number; } export interface User { id: string; email: string; firstName: string; lastName: string; role: string; phone?: string; gymId?: string; gymName?: string | null; createdAt?: string; isCheckedIn?: boolean; checkInTime?: string; lastCheckInTime?: string; checkInsThisWeek?: number; checkInsThisMonth?: number; client?: { id: string; membershipType: string; membershipStatus: string; joinDate: string; lastVisit?: string; }; } export interface Recommendation { id: string; userId: string; type: string; title: string; description: string; content?: string; recommendationText?: string; activityPlan?: string; dietPlan?: string; status: "pending" | "approved" | "rejected"; createdAt: string; user?: User; } export interface Gym { id: string; name: string; address?: string; } export interface AttendanceRecord { id: string; userId: string; checkIn: string; checkOut?: string; date: string; type?: string; } export interface Invitation { id: string; emailAddress: string; publicMetadata: { role?: string; gymId?: string; createdBy?: string; } | null; status: "pending" | "accepted" | "revoked" | "expired"; url?: string; createdAt: number; updatedAt: number; revoked?: boolean; } async function fetchApi(url: string, options?: RequestInit): Promise { const response = await fetch(url, { ...options, headers: { "Content-Type": "application/json", ...options?.headers, }, }); if (!response.ok) { const error = await response .json() .catch(() => ({ error: "Request failed" })); throw new Error(error.error || `HTTP ${response.status}`); } return response.json(); } export function useDashboardStats(gymId?: string) { return useQuery({ queryKey: ["dashboard-stats", gymId], queryFn: () => { const url = gymId ? `/api/admin/stats?gymId=${gymId}` : "/api/admin/stats"; return fetchApi<{ data: { stats: DashboardStats } }>(url).then( (res) => res.data?.stats, ); }, }); } export function useUsers(filters?: { role?: string; gymId?: string; search?: string; }) { const params = new URLSearchParams(); if (filters?.role) params.set("role", filters.role); if (filters?.gymId) params.set("gymId", filters.gymId); if (filters?.search) params.set("search", filters.search); const queryString = params.toString(); const url = queryString ? `/api/users?${queryString}` : "/api/users"; return useQuery({ queryKey: ["users", filters], queryFn: () => fetchApi<{ data: { users: User[] } }>(url).then( (res) => res.data?.users ?? [], ), }); } export function useUser(userId: string) { return useQuery({ queryKey: ["user", userId], queryFn: () => fetchApi<{ data: { user: User } }>(`/api/users?id=${userId}`).then( (res) => res.data?.user, ), enabled: !!userId, }); } export function useRecommendations(filters?: { userId?: string; status?: string; }) { const params = new URLSearchParams(); if (filters?.userId) params.set("userId", filters.userId); if (filters?.status) params.set("status", filters.status); const queryString = params.toString(); const url = queryString ? `/api/recommendations?${queryString}` : "/api/recommendations"; return useQuery({ queryKey: ["recommendations", filters], queryFn: () => fetchApi<{ data: { recommendations: Recommendation[] } }>(url).then( (res) => res.data?.recommendations ?? [], ), }); } export function usePendingRecommendationsCount() { return useQuery({ queryKey: ["pending-recommendations-count"], queryFn: () => fetchApi<{ data: { recommendations: Recommendation[] } }>( "/api/recommendations", ).then( (res) => (res.data?.recommendations ?? []).filter( (r) => r.status === "pending", ).length, ), refetchInterval: 30000, }); } export function useGyms() { return useQuery({ queryKey: ["gyms"], queryFn: () => fetchApi<{ data: { gyms: Gym[] } }>("/api/gyms").then( (res) => res.data?.gyms ?? (Array.isArray(res) ? res : []), ), }); } export function useAttendance(filters?: { userId?: string; date?: string }) { const params = new URLSearchParams(); if (filters?.userId) params.set("userId", filters.userId); if (filters?.date) params.set("date", filters.date); const queryString = params.toString(); const url = queryString ? `/api/admin/attendance?${queryString}` : "/api/admin/attendance"; return useQuery({ queryKey: ["attendance", filters], queryFn: () => fetchApi<{ data: { records: AttendanceRecord[] } }>(url).then( (res) => res.data?.records ?? [], ), }); } export function useCreateUser() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (data: { email: string; firstName: string; lastName: string; role: string; phone?: string; gymId?: string; }) => fetchApi<{ data: { userId: string } }>("/api/users/create", { method: "POST", body: JSON.stringify(data), }).then((res) => res.data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["users"] }); queryClient.invalidateQueries({ queryKey: ["dashboard-stats"] }); }, }); } export function useUpdateUser() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (data: { id: string; [key: string]: unknown }) => fetchApi<{ data: { success: boolean } }>(`/api/users?id=${data.id}`, { method: "PUT", body: JSON.stringify(data), }), onSuccess: (_, variables) => { queryClient.invalidateQueries({ queryKey: ["users"] }); queryClient.invalidateQueries({ queryKey: ["user", variables.id] }); }, }); } export function useDeleteUser() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (userId: string) => fetchApi<{ data: { success: boolean } }>(`/api/users?id=${userId}`, { method: "DELETE", }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["users"] }); queryClient.invalidateQueries({ queryKey: ["dashboard-stats"] }); }, }); } export function useGenerateRecommendations() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (userId?: string) => fetchApi<{ data: { success: boolean; recommendations?: Recommendation[] }; }>("/api/recommendations/generate", { method: "POST", body: userId ? JSON.stringify({ userId }) : undefined, }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["recommendations"] }); queryClient.invalidateQueries({ queryKey: ["pending-recommendations-count"], }); }, }); } export function useApproveRecommendation() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (data: { recommendationId: string; approved: boolean }) => fetchApi<{ data: { success: boolean } }>("/api/recommendations/approve", { method: "POST", body: JSON.stringify(data), }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["recommendations"] }); queryClient.invalidateQueries({ queryKey: ["pending-recommendations-count"], }); }, }); } export function useUpdateRecommendation() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (data: { id: string; content?: string; activityPlan?: string; dietPlan?: string; }) => fetchApi<{ data: { success: boolean } }>("/api/recommendations", { method: "PUT", body: JSON.stringify(data), }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["recommendations"] }); }, }); } export function useCheckIn() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (userId: string) => fetchApi<{ data: { success: boolean; record: AttendanceRecord } }>( "/api/attendance/check-in", { method: "POST", body: JSON.stringify({ userId }), }, ), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["attendance"] }); }, }); } export function useCheckOut() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (userId: string) => fetchApi<{ data: { success: boolean } }>("/api/attendance/check-out", { method: "POST", body: JSON.stringify({ userId }), }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["attendance"] }); }, }); } export function useInvitations(gymId?: string) { return useQuery({ queryKey: ["invitations", gymId], queryFn: () => { const url = gymId ? `/api/invitations?gymId=${gymId}` : "/api/invitations"; return fetchApi<{ data: { invitations: Invitation[] } }>(url).then( (res) => res.data?.invitations ?? [], ); }, refetchInterval: (query) => { const data = query.state.data; const hasData = data && data.length > 0; const fetchCount = query.state.dataUpdateCount; // Poll every 2 seconds if: // 1. No invitations returned yet // 2. Haven't exceeded 5 attempts (10 seconds total) if (!hasData && fetchCount < 5) { return 2000; } // Stop polling after 10 seconds or when data is present return false; }, }); } export function useSendInvitation() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (data: { email: string; role: string }) => fetchApi<{ data: { success: boolean } }>("/api/invitations", { method: "POST", body: JSON.stringify(data), }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["invitations"] }); }, }); } export function useRevokeInvitation() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (invitationId: string) => fetchApi<{ data: { success: boolean } }>( `/api/invitations/${invitationId}`, { method: "DELETE" }, ), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["invitations"] }); queryClient.invalidateQueries({ queryKey: ["users"] }); }, }); } export function useResendInvitation() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (invitationId: string) => fetchApi<{ data: { invitation: any } }>( `/api/invitations/${invitationId}/resend`, { method: "POST" }, ), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["invitations"] }); }, }); } export interface AnalyticsData { userGrowth: { label: string; value: number }[]; membershipDistribution: { label: string; value: number; color: string }[]; revenue: { label: string; value: number; color: string }[]; } export function useAnalytics(months: number = 6, gymId?: string) { return useQuery({ queryKey: ["analytics", months, gymId], queryFn: () => { const url = gymId ? `/api/admin/analytics?months=${months}&gymId=${gymId}` : `/api/admin/analytics?months=${months}`; return fetchApi<{ data: { analytics: AnalyticsData } }>(url).then( (res) => res.data?.analytics, ); }, }); }