Files
triqura-ecd/lib/cortex/hooks/use-block-data.ts
colinislit 601ad8c3f0 fix(cortex): integrate intake intents into full chat + artifact flow
Fixes 4 bugs preventing intake blocks from working:

1. Chat API validation: Add 'nudge' to message type enum, allow empty
   content for streaming messages
2. AI chat recognition: Add P3 intake intents (intake_status,
   risico_query, diagnose_query, intake_navigeer) to system prompt
   with triggers, entities, and JSON examples
3. Artifact rendering: Add intake block imports and switch cases
   to artifact-container.tsx
4. API 400 error: Convert null to undefined for optional Zod params
   (searchParams.get returns null, Zod .optional() expects undefined)

Also adds:
- Testplan for E2E testing (60+ test cases)
- Session log with lessons learned and new intent checklist

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 00:07:10 +01:00

132 lines
3.4 KiB
TypeScript

'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<T> {
/** API endpoint (relatief pad) */
endpoint: string;
/** Query parameters */
params?: Record<string, string | undefined>;
/** Of data moet worden opgehaald */
enabled?: boolean;
/** Callback bij error */
onError?: (error: Error) => void;
/** Operatie naam voor error messages */
operationName?: string;
}
interface UseBlockDataResult<T> {
/** Opgehaalde data */
data: T | null;
/** Loading state */
isLoading: boolean;
/** Error message */
error: string | null;
/** Refetch functie */
refetch: () => Promise<void>;
}
// ============================================================================
// Hook
// ============================================================================
/**
* Generic data fetching hook for Cortex blocks.
*
* @example
* const { data, isLoading, error, refetch } = useBlockData<RiskData>({
* endpoint: `/api/cortex/intake/${intakeId}/risks`,
* enabled: Boolean(intakeId),
* operationName: 'Risico\'s laden',
* });
*/
export function useBlockData<T>({
endpoint,
params,
enabled = true,
onError,
operationName = 'Data laden',
}: UseBlockDataOptions<T>): UseBlockDataResult<T> {
const [data, setData] = useState<T | null>(null);
const [isLoading, setIsLoading] = useState(enabled);
const [error, setError] = useState<string | null>(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 };
}