304 lines
9.4 KiB
TypeScript
304 lines
9.4 KiB
TypeScript
/**
|
|
* React Hooks for AI Insights
|
|
*
|
|
* Provides React Query hooks for AI Insights API integration.
|
|
*
|
|
* Usage:
|
|
* ```tsx
|
|
* const { data: insights, isLoading } = useAIInsights(tenantId, { priority: 'high' });
|
|
* const { data: stats } = useAIInsightStats(tenantId);
|
|
* const applyMutation = useApplyInsight();
|
|
* ```
|
|
*
|
|
* Last Updated: 2025-11-03
|
|
* Status: ✅ Complete - React Query Integration
|
|
*/
|
|
|
|
import { useQuery, useMutation, useQueryClient, UseQueryOptions, UseMutationOptions } from '@tanstack/react-query';
|
|
import { useState } from 'react';
|
|
import {
|
|
aiInsightsService,
|
|
AIInsight,
|
|
AIInsightFilters,
|
|
AIInsightListResponse,
|
|
AIInsightStatsResponse,
|
|
FeedbackRequest,
|
|
OrchestrationReadyInsightsRequest,
|
|
OrchestrationReadyInsightsResponse,
|
|
} from '../services/aiInsights';
|
|
|
|
// Query Keys
|
|
export const aiInsightsKeys = {
|
|
all: ['aiInsights'] as const,
|
|
lists: () => [...aiInsightsKeys.all, 'list'] as const,
|
|
list: (tenantId: string, filters?: AIInsightFilters) => [...aiInsightsKeys.lists(), tenantId, filters] as const,
|
|
details: () => [...aiInsightsKeys.all, 'detail'] as const,
|
|
detail: (tenantId: string, insightId: string) => [...aiInsightsKeys.details(), tenantId, insightId] as const,
|
|
stats: (tenantId: string, filters?: any) => [...aiInsightsKeys.all, 'stats', tenantId, filters] as const,
|
|
orchestration: (tenantId: string, targetDate: string) => [...aiInsightsKeys.all, 'orchestration', tenantId, targetDate] as const,
|
|
dashboard: (tenantId: string) => [...aiInsightsKeys.all, 'dashboard', tenantId] as const,
|
|
};
|
|
|
|
/**
|
|
* Hook to get AI insights with filters
|
|
*/
|
|
export function useAIInsights(
|
|
tenantId: string,
|
|
filters?: AIInsightFilters,
|
|
options?: Omit<UseQueryOptions<AIInsightListResponse>, 'queryKey' | 'queryFn'>
|
|
) {
|
|
return useQuery({
|
|
queryKey: aiInsightsKeys.list(tenantId, filters),
|
|
queryFn: () => aiInsightsService.getInsights(tenantId, filters),
|
|
staleTime: 1000 * 60 * 2, // 2 minutes
|
|
...options,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Hook to get a single AI insight
|
|
*/
|
|
export function useAIInsight(
|
|
tenantId: string,
|
|
insightId: string,
|
|
options?: Omit<UseQueryOptions<AIInsight>, 'queryKey' | 'queryFn'>
|
|
) {
|
|
return useQuery({
|
|
queryKey: aiInsightsKeys.detail(tenantId, insightId),
|
|
queryFn: () => aiInsightsService.getInsight(tenantId, insightId),
|
|
enabled: !!insightId,
|
|
staleTime: 1000 * 60 * 5, // 5 minutes
|
|
...options,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Hook to get AI insight statistics
|
|
*/
|
|
export function useAIInsightStats(
|
|
tenantId: string,
|
|
filters?: { start_date?: string; end_date?: string },
|
|
options?: Omit<UseQueryOptions<AIInsightStatsResponse>, 'queryKey' | 'queryFn'>
|
|
) {
|
|
return useQuery({
|
|
queryKey: aiInsightsKeys.stats(tenantId, filters),
|
|
queryFn: () => aiInsightsService.getInsightStats(tenantId, filters),
|
|
staleTime: 1000 * 60 * 5, // 5 minutes
|
|
...options,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Hook to get orchestration-ready insights
|
|
*/
|
|
export function useOrchestrationReadyInsights(
|
|
tenantId: string,
|
|
request: OrchestrationReadyInsightsRequest,
|
|
options?: Omit<UseQueryOptions<OrchestrationReadyInsightsResponse>, 'queryKey' | 'queryFn'>
|
|
) {
|
|
return useQuery({
|
|
queryKey: aiInsightsKeys.orchestration(tenantId, request.target_date),
|
|
queryFn: () => aiInsightsService.getOrchestrationReadyInsights(tenantId, request),
|
|
enabled: !!request.target_date,
|
|
staleTime: 1000 * 60 * 10, // 10 minutes
|
|
...options,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Hook to get dashboard summary
|
|
*/
|
|
export function useAIInsightsDashboard(
|
|
tenantId: string,
|
|
options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
|
|
) {
|
|
return useQuery({
|
|
queryKey: aiInsightsKeys.dashboard(tenantId),
|
|
queryFn: () => aiInsightsService.getDashboardSummary(tenantId),
|
|
staleTime: 1000 * 60 * 2, // 2 minutes
|
|
...options,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Hook to get high priority insights
|
|
*/
|
|
export function useHighPriorityInsights(
|
|
tenantId: string,
|
|
limit: number = 10,
|
|
options?: Omit<UseQueryOptions<AIInsight[]>, 'queryKey' | 'queryFn'>
|
|
) {
|
|
return useQuery({
|
|
queryKey: [...aiInsightsKeys.lists(), tenantId, 'highPriority', limit],
|
|
queryFn: () => aiInsightsService.getHighPriorityInsights(tenantId, limit),
|
|
staleTime: 1000 * 60 * 2, // 2 minutes
|
|
...options,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Hook to get actionable insights
|
|
*/
|
|
export function useActionableInsights(
|
|
tenantId: string,
|
|
limit: number = 20,
|
|
options?: Omit<UseQueryOptions<AIInsight[]>, 'queryKey' | 'queryFn'>
|
|
) {
|
|
return useQuery({
|
|
queryKey: [...aiInsightsKeys.lists(), tenantId, 'actionable', limit],
|
|
queryFn: () => aiInsightsService.getActionableInsights(tenantId, limit),
|
|
staleTime: 1000 * 60 * 2, // 2 minutes
|
|
...options,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Hook to get insights by category
|
|
*/
|
|
export function useInsightsByCategory(
|
|
tenantId: string,
|
|
category: string,
|
|
limit: number = 20,
|
|
options?: Omit<UseQueryOptions<AIInsight[]>, 'queryKey' | 'queryFn'>
|
|
) {
|
|
return useQuery({
|
|
queryKey: [...aiInsightsKeys.lists(), tenantId, 'category', category, limit],
|
|
queryFn: () => aiInsightsService.getInsightsByCategory(tenantId, category, limit),
|
|
enabled: !!category,
|
|
staleTime: 1000 * 60 * 2, // 2 minutes
|
|
...options,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Hook to search insights
|
|
*/
|
|
export function useSearchInsights(
|
|
tenantId: string,
|
|
query: string,
|
|
filters?: Partial<AIInsightFilters>,
|
|
options?: Omit<UseQueryOptions<AIInsight[]>, 'queryKey' | 'queryFn'>
|
|
) {
|
|
return useQuery({
|
|
queryKey: [...aiInsightsKeys.lists(), tenantId, 'search', query, filters],
|
|
queryFn: () => aiInsightsService.searchInsights(tenantId, query, filters),
|
|
enabled: query.length > 0,
|
|
staleTime: 1000 * 30, // 30 seconds
|
|
...options,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Mutation hook to apply an insight
|
|
*/
|
|
export function useApplyInsight(
|
|
options?: UseMutationOptions<AIInsight, Error, { tenantId: string; insightId: string }>
|
|
) {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: ({ tenantId, insightId }: { tenantId: string; insightId: string }) =>
|
|
aiInsightsService.applyInsight(tenantId, insightId),
|
|
onSuccess: (_, variables) => {
|
|
// Invalidate all insight queries for this tenant
|
|
queryClient.invalidateQueries({ queryKey: aiInsightsKeys.lists() });
|
|
queryClient.invalidateQueries({ queryKey: aiInsightsKeys.detail(variables.tenantId, variables.insightId) });
|
|
queryClient.invalidateQueries({ queryKey: aiInsightsKeys.stats(variables.tenantId) });
|
|
queryClient.invalidateQueries({ queryKey: aiInsightsKeys.dashboard(variables.tenantId) });
|
|
},
|
|
...options,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Mutation hook to dismiss an insight
|
|
*/
|
|
export function useDismissInsight(
|
|
options?: UseMutationOptions<void, Error, { tenantId: string; insightId: string }>
|
|
) {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: ({ tenantId, insightId }) =>
|
|
aiInsightsService.dismissInsight(tenantId, insightId),
|
|
onSuccess: (_, variables) => {
|
|
queryClient.invalidateQueries({ queryKey: aiInsightsKeys.lists() });
|
|
queryClient.invalidateQueries({ queryKey: aiInsightsKeys.detail(variables.tenantId, variables.insightId) });
|
|
queryClient.invalidateQueries({ queryKey: aiInsightsKeys.stats(variables.tenantId) });
|
|
queryClient.invalidateQueries({ queryKey: aiInsightsKeys.dashboard(variables.tenantId) });
|
|
},
|
|
...options,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Mutation hook to update insight status
|
|
*/
|
|
export function useUpdateInsightStatus(
|
|
options?: UseMutationOptions<AIInsight, Error, { tenantId: string; insightId: string; status: 'acknowledged' | 'in_progress' | 'applied' | 'expired' }>
|
|
) {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: ({ tenantId, insightId, status }) =>
|
|
aiInsightsService.updateInsightStatus(tenantId, insightId, status),
|
|
onSuccess: (_, variables) => {
|
|
queryClient.invalidateQueries({ queryKey: aiInsightsKeys.lists() });
|
|
queryClient.invalidateQueries({ queryKey: aiInsightsKeys.detail(variables.tenantId, variables.insightId) });
|
|
queryClient.invalidateQueries({ queryKey: aiInsightsKeys.stats(variables.tenantId) });
|
|
queryClient.invalidateQueries({ queryKey: aiInsightsKeys.dashboard(variables.tenantId) });
|
|
},
|
|
...options,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Mutation hook to record feedback for an insight
|
|
*/
|
|
export function useRecordFeedback(
|
|
options?: UseMutationOptions<any, Error, { tenantId: string; insightId: string; feedback: FeedbackRequest }>
|
|
) {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: ({ tenantId, insightId, feedback }) =>
|
|
aiInsightsService.recordFeedback(tenantId, insightId, feedback),
|
|
onSuccess: (_, variables) => {
|
|
queryClient.invalidateQueries({ queryKey: aiInsightsKeys.detail(variables.tenantId, variables.insightId) });
|
|
queryClient.invalidateQueries({ queryKey: aiInsightsKeys.stats(variables.tenantId) });
|
|
},
|
|
...options,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Utility hook to manage insight selection
|
|
*/
|
|
export function useInsightSelection() {
|
|
const [selectedInsights, setSelectedInsights] = useState<string[]>([]);
|
|
|
|
const toggleInsight = (insightId: string) => {
|
|
setSelectedInsights((prev) =>
|
|
prev.includes(insightId)
|
|
? prev.filter((id) => id !== insightId)
|
|
: [...prev, insightId]
|
|
);
|
|
};
|
|
|
|
const selectAll = (insightIds: string[]) => {
|
|
setSelectedInsights(insightIds);
|
|
};
|
|
|
|
const clearSelection = () => {
|
|
setSelectedInsights([]);
|
|
};
|
|
|
|
return {
|
|
selectedInsights,
|
|
toggleInsight,
|
|
selectAll,
|
|
clearSelection,
|
|
isSelected: (insightId: string) => selectedInsights.includes(insightId),
|
|
};
|
|
}
|