/** * Staff Email Hooks * * React Query hooks for the platform staff email client. * Provides data fetching, mutations, and caching for the email UI. */ import { useQuery, useMutation, useQueryClient, useInfiniteQuery } from '@tanstack/react-query'; import * as staffEmailApi from '../api/staffEmail'; import { StaffEmailFolder, StaffEmail, StaffEmailListItem, StaffEmailLabel, StaffEmailFilters, StaffEmailCreateDraft, StaffEmailMove, StaffEmailBulkAction, StaffEmailReply, StaffEmailForward, EmailContactSuggestion, } from '../types'; // Query keys for cache management export const staffEmailKeys = { all: ['staffEmail'] as const, folders: () => [...staffEmailKeys.all, 'folders'] as const, emails: () => [...staffEmailKeys.all, 'emails'] as const, // Use explicit key parts instead of object to ensure proper cache separation emailList: (filters: StaffEmailFilters) => [ ...staffEmailKeys.emails(), 'list', 'folder', filters.folderId ?? 'none', 'account', filters.emailAddressId ?? 'none', 'search', filters.search ?? '', ] as const, emailDetail: (id: number) => [...staffEmailKeys.emails(), 'detail', id] as const, emailThread: (threadId: string) => [...staffEmailKeys.emails(), 'thread', threadId] as const, labels: () => [...staffEmailKeys.all, 'labels'] as const, contacts: (query: string) => [...staffEmailKeys.all, 'contacts', query] as const, userEmailAddresses: () => [...staffEmailKeys.all, 'userEmailAddresses'] as const, }; // ============================================================================ // Folder Hooks // ============================================================================ export const useStaffEmailFolders = () => { return useQuery({ queryKey: staffEmailKeys.folders(), queryFn: staffEmailApi.getFolders, staleTime: 30000, // 30 seconds }); }; export const useCreateStaffEmailFolder = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: (name: string) => staffEmailApi.createFolder(name), onSuccess: () => { queryClient.invalidateQueries({ queryKey: staffEmailKeys.folders() }); }, }); }; export const useUpdateStaffEmailFolder = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, name }: { id: number; name: string }) => staffEmailApi.updateFolder(id, name), onSuccess: () => { queryClient.invalidateQueries({ queryKey: staffEmailKeys.folders() }); }, }); }; export const useDeleteStaffEmailFolder = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: number) => staffEmailApi.deleteFolder(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: staffEmailKeys.folders() }); }, }); }; // ============================================================================ // Email List Hooks // ============================================================================ export const useStaffEmails = (filters: StaffEmailFilters = {}, pageSize: number = 50) => { const queryKey = staffEmailKeys.emailList(filters); // Debug logging console.log('[useStaffEmails] Hook called with:', { filters, queryKey, enabled: !!filters.folderId }); return useInfiniteQuery({ queryKey, queryFn: async ({ pageParam = 1 }) => { console.log('[useStaffEmails] queryFn executing with:', { filters, pageParam }); return staffEmailApi.getEmails(filters, pageParam, pageSize); }, initialPageParam: 1, getNextPageParam: (lastPage, allPages) => { if (lastPage.next) { return allPages.length + 1; } return undefined; }, staleTime: 10000, // 10 seconds // Only fetch when a folder is selected to prevent showing all emails enabled: !!filters.folderId, // Ensure fresh data when filters change refetchOnMount: true, }); }; export const useStaffEmailList = (filters: StaffEmailFilters = {}, page: number = 1, pageSize: number = 50) => { return useQuery({ queryKey: [...staffEmailKeys.emailList(filters), page], queryFn: () => staffEmailApi.getEmails(filters, page, pageSize), staleTime: 10000, }); }; // ============================================================================ // Single Email Hooks // ============================================================================ export const useStaffEmail = (id: number | undefined) => { return useQuery({ queryKey: staffEmailKeys.emailDetail(id!), queryFn: () => staffEmailApi.getEmail(id!), enabled: !!id, }); }; export const useStaffEmailThread = (threadId: string | undefined) => { return useQuery({ queryKey: staffEmailKeys.emailThread(threadId!), queryFn: () => staffEmailApi.getEmailThread(threadId!), enabled: !!threadId, }); }; // ============================================================================ // Draft Hooks // ============================================================================ export const useCreateDraft = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: (data: StaffEmailCreateDraft) => staffEmailApi.createDraft(data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: staffEmailKeys.emails() }); queryClient.invalidateQueries({ queryKey: staffEmailKeys.folders() }); }, }); }; export const useUpdateDraft = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, data }: { id: number; data: Partial }) => staffEmailApi.updateDraft(id, data), onSuccess: (_, variables) => { queryClient.invalidateQueries({ queryKey: staffEmailKeys.emailDetail(variables.id) }); }, }); }; export const useDeleteDraft = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: number) => staffEmailApi.deleteDraft(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: staffEmailKeys.emails() }); queryClient.invalidateQueries({ queryKey: staffEmailKeys.folders() }); }, }); }; // ============================================================================ // Send/Reply/Forward Hooks // ============================================================================ export const useSendEmail = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: number) => staffEmailApi.sendEmail(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: staffEmailKeys.emails() }); queryClient.invalidateQueries({ queryKey: staffEmailKeys.folders() }); }, }); }; export const useReplyToEmail = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, data }: { id: number; data: StaffEmailReply }) => staffEmailApi.replyToEmail(id, data), onSuccess: (_, variables) => { queryClient.invalidateQueries({ queryKey: staffEmailKeys.emails() }); queryClient.invalidateQueries({ queryKey: staffEmailKeys.folders() }); queryClient.invalidateQueries({ queryKey: staffEmailKeys.emailDetail(variables.id) }); }, }); }; export const useForwardEmail = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, data }: { id: number; data: StaffEmailForward }) => staffEmailApi.forwardEmail(id, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: staffEmailKeys.emails() }); queryClient.invalidateQueries({ queryKey: staffEmailKeys.folders() }); }, }); }; // ============================================================================ // Email Action Hooks // ============================================================================ export const useMarkAsRead = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: number) => staffEmailApi.markAsRead(id), onSuccess: (_, id) => { queryClient.invalidateQueries({ queryKey: staffEmailKeys.emailDetail(id) }); queryClient.invalidateQueries({ queryKey: staffEmailKeys.folders() }); }, // Optimistic update onMutate: async (id) => { await queryClient.cancelQueries({ queryKey: staffEmailKeys.emailDetail(id) }); const previousEmail = queryClient.getQueryData(staffEmailKeys.emailDetail(id)); if (previousEmail) { queryClient.setQueryData(staffEmailKeys.emailDetail(id), { ...previousEmail, isRead: true, }); } return { previousEmail }; }, onError: (err, id, context) => { if (context?.previousEmail) { queryClient.setQueryData(staffEmailKeys.emailDetail(id), context.previousEmail); } }, }); }; export const useMarkAsUnread = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: number) => staffEmailApi.markAsUnread(id), onSuccess: (_, id) => { queryClient.invalidateQueries({ queryKey: staffEmailKeys.emailDetail(id) }); queryClient.invalidateQueries({ queryKey: staffEmailKeys.folders() }); }, }); }; export const useStarEmail = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: number) => staffEmailApi.starEmail(id), onSuccess: (_, id) => { queryClient.invalidateQueries({ queryKey: staffEmailKeys.emailDetail(id) }); }, // Optimistic update onMutate: async (id) => { await queryClient.cancelQueries({ queryKey: staffEmailKeys.emailDetail(id) }); const previousEmail = queryClient.getQueryData(staffEmailKeys.emailDetail(id)); if (previousEmail) { queryClient.setQueryData(staffEmailKeys.emailDetail(id), { ...previousEmail, isStarred: true, }); } return { previousEmail }; }, onError: (err, id, context) => { if (context?.previousEmail) { queryClient.setQueryData(staffEmailKeys.emailDetail(id), context.previousEmail); } }, }); }; export const useUnstarEmail = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: number) => staffEmailApi.unstarEmail(id), onSuccess: (_, id) => { queryClient.invalidateQueries({ queryKey: staffEmailKeys.emailDetail(id) }); }, }); }; export const useArchiveEmail = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: number) => staffEmailApi.archiveEmail(id), onSuccess: () => { // Reset and refetch all email list queries queryClient.resetQueries({ queryKey: staffEmailKeys.emails() }); queryClient.invalidateQueries({ queryKey: staffEmailKeys.folders() }); }, }); }; export const useTrashEmail = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: number) => staffEmailApi.trashEmail(id), onSuccess: () => { // Reset and refetch all email list queries queryClient.resetQueries({ queryKey: staffEmailKeys.emails() }); queryClient.invalidateQueries({ queryKey: staffEmailKeys.folders() }); }, }); }; export const useRestoreEmail = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: number) => staffEmailApi.restoreEmail(id), onSuccess: () => { queryClient.resetQueries({ queryKey: staffEmailKeys.emails() }); queryClient.invalidateQueries({ queryKey: staffEmailKeys.folders() }); }, }); }; export const usePermanentlyDeleteEmail = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: number) => staffEmailApi.permanentlyDeleteEmail(id), onSuccess: () => { queryClient.resetQueries({ queryKey: staffEmailKeys.emails() }); queryClient.invalidateQueries({ queryKey: staffEmailKeys.folders() }); }, }); }; export const useMoveEmails = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: (data: StaffEmailMove) => staffEmailApi.moveEmails(data), onSuccess: () => { queryClient.resetQueries({ queryKey: staffEmailKeys.emails() }); queryClient.invalidateQueries({ queryKey: staffEmailKeys.folders() }); }, }); }; export const useBulkEmailAction = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: (data: StaffEmailBulkAction) => staffEmailApi.bulkAction(data), onSuccess: () => { queryClient.resetQueries({ queryKey: staffEmailKeys.emails() }); queryClient.invalidateQueries({ queryKey: staffEmailKeys.folders() }); }, }); }; // ============================================================================ // Label Hooks // ============================================================================ export const useStaffEmailLabels = () => { return useQuery({ queryKey: staffEmailKeys.labels(), queryFn: staffEmailApi.getLabels, staleTime: 60000, // 1 minute }); }; export const useCreateLabel = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ name, color }: { name: string; color: string }) => staffEmailApi.createLabel(name, color), onSuccess: () => { queryClient.invalidateQueries({ queryKey: staffEmailKeys.labels() }); }, }); }; export const useUpdateLabel = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, data }: { id: number; data: { name?: string; color?: string } }) => staffEmailApi.updateLabel(id, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: staffEmailKeys.labels() }); }, }); }; export const useDeleteLabel = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: number) => staffEmailApi.deleteLabel(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: staffEmailKeys.labels() }); queryClient.invalidateQueries({ queryKey: staffEmailKeys.emails() }); }, }); }; export const useAddLabelToEmail = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ emailId, labelId }: { emailId: number; labelId: number }) => staffEmailApi.addLabelToEmail(emailId, labelId), onSuccess: (_, { emailId }) => { queryClient.invalidateQueries({ queryKey: staffEmailKeys.emailDetail(emailId) }); }, }); }; export const useRemoveLabelFromEmail = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ emailId, labelId }: { emailId: number; labelId: number }) => staffEmailApi.removeLabelFromEmail(emailId, labelId), onSuccess: (_, { emailId }) => { queryClient.invalidateQueries({ queryKey: staffEmailKeys.emailDetail(emailId) }); }, }); }; // ============================================================================ // Contact Search Hook // ============================================================================ export const useContactSearch = (query: string) => { return useQuery({ queryKey: staffEmailKeys.contacts(query), queryFn: () => staffEmailApi.searchContacts(query), enabled: query.length >= 2, staleTime: 30000, }); }; // ============================================================================ // Attachment Hook // ============================================================================ export const useUploadAttachment = () => { return useMutation({ mutationFn: ({ file, emailId }: { file: File; emailId?: number }) => staffEmailApi.uploadAttachment(file, emailId), }); }; export const useDeleteAttachment = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: number) => staffEmailApi.deleteAttachment(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: staffEmailKeys.emails() }); }, }); }; // ============================================================================ // Sync Hook // ============================================================================ export const useSyncEmails = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: () => staffEmailApi.syncEmails(), onSuccess: () => { queryClient.invalidateQueries({ queryKey: staffEmailKeys.emails() }); queryClient.invalidateQueries({ queryKey: staffEmailKeys.folders() }); }, }); }; export const useFullSyncEmails = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: () => staffEmailApi.fullSyncEmails(), onSuccess: () => { // Invalidate after a delay to allow sync to complete setTimeout(() => { queryClient.invalidateQueries({ queryKey: staffEmailKeys.emails() }); queryClient.invalidateQueries({ queryKey: staffEmailKeys.folders() }); queryClient.invalidateQueries({ queryKey: staffEmailKeys.userEmailAddresses() }); }, 2000); }, }); }; // ============================================================================ // User Email Addresses Hook // ============================================================================ export const useUserEmailAddresses = () => { return useQuery({ queryKey: staffEmailKeys.userEmailAddresses(), queryFn: staffEmailApi.getUserEmailAddresses, staleTime: 60000, // 1 minute }); };