performance improvments
This commit is contained in:
1 parent
912981e3f3
commit
02c9681aca
13 files changed
+805
-474
No files matched your search
@@ -0,0 +1,380 @@
|
||||
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;
|
||||
}
|
||||
|
||||
async function fetchApi<T>(url: string, options?: RequestInit): Promise<T> {
|
||||
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 ?? [],
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
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() {
|
||||
return useQuery({
|
||||
queryKey: ["invitations"],
|
||||
queryFn: () =>
|
||||
fetchApi<{ data: { invitations: unknown[] } }>("/api/invitations").then(
|
||||
(res) => res.data?.invitations ?? [],
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
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 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) {
|
||||
return useQuery({
|
||||
queryKey: ["analytics", months],
|
||||
queryFn: () =>
|
||||
fetchApi<{ data: { analytics: AnalyticsData } }>(
|
||||
`/api/admin/analytics?months=${months}`,
|
||||
).then((res) => res.data?.analytics),
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user