Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | 2x 23x 20x 14x 5x 11x 11x 6x 4x 4x 6x 6x 3x 2x | /**
* Speaker Outreach React Query Hooks (Story 5.3 - Task 4b)
*
* Custom hooks for speaker outreach data fetching and mutations
* Features:
* - React Query for server state caching
* - Automatic cache invalidation on updates
* - Per-speaker outreach history caching
*/
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { speakerOutreachService } from '@/services/speakerOutreachService';
import { speakerPoolKeys } from './useSpeakerPool';
import type { RecordOutreachRequest } from '@/types/speakerOutreach.types';
/**
* Query key factory for speaker outreach
* Centralizes query key management for cache invalidation
*/
export const speakerOutreachKeys = {
all: ['speakerOutreach'] as const,
histories: () => [...speakerOutreachKeys.all, 'history'] as const,
history: (eventCode: string, speakerId: string) =>
[...speakerOutreachKeys.histories(), eventCode, speakerId] as const,
};
/**
* Hook to fetch outreach history for a speaker (AC4)
*
* @param eventCode Event code (e.g., "BATbern56")
* @param speakerId Speaker UUID
* @returns Query result with list of outreach attempts (most recent first)
* @example
* const { data: history, isLoading } = useSpeakerOutreachHistory('BATbern56', 'speaker-123');
*/
export function useSpeakerOutreachHistory(eventCode: string, speakerId: string) {
return useQuery({
queryKey: speakerOutreachKeys.history(eventCode, speakerId),
queryFn: () => speakerOutreachService.getOutreachHistory(eventCode, speakerId),
enabled: !!eventCode && !!speakerId,
staleTime: 1000 * 60 * 2, // 2 minutes - outreach data updated frequently during campaigns
});
}
/**
* Hook to record speaker outreach attempt (AC2-3, ORGANIZER only)
*
* @returns Mutation object with mutate function
* @example
* const recordMutation = useRecordOutreach();
* recordMutation.mutate({
* eventCode: 'BATbern56',
* speakerId: 'speaker-123',
* request: { contactMethod: 'email', contactDate: '2025-12-14T10:00:00Z', notes: '...' }
* });
*/
export function useRecordOutreach() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({
eventCode,
speakerId,
request,
}: {
eventCode: string;
speakerId: string;
request: RecordOutreachRequest;
}) => speakerOutreachService.recordOutreach(eventCode, speakerId, request),
onSuccess: (_data, variables) => {
// Invalidate outreach history for this specific speaker to refetch
queryClient.invalidateQueries({
queryKey: speakerOutreachKeys.history(variables.eventCode, variables.speakerId),
});
// Invalidate speaker pool to update status in the dashboard
queryClient.invalidateQueries({
queryKey: speakerPoolKeys.list(variables.eventCode),
});
},
});
}
/**
* Hook to bulk record outreach for multiple speakers (AC6, ORGANIZER only)
*
* @returns Mutation object with mutate function
* @example
* const bulkMutation = useBulkRecordOutreach();
* bulkMutation.mutate({
* eventCode: 'BATbern56',
* speakerIds: ['speaker-1', 'speaker-2', 'speaker-3'],
* request: { contactMethod: 'email', contactDate: '2025-12-14T10:00:00Z', notes: '...' }
* });
*/
export function useBulkRecordOutreach() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({
eventCode,
speakerIds,
request,
}: {
eventCode: string;
speakerIds: string[];
request: RecordOutreachRequest;
}) => speakerOutreachService.bulkRecordOutreach(eventCode, speakerIds, request),
onSuccess: () => {
// Invalidate all outreach histories since we don't know which speakers were updated
queryClient.invalidateQueries({
queryKey: speakerOutreachKeys.histories(),
});
},
});
}
|