'use client'; /** * useBlockData Hook * * Generic data fetching hook for Cortex blocks. * Handles loading, error states, and provides refetch capability. * * Epic: E2.S1 - Custom Hooks */ import { useState, useEffect, useCallback } from 'react'; import { useToast } from '@/hooks/use-toast'; import { safeFetch, getErrorInfo } from '@/lib/cortex/error-handler'; // ============================================================================ // Types // ============================================================================ interface UseBlockDataOptions { /** API endpoint (relatief pad) */ endpoint: string; /** Query parameters */ params?: Record; /** Of data moet worden opgehaald */ enabled?: boolean; /** Callback bij error */ onError?: (error: Error) => void; /** Operatie naam voor error messages */ operationName?: string; } interface UseBlockDataResult { /** Opgehaalde data */ data: T | null; /** Loading state */ isLoading: boolean; /** Error message */ error: string | null; /** Refetch functie */ refetch: () => Promise; } // ============================================================================ // Hook // ============================================================================ /** * Generic data fetching hook for Cortex blocks. * * @example * const { data, isLoading, error, refetch } = useBlockData({ * endpoint: `/api/cortex/intake/${intakeId}/risks`, * enabled: Boolean(intakeId), * operationName: 'Risico\'s laden', * }); */ export function useBlockData({ endpoint, params, enabled = true, onError, operationName = 'Data laden', }: UseBlockDataOptions): UseBlockDataResult { const [data, setData] = useState(null); const [isLoading, setIsLoading] = useState(enabled); const [error, setError] = useState(null); const { toast } = useToast(); // Stringify params for dependency comparison const paramsKey = params ? JSON.stringify(params) : ''; const fetchData = useCallback(async () => { if (!enabled) return; setIsLoading(true); setError(null); try { // Build URL with params const url = new URL(endpoint, window.location.origin); if (params) { Object.entries(params).forEach(([key, value]) => { if (value !== undefined) { url.searchParams.set(key, value); } }); } const response = await safeFetch(url.toString(), undefined, { operation: operationName, }); const result = await response.json(); setData(result); } catch (err) { const statusCode = (err as any)?.statusCode; const details = (err as any)?.details; console.error('[useBlockData] Error:', { operation: operationName, statusCode, message: (err as Error)?.message, details, }); const errorInfo = getErrorInfo(err, { operation: operationName, statusCode, }); setError(errorInfo.description); onError?.(err as Error); toast({ variant: 'destructive', title: errorInfo.title, description: errorInfo.description, }); } finally { setIsLoading(false); } }, [endpoint, paramsKey, enabled, onError, operationName, toast]); useEffect(() => { if (enabled) { fetchData(); } }, [fetchData, enabled]); return { data, isLoading, error, refetch: fetchData }; }