/** * Analytics Admin Query Hooks * * React Query hooks for fetching analytics data from the backend. * These hooks use the analytics backend client and provide caching, * automatic refetching, and error handling. * * Supports mock mode when wrapped in MockDataProvider or when * MOCK_ANALYTICS environment variable is set. */ import { useState, useEffect, useCallback, useContext, createContext } from 'react' // Mock data context - allows hooks to use mock data instead of real API calls interface MockContextValue { enabled: boolean mockData: Record } const MockContext = createContext({ enabled: false, mockData: {} }) export const useMockContext = () => useContext(MockContext) // Provider for mock mode export { MockContext } // Types for API responses interface QueryResult { data: T | undefined isLoading: boolean isError: boolean error: Error | null refetch: () => void } // Simple query hook factory - in production, use @tanstack/react-query function useQuery( key: string, fetcher: () => Promise, options?: { enabled?: boolean; refetchInterval?: number; mockData?: T } ): QueryResult { const [data, setData] = useState(undefined) const [isLoading, setIsLoading] = useState(true) const [isError, setIsError] = useState(false) const [error, setError] = useState(null) const enabled = options?.enabled ?? true const mockData = options?.mockData const fetchData = useCallback(async () => { if (!enabled) return setIsLoading(true) setIsError(false) setError(null) try { // If mock data is provided, use it instead of making API call if (mockData !== undefined) { // Small delay to simulate network await new Promise(resolve => setTimeout(resolve, 50)) setData(mockData) } else { const result = await fetcher() setData(result) } } catch (err) { setIsError(true) setError(err instanceof Error ? err : new Error('Unknown error')) } finally { setIsLoading(false) } }, [enabled, fetcher, mockData]) useEffect(() => { fetchData() }, [fetchData, key]) useEffect(() => { if (options?.refetchInterval && enabled && !mockData) { const interval = setInterval(fetchData, options.refetchInterval) return () => clearInterval(interval) } return undefined }, [fetchData, options?.refetchInterval, enabled, mockData]) return { data, isLoading, isError, error, refetch: fetchData, } } // Check if we should use mock data (env var or mock provider) const USE_MOCK = typeof process !== 'undefined' && process.env?.MOCK_ANALYTICS === 'true' // Base URL for analytics API - should be configured via environment const API_BASE = typeof process !== 'undefined' && process.env?.ANALYTICS_API_URL ? process.env.ANALYTICS_API_URL : '/api/analytics' async function fetchJson(endpoint: string): Promise { const response = await fetch(`${API_BASE}${endpoint}`) if (!response.ok) { throw new Error(`Failed to fetch ${endpoint}: ${response.statusText}`) } return response.json() } // Import mock data for fallback import { mockData as MOCK_DATA } from '../providers/MockDataProvider' // ============================================================================ // Revenue Hooks // ============================================================================ export interface RevenueMetrics { totalRevenue: number monthlyRecurring: number oneTimeRevenue: number cryptoRevenue: number growthRate: number avgRevenuePerUser: number } export interface RevenueTrendPoint { date: string revenue: number recurring: number oneTime: number } export interface RevenueBreakdown { bySource: { source: string; amount: number; percentage: number }[] byProvider: { provider: string; amount: number; percentage: number }[] } export function useRevenueMetrics() { return useQuery( 'revenue-metrics', () => fetchJson('/dashboard/revenue'), { mockData: USE_MOCK ? MOCK_DATA.revenueMetrics : undefined } ) } export function useRevenueTrend() { return useQuery( 'revenue-trend', () => fetchJson('/dashboard/revenue-chart'), { mockData: USE_MOCK ? MOCK_DATA.revenueTrend : undefined } ) } export function useRevenueBreakdown() { return useQuery( 'revenue-breakdown', () => fetchJson('/dashboard/revenue-breakdown'), { mockData: USE_MOCK ? MOCK_DATA.revenueBreakdown : undefined } ) } // ============================================================================ // Transactions Hooks // ============================================================================ export interface Transaction { id: string timestamp: string type: string status: string amount: number cryptoAmount?: number cryptoCurrency?: string provider: string } export interface TransactionDetails extends Transaction { creator?: { name: string; id: string } subscriber?: { name: string; id: string } fees?: { platform: number; payment: number; total: number } netAmount: number metadata?: { ip: string; userAgent: string; referrer: string } errorReason?: string } export interface TransactionsResult { transactions: Transaction[] total: number } export function useTransactions(filters: { status?: string type?: string dateRange?: string provider?: string search?: string }) { const queryString = new URLSearchParams( Object.entries(filters).filter(([, v]) => v && v !== 'all') ).toString() return useQuery( `transactions-${queryString}`, () => fetchJson(`/transactions?${queryString}`), { mockData: USE_MOCK ? MOCK_DATA.transactions(filters) : undefined } ) } export function useTransactionDetails(id: string) { return useQuery( `transaction-${id}`, () => fetchJson(`/transactions/${id}`), { enabled: !!id, mockData: USE_MOCK ? MOCK_DATA.transactionDetails(id) : undefined } ) } // ============================================================================ // P&L Hooks // ============================================================================ export interface PnLStatement { revenue: { total: number; crypto: number } costs: { total: number } grossProfit: number operatingExpenses: number netIncome: number ebitda: number margins: { gross: number; net: number } } export interface PnLTrendPoint { date: string revenue: number costs: number netIncome: number } export interface ReserveProgress { target: number current: number percentage: number monthlyContribution: number projectedDate: string } export function usePnLStatement() { return useQuery( 'pnl-statement', () => fetchJson('/reports/pnl'), { mockData: USE_MOCK ? MOCK_DATA.pnlStatement : undefined } ) } export function usePnLTrend() { return useQuery( 'pnl-trend', () => fetchJson('/reports/pnl-trend'), { mockData: USE_MOCK ? MOCK_DATA.pnlTrend : undefined } ) } export function useReserveProgress() { return useQuery( 'reserve-progress', () => fetchJson('/reports/reserve'), { mockData: USE_MOCK ? MOCK_DATA.reserveProgress : undefined } ) } // ============================================================================ // Real-Time Hooks // ============================================================================ export interface RealTimeMetrics { activeUsers: number activeCreators: number liveTransactions: number revenuePerMinute: number systemLoad: number responseTime: number } export interface RealTimeActivity { timestamp: string event: string user: string amount: number } export interface ActiveUserPoint { minute: string count: number } export function useRealTimeMetrics() { return useQuery( 'realtime-metrics', () => fetchJson('/realtime/metrics'), { refetchInterval: USE_MOCK ? undefined : 5000, mockData: USE_MOCK ? MOCK_DATA.realTimeMetrics : undefined } ) } export function useRealTimeActivity() { return useQuery( 'realtime-activity', () => fetchJson('/realtime/activity'), { refetchInterval: USE_MOCK ? undefined : 5000, mockData: USE_MOCK ? MOCK_DATA.realTimeActivity : undefined } ) } export function useActiveUsers() { return useQuery( 'active-users', () => fetchJson('/realtime/active-users'), { refetchInterval: USE_MOCK ? undefined : 5000, mockData: USE_MOCK ? MOCK_DATA.activeUsers : undefined } ) } // ============================================================================ // Costs Hooks // ============================================================================ export interface CostMetrics { totalCosts: number fixedCosts: number variableCosts: number costPerTransaction: number costGrowthRate: number budgetUtilization: number } export interface CostBreakdown { byCategory: { category: string; amount: number; percentage: number }[] byType: { type: string; amount: number; percentage: number }[] } export interface BudgetComparison { totalBudget: number actualCosts: number remaining: number utilization: number byCategory: { category: string; budget: number; actual: number; variance: number }[] } export function useCostMetrics() { return useQuery( 'cost-metrics', () => fetchJson('/dashboard/costs'), { mockData: USE_MOCK ? MOCK_DATA.costMetrics : undefined } ) } export function useCostBreakdown() { return useQuery( 'cost-breakdown', () => fetchJson('/dashboard/costs-breakdown'), { mockData: USE_MOCK ? MOCK_DATA.costBreakdown : undefined } ) } export function useBudgetComparison() { return useQuery( 'budget-comparison', () => fetchJson('/dashboard/budget'), { mockData: USE_MOCK ? MOCK_DATA.budgetComparison : undefined } ) } // ============================================================================ // Performance Hooks // ============================================================================ export interface PerformanceMetrics { avgResponseTime: number p50ResponseTime: number p95ResponseTime: number p99ResponseTime: number requestsPerSecond: number errorRate: number uptime: number } export interface EndpointMetrics { endpoint: string avgResponseTime: number requestCount: number errorRate: number } export function usePerformanceMetrics() { return useQuery( 'performance-metrics', () => fetchJson('/dashboard/performance'), { mockData: USE_MOCK ? MOCK_DATA.performanceMetrics : undefined } ) } export function useEndpointMetrics() { return useQuery( 'endpoint-metrics', () => fetchJson('/dashboard/endpoints'), { mockData: USE_MOCK ? MOCK_DATA.endpointMetrics : undefined } ) } // ============================================================================ // Error Tracking Hooks // ============================================================================ export interface ErrorMetrics { totalErrors: number errorRate: number criticalErrors: number resolvedErrors: number avgResolutionTime: number } export interface ErrorByType { type: string count: number percentage: number } export interface RecentError { id: string type: string message: string endpoint: string count: number lastOccurrence: string severity: string status: string } export function useErrorMetrics() { return useQuery( 'error-metrics', () => fetchJson('/dashboard/errors'), { mockData: USE_MOCK ? MOCK_DATA.errorMetrics : undefined } ) } export function useErrorsByType() { return useQuery( 'errors-by-type', () => fetchJson('/dashboard/errors-by-type'), { mockData: USE_MOCK ? MOCK_DATA.errorsByType : undefined } ) } export function useRecentErrors() { return useQuery( 'recent-errors', () => fetchJson('/dashboard/recent-errors'), { mockData: USE_MOCK ? MOCK_DATA.recentErrors : undefined } ) } // ============================================================================ // Bounce Rate Hooks // ============================================================================ export interface BounceRateMetrics { overallBounceRate: number avgSessionDuration: number pagesPerSession: number exitRate: number } export interface BounceRateByPage { page: string bounceRate: number visits: number } export function useBounceRateMetrics() { return useQuery( 'bounce-rate-metrics', () => fetchJson('/dashboard/bounce-rate'), { mockData: USE_MOCK ? MOCK_DATA.bounceRateMetrics : undefined } ) } export function useBounceRateByPage() { return useQuery( 'bounce-rate-by-page', () => fetchJson('/dashboard/bounce-rate-by-page'), { mockData: USE_MOCK ? MOCK_DATA.bounceRateByPage : undefined } ) } // ============================================================================ // Conversion Hooks // ============================================================================ export interface ConversionMetrics { overallConversionRate: number signupToSubscriber: number visitorToSignup: number freeToTrial: number trialToPaid: number avgTimeToConversion: number } export interface FunnelStage { stage: string count: number rate: number } export interface ConversionBySource { source: string conversions: number rate: number } export function useConversionMetrics() { return useQuery( 'conversion-metrics', () => fetchJson('/dashboard/conversions'), { mockData: USE_MOCK ? MOCK_DATA.conversionMetrics : undefined } ) } export function useFunnelData() { return useQuery( 'funnel-data', () => fetchJson('/funnel'), { mockData: USE_MOCK ? MOCK_DATA.funnelData : undefined } ) } export function useConversionBySource() { return useQuery( 'conversion-by-source', () => fetchJson('/dashboard/conversions-by-source'), { mockData: USE_MOCK ? MOCK_DATA.conversionBySource : undefined } ) } // ============================================================================ // A/B Testing Hooks // ============================================================================ export interface ABTestMetrics { activeTests: number completedTests: number significantResults: number avgLiftPercent: number } export interface ABTest { id: string name: string status: 'running' | 'completed' | 'paused' variants: number participants: number conversionRateA: number conversionRateB: number significance: number startDate: string endDate?: string winner?: string } export function useABTestMetrics() { return useQuery( 'ab-test-metrics', () => fetchJson('/ab-tests/metrics'), { mockData: USE_MOCK ? MOCK_DATA.abTestMetrics : undefined } ) } export function useActiveTests() { return useQuery( 'active-tests', () => fetchJson('/ab-tests'), { mockData: USE_MOCK ? MOCK_DATA.activeTests : undefined } ) }