Initial commit - production deployment
This commit is contained in:
303
frontend/src/api/hooks/aiInsights.ts
Normal file
303
frontend/src/api/hooks/aiInsights.ts
Normal file
@@ -0,0 +1,303 @@
|
||||
/**
|
||||
* 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),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user