2025-11-23 21:43:13 +00:00
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
|
|
|
import { useCallback } from "react";
|
|
|
|
|
import {
|
|
|
|
|
createAppointment,
|
|
|
|
|
getAvailableDates,
|
|
|
|
|
listAppointments,
|
|
|
|
|
getUserAppointments,
|
|
|
|
|
getAppointmentDetail,
|
|
|
|
|
scheduleAppointment,
|
|
|
|
|
rejectAppointment,
|
|
|
|
|
getAdminAvailability,
|
|
|
|
|
updateAdminAvailability,
|
|
|
|
|
getAppointmentStats,
|
|
|
|
|
getJitsiMeetingInfo,
|
|
|
|
|
} from "@/lib/actions/appointments";
|
|
|
|
|
import type {
|
|
|
|
|
CreateAppointmentInput,
|
|
|
|
|
ScheduleAppointmentInput,
|
|
|
|
|
RejectAppointmentInput,
|
|
|
|
|
UpdateAvailabilityInput,
|
|
|
|
|
} from "@/lib/schema/appointments";
|
|
|
|
|
import type {
|
|
|
|
|
Appointment,
|
|
|
|
|
AdminAvailability,
|
|
|
|
|
AppointmentStats,
|
|
|
|
|
JitsiMeetingInfo,
|
|
|
|
|
} from "@/lib/models/appointments";
|
|
|
|
|
|
|
|
|
|
export function useAppointments() {
|
|
|
|
|
const queryClient = useQueryClient();
|
|
|
|
|
|
|
|
|
|
// Get available dates query
|
2025-11-25 21:04:22 +00:00
|
|
|
const availableDatesQuery = useQuery<AvailableDatesResponse>({
|
2025-11-23 21:43:13 +00:00
|
|
|
queryKey: ["appointments", "available-dates"],
|
|
|
|
|
queryFn: () => getAvailableDates(),
|
|
|
|
|
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// List appointments query
|
|
|
|
|
const appointmentsQuery = useQuery<Appointment[]>({
|
|
|
|
|
queryKey: ["appointments", "list"],
|
|
|
|
|
queryFn: () => listAppointments(),
|
|
|
|
|
enabled: false, // Only fetch when explicitly called
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Get user appointments query
|
|
|
|
|
const userAppointmentsQuery = useQuery<Appointment[]>({
|
|
|
|
|
queryKey: ["appointments", "user"],
|
|
|
|
|
queryFn: () => getUserAppointments(),
|
|
|
|
|
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 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
|
2025-11-25 21:04:22 +00:00
|
|
|
availableDates: availableDatesQuery.data?.dates || [],
|
|
|
|
|
availableDatesResponse: availableDatesQuery.data,
|
2025-11-23 21:43:13 +00:00
|
|
|
appointments: appointmentsQuery.data || [],
|
|
|
|
|
userAppointments: userAppointmentsQuery.data || [],
|
|
|
|
|
adminAvailability: adminAvailabilityQuery.data,
|
|
|
|
|
appointmentStats: appointmentStatsQuery.data,
|
|
|
|
|
|
|
|
|
|
// Query states
|
|
|
|
|
isLoadingAvailableDates: availableDatesQuery.isLoading,
|
|
|
|
|
isLoadingAppointments: appointmentsQuery.isLoading,
|
|
|
|
|
isLoadingUserAppointments: userAppointmentsQuery.isLoading,
|
|
|
|
|
isLoadingAdminAvailability: adminAvailabilityQuery.isLoading,
|
|
|
|
|
isLoadingStats: appointmentStatsQuery.isLoading,
|
|
|
|
|
|
|
|
|
|
// Query refetch functions
|
|
|
|
|
refetchAvailableDates: availableDatesQuery.refetch,
|
|
|
|
|
refetchAppointments: appointmentsQuery.refetch,
|
|
|
|
|
refetchUserAppointments: userAppointmentsQuery.refetch,
|
|
|
|
|
refetchAdminAvailability: adminAvailabilityQuery.refetch,
|
|
|
|
|
refetchStats: appointmentStatsQuery.refetch,
|
|
|
|
|
|
|
|
|
|
// 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,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|