'use client'; import { useState, useEffect, useMemo, useCallback } from 'react'; import type { ReviewIQAnalyticsResponse, ReviewIQFilters, } from '@/components/reviewiq/types'; interface UseReviewIQAnalyticsOptions { jobId?: string | null; businessId?: string | null; filters: ReviewIQFilters; issuesPage?: number; issuesPageSize?: number; spansPage?: number; spansPageSize?: number; } interface UseReviewIQAnalyticsResult { data: ReviewIQAnalyticsResponse | null; loading: boolean; error: string | null; refetch: () => void; } /** * Custom hook for fetching ReviewIQ analytics data. * Uses a single API call to fetch all dashboard data. */ export function useReviewIQAnalytics({ jobId, businessId, filters, issuesPage = 1, issuesPageSize = 10, spansPage = 1, spansPageSize = 10, }: UseReviewIQAnalyticsOptions): UseReviewIQAnalyticsResult { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [refetchTrigger, setRefetchTrigger] = useState(0); // Build query params from filters const queryParams = useMemo(() => { const params = new URLSearchParams(); if (jobId) { params.set('job_id', jobId); } if (businessId) { params.set('business_id', businessId); } params.set('time_range', filters.timeRange); if (filters.sentiment.length > 0) { params.set('sentiment', filters.sentiment.join(',')); } if (filters.urtDomain) { params.set('urt_domain', filters.urtDomain); } if (filters.intensity.length > 0) { params.set('intensity', filters.intensity.join(',')); } params.set('issues_page', issuesPage.toString()); params.set('issues_page_size', issuesPageSize.toString()); params.set('spans_page', spansPage.toString()); params.set('spans_page_size', spansPageSize.toString()); return params.toString(); }, [jobId, businessId, filters, issuesPage, issuesPageSize, spansPage, spansPageSize]); const fetchData = useCallback(async () => { if (!jobId && !businessId) { setLoading(false); setData(null); return; } setLoading(true); setError(null); try { const response = await fetch(`/api/pipelines/reviewiq/analytics?${queryParams}`); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.detail || `HTTP error ${response.status}`); } const responseData: ReviewIQAnalyticsResponse = await response.json(); setData(responseData); } catch (err) { console.error('Failed to fetch ReviewIQ analytics:', err); setError(err instanceof Error ? err.message : 'Failed to fetch analytics'); setData(null); } finally { setLoading(false); } }, [queryParams, jobId, businessId]); // Fetch data when params change useEffect(() => { fetchData(); }, [fetchData, refetchTrigger]); const refetch = useCallback(() => { setRefetchTrigger((prev) => prev + 1); }, []); return { data, loading, error, refetch }; } /** * Hook for fetching spans related to a specific issue. */ export function useIssueSpans(issueId: string | null) { const [data, setData] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); useEffect(() => { if (!issueId) { setData([]); return; } const fetchSpans = async () => { setLoading(true); setError(null); try { const response = await fetch(`/api/pipelines/reviewiq/issues/${issueId}/spans`); if (!response.ok) { throw new Error(`HTTP error ${response.status}`); } const spans = await response.json(); setData(spans); } catch (err) { console.error('Failed to fetch issue spans:', err); setError(err instanceof Error ? err.message : 'Failed to fetch spans'); setData([]); } finally { setLoading(false); } }; fetchSpans(); }, [issueId]); return { data, loading, error }; }