website/hooks/useAppointments.ts

297 lines
9.9 KiB
TypeScript
Raw Normal View History

"use client";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useCallback } from "react";
import {
createAppointment,
getAvailableDates,
listAppointments,
getUserAppointments,
getUserAppointmentStats,
getAppointmentDetail,
scheduleAppointment,
rejectAppointment,
getAdminAvailability,
updateAdminAvailability,
getAppointmentStats,
getJitsiMeetingInfo,
getWeeklyAvailability,
getAvailabilityConfig,
checkDateAvailability,
getAvailabilityOverview,
} from "@/lib/actions/appointments";
import type {
CreateAppointmentInput,
ScheduleAppointmentInput,
RejectAppointmentInput,
UpdateAvailabilityInput,
} from "@/lib/schema/appointments";
import type {
Appointment,
AdminAvailability,
AppointmentStats,
UserAppointmentStats,
AvailableDatesResponse,
JitsiMeetingInfo,
WeeklyAvailabilityResponse,
AvailabilityConfig,
CheckDateAvailabilityResponse,
AvailabilityOverview,
} from "@/lib/models/appointments";
export function useAppointments(options?: {
enableAvailableDates?: boolean;
enableStats?: boolean;
enableConfig?: boolean;
enableWeeklyAvailability?: boolean;
enableOverview?: boolean;
}) {
const queryClient = useQueryClient();
const enableAvailableDates = options?.enableAvailableDates ?? false;
const enableStats = options?.enableStats ?? true;
const enableConfig = options?.enableConfig ?? true;
const enableWeeklyAvailability = options?.enableWeeklyAvailability ?? true;
const enableOverview = options?.enableOverview ?? true;
// Get available dates query (optional, disabled by default - using weekly_availability as primary source)
// Can be enabled when explicitly needed (e.g., on admin booking page)
const availableDatesQuery = useQuery<AvailableDatesResponse>({
queryKey: ["appointments", "available-dates"],
queryFn: () => getAvailableDates(),
enabled: enableAvailableDates, // Can be enabled when needed
staleTime: 5 * 60 * 1000, // 5 minutes
retry: 0, // Don't retry failed requests
});
// Get weekly availability query
const weeklyAvailabilityQuery = useQuery<WeeklyAvailabilityResponse>({
queryKey: ["appointments", "weekly-availability"],
queryFn: async () => {
const data = await getWeeklyAvailability();
// Normalize response format - ensure it's always an object with week array
if (Array.isArray(data)) {
return { week: data };
}
return data;
},
enabled: enableWeeklyAvailability,
staleTime: 5 * 60 * 1000, // 5 minutes
});
// Get availability config query
const availabilityConfigQuery = useQuery<AvailabilityConfig>({
queryKey: ["appointments", "availability-config"],
queryFn: () => getAvailabilityConfig(),
enabled: enableConfig,
staleTime: 60 * 60 * 1000, // 1 hour (config rarely changes)
});
// Get availability overview query
const availabilityOverviewQuery = useQuery<AvailabilityOverview>({
queryKey: ["appointments", "availability-overview"],
queryFn: () => getAvailabilityOverview(),
enabled: enableOverview,
staleTime: 5 * 60 * 1000, // 5 minutes
});
// List appointments query
const appointmentsQuery = useQuery<Appointment[]>({
queryKey: ["appointments", "list"],
queryFn: async () => {
const data = await listAppointments();
return data || [];
},
enabled: true, // Enable by default to fetch user appointments
staleTime: 30 * 1000, // 30 seconds
retry: 1, // Retry once on failure
refetchOnMount: true, // Always refetch when component mounts
});
// Get user appointments query (disabled - using listAppointments instead)
const userAppointmentsQuery = useQuery<Appointment[]>({
queryKey: ["appointments", "user"],
queryFn: () => getUserAppointments(),
enabled: false, // Disabled - using listAppointments endpoint instead
staleTime: 30 * 1000, // 30 seconds
});
// Get appointment detail query
const useAppointmentDetail = (id: string | null) => {
return useQuery<Appointment>({
queryKey: ["appointments", "detail", id],
queryFn: () => getAppointmentDetail(id!),
enabled: !!id,
});
};
// Get admin availability query
const adminAvailabilityQuery = useQuery<AdminAvailability>({
queryKey: ["appointments", "admin", "availability"],
queryFn: () => getAdminAvailability(),
staleTime: 5 * 60 * 1000, // 5 minutes
});
// Get appointment stats query
const appointmentStatsQuery = useQuery<AppointmentStats>({
queryKey: ["appointments", "stats"],
queryFn: () => getAppointmentStats(),
staleTime: 1 * 60 * 1000, // 1 minute
});
// Get user appointment stats query
const userAppointmentStatsQuery = useQuery<UserAppointmentStats>({
queryKey: ["appointments", "user", "stats"],
queryFn: () => getUserAppointmentStats(),
enabled: enableStats,
staleTime: 1 * 60 * 1000, // 1 minute
});
// Get Jitsi meeting info query
const useJitsiMeetingInfo = (id: string | null) => {
return useQuery<JitsiMeetingInfo>({
queryKey: ["appointments", "jitsi", id],
queryFn: () => getJitsiMeetingInfo(id!),
enabled: !!id,
staleTime: 30 * 1000, // 30 seconds
});
};
// Create appointment mutation
const createAppointmentMutation = useMutation({
mutationFn: (input: CreateAppointmentInput) => createAppointment(input),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["appointments"] });
},
});
// Schedule appointment mutation
const scheduleAppointmentMutation = useMutation({
mutationFn: ({ id, input }: { id: string; input: ScheduleAppointmentInput }) =>
scheduleAppointment(id, input),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["appointments"] });
},
});
// Reject appointment mutation
const rejectAppointmentMutation = useMutation({
mutationFn: ({ id, input }: { id: string; input: RejectAppointmentInput }) =>
rejectAppointment(id, input),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["appointments"] });
},
});
// Update admin availability mutation
const updateAdminAvailabilityMutation = useMutation({
mutationFn: (input: UpdateAvailabilityInput) => updateAdminAvailability(input),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["appointments", "admin", "availability"] });
queryClient.invalidateQueries({ queryKey: ["appointments", "available-dates"] });
},
});
// Convenience functions
const create = useCallback(
async (input: CreateAppointmentInput) => {
return await createAppointmentMutation.mutateAsync(input);
},
[createAppointmentMutation]
);
const schedule = useCallback(
async (id: string, input: ScheduleAppointmentInput) => {
return await scheduleAppointmentMutation.mutateAsync({ id, input });
},
[scheduleAppointmentMutation]
);
const reject = useCallback(
async (id: string, input: RejectAppointmentInput) => {
return await rejectAppointmentMutation.mutateAsync({ id, input });
},
[rejectAppointmentMutation]
);
const updateAvailability = useCallback(
async (input: UpdateAvailabilityInput) => {
return await updateAdminAvailabilityMutation.mutateAsync(input);
},
[updateAdminAvailabilityMutation]
);
const fetchAppointments = useCallback(
async (email?: string) => {
const data = await listAppointments(email);
queryClient.setQueryData(["appointments", "list"], data);
return data;
},
[queryClient]
);
return {
// Queries
availableDates: availableDatesQuery.data?.dates || [],
availableDatesResponse: availableDatesQuery.data,
weeklyAvailability: weeklyAvailabilityQuery.data,
availabilityConfig: availabilityConfigQuery.data,
availabilityOverview: availabilityOverviewQuery.data,
appointments: appointmentsQuery.data || [],
userAppointments: userAppointmentsQuery.data || [],
adminAvailability: adminAvailabilityQuery.data,
appointmentStats: appointmentStatsQuery.data,
userAppointmentStats: userAppointmentStatsQuery.data,
// Query states
isLoadingAvailableDates: availableDatesQuery.isLoading,
isLoadingWeeklyAvailability: weeklyAvailabilityQuery.isLoading,
isLoadingAvailabilityConfig: availabilityConfigQuery.isLoading,
isLoadingAvailabilityOverview: availabilityOverviewQuery.isLoading,
isLoadingAppointments: appointmentsQuery.isLoading,
isLoadingUserAppointments: userAppointmentsQuery.isLoading,
isLoadingAdminAvailability: adminAvailabilityQuery.isLoading,
isLoadingStats: appointmentStatsQuery.isLoading,
isLoadingUserStats: userAppointmentStatsQuery.isLoading,
// Query refetch functions
refetchAvailableDates: availableDatesQuery.refetch,
refetchWeeklyAvailability: weeklyAvailabilityQuery.refetch,
refetchAvailabilityConfig: availabilityConfigQuery.refetch,
refetchAvailabilityOverview: availabilityOverviewQuery.refetch,
refetchAppointments: appointmentsQuery.refetch,
refetchUserAppointments: userAppointmentsQuery.refetch,
refetchAdminAvailability: adminAvailabilityQuery.refetch,
refetchStats: appointmentStatsQuery.refetch,
refetchUserStats: userAppointmentStatsQuery.refetch,
// Helper functions
checkDateAvailability: async (date: string) => {
return await checkDateAvailability(date);
},
// Hooks for specific queries
useAppointmentDetail,
useJitsiMeetingInfo,
// Mutations
create,
schedule,
reject,
updateAvailability,
fetchAppointments,
// Mutation states
isCreating: createAppointmentMutation.isPending,
isScheduling: scheduleAppointmentMutation.isPending,
isRejecting: rejectAppointmentMutation.isPending,
isUpdatingAvailability: updateAdminAvailabilityMutation.isPending,
// Direct mutation access (if needed)
createAppointmentMutation,
scheduleAppointmentMutation,
rejectAppointmentMutation,
updateAdminAvailabilityMutation,
};
}