import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { toast } from 'sonner'; import api, { getErrorMessage } from '@/lib/api'; import axios from 'axios'; import type { CalendarMemberInfo, CalendarInvite, CalendarPermission } from '@/types'; export function useSharedCalendars() { const queryClient = useQueryClient(); const incomingInvitesQuery = useQuery({ queryKey: ['calendar-invites', 'incoming'], queryFn: async () => { const { data } = await api.get( '/shared-calendars/invites/incoming' ); return data; }, refetchOnMount: 'always' as const, }); const fetchMembers = async (calendarId: number) => { const { data } = await api.get( `/shared-calendars/${calendarId}/members` ); return data; }; const useMembersQuery = (calendarId: number | null) => useQuery({ queryKey: ['calendar-members', calendarId], queryFn: () => fetchMembers(calendarId!), enabled: calendarId != null, }); const inviteMutation = useMutation({ mutationFn: async ({ calendarId, connectionId, permission, canAddOthers, }: { calendarId: number; connectionId: number; permission: CalendarPermission; canAddOthers: boolean; }) => { const { data } = await api.post(`/shared-calendars/${calendarId}/invite`, { connection_id: connectionId, permission, can_add_others: canAddOthers, }); return data; }, onSuccess: (_, variables) => { queryClient.invalidateQueries({ queryKey: ['calendar-members', variables.calendarId] }); queryClient.invalidateQueries({ queryKey: ['calendars'] }); toast.success('Invite sent'); }, onError: (error) => { toast.error(getErrorMessage(error, 'Failed to send invite')); }, }); const updateMemberMutation = useMutation({ mutationFn: async ({ calendarId, memberId, permission, canAddOthers, }: { calendarId: number; memberId: number; permission?: CalendarPermission; canAddOthers?: boolean; }) => { const body: Record = {}; if (permission !== undefined) body.permission = permission; if (canAddOthers !== undefined) body.can_add_others = canAddOthers; const { data } = await api.put( `/shared-calendars/${calendarId}/members/${memberId}`, body ); return data; }, onSuccess: (_, variables) => { queryClient.invalidateQueries({ queryKey: ['calendar-members', variables.calendarId] }); toast.success('Member updated'); }, onError: (error) => { toast.error(getErrorMessage(error, 'Failed to update member')); }, }); const removeMemberMutation = useMutation({ mutationFn: async ({ calendarId, memberId, }: { calendarId: number; memberId: number; }) => { await api.delete(`/shared-calendars/${calendarId}/members/${memberId}`); }, onSuccess: (_, variables) => { queryClient.invalidateQueries({ queryKey: ['calendar-members', variables.calendarId] }); queryClient.invalidateQueries({ queryKey: ['calendars'] }); queryClient.invalidateQueries({ queryKey: ['calendars', 'shared'] }); toast.success('Member removed'); }, onError: (error) => { toast.error(getErrorMessage(error, 'Failed to remove member')); }, }); const respondInviteMutation = useMutation({ mutationFn: async ({ inviteId, action, }: { inviteId: number; action: 'accept' | 'reject'; }) => { const { data } = await api.put(`/shared-calendars/invites/${inviteId}/respond`, { action, }); return data; }, onSuccess: (_, variables) => { toast.dismiss(`calendar-invite-${variables.inviteId}`); queryClient.invalidateQueries({ queryKey: ['calendar-invites'] }); queryClient.invalidateQueries({ queryKey: ['calendars', 'shared'] }); queryClient.invalidateQueries({ queryKey: ['calendar-events'] }); queryClient.invalidateQueries({ queryKey: ['notifications'] }); toast.success(variables.action === 'accept' ? 'Calendar added' : 'Invite declined'); }, onError: (error, variables) => { if (axios.isAxiosError(error) && error.response?.status === 409) { toast.dismiss(`calendar-invite-${variables.inviteId}`); queryClient.invalidateQueries({ queryKey: ['calendar-invites'] }); queryClient.invalidateQueries({ queryKey: ['calendars', 'shared'] }); queryClient.invalidateQueries({ queryKey: ['notifications'] }); return; } toast.error(getErrorMessage(error, 'Failed to respond to invite')); }, }); const updateColorMutation = useMutation({ mutationFn: async ({ calendarId, localColor, }: { calendarId: number; localColor: string | null; }) => { await api.put(`/shared-calendars/${calendarId}/members/me/color`, { local_color: localColor, }); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['calendars', 'shared'] }); queryClient.invalidateQueries({ queryKey: ['calendar-events'] }); }, onError: (error) => { toast.error(getErrorMessage(error, 'Failed to update color')); }, }); const leaveCalendarMutation = useMutation({ mutationFn: async ({ calendarId, memberId }: { calendarId: number; memberId: number }) => { await api.delete(`/shared-calendars/${calendarId}/members/${memberId}`); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['calendars', 'shared'] }); queryClient.invalidateQueries({ queryKey: ['calendar-events'] }); toast.success('Left calendar'); }, onError: (error) => { toast.error(getErrorMessage(error, 'Failed to leave calendar')); }, }); return { incomingInvites: incomingInvitesQuery.data ?? [], isLoadingInvites: incomingInvitesQuery.isLoading, useMembersQuery, invite: inviteMutation.mutateAsync, isInviting: inviteMutation.isPending, updateMember: updateMemberMutation.mutateAsync, removeMember: removeMemberMutation.mutateAsync, respondInvite: respondInviteMutation.mutateAsync, isResponding: respondInviteMutation.isPending, updateColor: updateColorMutation.mutateAsync, leaveCalendar: leaveCalendarMutation.mutateAsync, }; }