import { ApiRequestOptions } from '../hooks/useApi'; // ============================================================================ // TYPES & INTERFACES // ============================================================================ // Workflow interfaces export interface Workflow { id: string; mandateId: string; status: string; name?: string; workflowMode?: string; [key: string]: any; // Allow additional properties } export interface WorkflowMessage { [key: string]: any; } export interface WorkflowStats { [key: string]: any; } export interface WorkflowDocument { [key: string]: any; } export interface WorkflowLog { [key: string]: any; } // Request/Response interfaces based on API documentation export interface FileAttachment { id: string; name: string; } export interface UserInputRequest { input: string; files?: FileAttachment[]; // optional file attachments - array of {id, name} objects metadata?: Record; // optional metadata } export interface StartWorkflowRequest { prompt: string; listFileId?: string[]; // Array of file ID strings (files must be uploaded first via /api/files/upload) userLanguage?: string; // Optional, defaults to "en" metadata?: Record; } export interface StartWorkflowResponse extends Workflow { // Workflow object returned from start endpoint } export interface ChatDataResponse { messages: WorkflowMessage[]; logs: WorkflowLog[]; stats: WorkflowStats[]; documents: WorkflowDocument[]; } // Type for the request function passed to API functions export type ApiRequestFunction = (options: ApiRequestOptions) => Promise; // ============================================================================ // API REQUEST FUNCTIONS // ============================================================================ /** * Fetch all workflows for the current user * Endpoint: GET /api/workflows/ */ export async function fetchWorkflows(request: ApiRequestFunction): Promise { console.log('📤 fetchWorkflows: Making API request to /api/workflows/'); try { const data = await request({ url: '/api/workflows/', method: 'get' }); console.log('đŸ“Ĩ fetchWorkflows: API response:', data); // Handle different response formats let workflows: Workflow[] = []; if (Array.isArray(data)) { // Direct array response workflows = data; } else if (data && typeof data === 'object') { // Check for common wrapper properties if (Array.isArray(data.workflows)) { workflows = data.workflows; } else if (Array.isArray(data.data)) { workflows = data.data; } else if (Array.isArray(data.items)) { workflows = data.items; } else if (Array.isArray(data.results)) { workflows = data.results; } else { // Try to find any array property const keys = Object.keys(data); for (const key of keys) { if (Array.isArray(data[key])) { workflows = data[key]; console.log(`â„šī¸ fetchWorkflows: Found workflows array in property '${key}'`); break; } } } } // Validate that we have workflow objects with id property const validWorkflows = workflows.filter((w: any) => w && typeof w === 'object' && w.id); if (validWorkflows.length !== workflows.length) { console.warn(`âš ī¸ fetchWorkflows: Filtered out ${workflows.length - validWorkflows.length} invalid workflows`); } console.log(`✅ fetchWorkflows: Returning ${validWorkflows.length} valid workflows`); return validWorkflows; } catch (error) { console.error('❌ fetchWorkflows: Error fetching workflows:', error); throw error; } } /** * Fetch a single workflow by ID * Endpoint: GET /api/workflows/{workflowId} */ export async function fetchWorkflow( request: ApiRequestFunction, workflowId: string ): Promise { return await request({ url: `/api/workflows/${workflowId}`, method: 'get' }); } /** * Fetch workflow status (lightweight status check) * Endpoint: GET /api/workflows/{workflowId}/status */ export async function fetchWorkflowStatus( request: ApiRequestFunction, workflowId: string ): Promise { const data = await request({ url: `/api/workflows/${workflowId}/status`, method: 'get' }); if (data && typeof data === 'object') { if (data.status) { return { status: data.status }; } return data; } return null; } /** * Fetch workflow messages * Endpoint: GET /api/workflows/{workflowId}/messages * Query params: messageId (optional) - fetch only newer messages */ export async function fetchWorkflowMessages( request: ApiRequestFunction, workflowId: string, messageId?: string ): Promise { const params = messageId ? { messageId } : undefined; const data = await request({ url: `/api/workflows/${workflowId}/messages`, method: 'get', params }); if (Array.isArray(data)) { return data; } if (data && typeof data === 'object') { if (Array.isArray(data.messages)) { return data.messages; } if (Array.isArray(data.data)) { return data.data; } } return []; } /** * Fetch workflow logs * Endpoint: GET /api/workflows/{workflowId}/logs * Query params: logId (optional) - fetch only newer logs */ export async function fetchWorkflowLogs( request: ApiRequestFunction, workflowId: string, logId?: string ): Promise { const params = logId ? { logId } : undefined; const data = await request({ url: `/api/workflows/${workflowId}/logs`, method: 'get', params }); if (Array.isArray(data)) { return data; } if (data && typeof data === 'object') { if (Array.isArray(data.logs)) { return data.logs; } if (Array.isArray(data.data)) { return data.data; } } return []; } /** * Fetch unified chat data (messages, logs, stats, documents) * Endpoint: GET /api/chat/playground/{workflowId}/chatData * Query params: afterTimestamp (optional) - fetch only data created after this time */ export async function fetchChatData( request: ApiRequestFunction, workflowId: string, afterTimestamp?: number ): Promise { const params = afterTimestamp ? { afterTimestamp: afterTimestamp.toString() } : undefined; const requestConfig = { url: `/api/chat/playground/${workflowId}/chatData`, method: 'get' as const, params }; console.log('📤 fetchChatData request:', requestConfig); const data = await request(requestConfig); console.log('đŸ“Ĩ fetchChatData response:', data); // Handle unified items format: { items: [{ type: 'message'|'log'|'stat', item: {...}, createdAt: ... }] } if (data.items && Array.isArray(data.items)) { const messages: WorkflowMessage[] = []; const logs: WorkflowLog[] = []; const stats: WorkflowStats[] = []; const documents: WorkflowDocument[] = []; data.items.forEach((item: any) => { if (item.type === 'message') { // Handle both formats: item.item or direct item data const messageData = item.item || item; if (messageData && (messageData.id || messageData.message)) { messages.push(messageData); } else { console.warn('âš ī¸ Invalid message item:', item); } } else if (item.type === 'log') { const logData = item.item || item; if (logData) { logs.push(logData); } } else if (item.type === 'stat') { const statData = item.item || item; if (statData) { stats.push(statData); } } // Documents might be in items or separate if (item.type === 'document') { const docData = item.item || item; if (docData) { documents.push(docData); } } }); console.log('đŸ“Ļ Extracted from items:', { messages: messages.length, logs: logs.length, stats: stats.length, documents: documents.length }); return { messages, logs, stats, documents: documents.length > 0 ? documents : (Array.isArray(data.documents) ? data.documents : []) }; } // Fallback to direct format: { messages: [], logs: [], stats: [] } return { messages: Array.isArray(data.messages) ? data.messages : [], logs: Array.isArray(data.logs) ? data.logs : [], stats: Array.isArray(data.stats) ? data.stats : [], documents: Array.isArray(data.documents) ? data.documents : [] }; } /** * Start a new workflow or continue an existing one * Endpoint: POST /api/chat/playground/start * Query params: workflowId (optional), workflowMode (default: "Actionplan") */ export async function startWorkflowApi( request: ApiRequestFunction, workflowData: StartWorkflowRequest, options?: { workflowId?: string; workflowMode?: 'Dynamic' | 'Automation' } ): Promise { const params: Record = {}; // workflowMode is REQUIRED according to API spec if (options?.workflowMode) { params.workflowMode = options.workflowMode; } else { // Default to 'Dynamic' if not provided (though it should always be provided) params.workflowMode = 'Dynamic'; } if (options?.workflowId) { params.workflowId = options.workflowId; } // Request body uses 'prompt' field (not 'input') according to API spec const requestBody: any = { prompt: workflowData.prompt, ...(workflowData.listFileId && workflowData.listFileId.length > 0 && { listFileId: workflowData.listFileId }), ...(workflowData.userLanguage && { userLanguage: workflowData.userLanguage }), ...(workflowData.metadata && { metadata: workflowData.metadata }) }; const requestConfig = { url: '/api/chat/playground/start', method: 'post' as const, data: requestBody, params: params // Always include workflowMode }; // Log full request details console.log('📤 Full startWorkflow request details:'); console.log(' URL:', requestConfig.url); console.log(' Method:', requestConfig.method); console.log(' Query Parameters:', params); console.log(' Request Body:', JSON.stringify(requestBody, null, 2)); console.log(' Full Request Config:', JSON.stringify(requestConfig, null, 2)); const response = await request(requestConfig); console.log('đŸ“Ĩ startWorkflow response:', response); return response; } /** * Stop a running workflow * Endpoint: POST /api/chat/playground/{workflowId}/stop */ export async function stopWorkflowApi( request: ApiRequestFunction, workflowId: string ): Promise { await request({ url: `/api/chat/playground/${workflowId}/stop`, method: 'post' }); } /** * Update workflow properties * Endpoint: PUT /api/workflows/{workflowId} */ export async function updateWorkflowApi( request: ApiRequestFunction, workflowId: string, updateData: Partial<{ name: string; description?: string; tags?: string[] }> ): Promise { return await request({ url: `/api/workflows/${workflowId}`, method: 'put', data: updateData }); } /** * Delete a workflow and all associated data * Endpoint: DELETE /api/workflows/{workflowId} */ export async function deleteWorkflowApi( request: ApiRequestFunction, workflowId: string ): Promise { await request({ url: `/api/workflows/${workflowId}`, method: 'delete' }); } /** * Delete multiple workflows */ export async function deleteWorkflowsApi( request: ApiRequestFunction, workflowIds: string[] ): Promise { // Delete workflows one by one since there's no bulk delete endpoint const deletePromises = workflowIds.map(workflowId => request({ url: `/api/workflows/${workflowId}`, method: 'delete' }).catch(error => { console.error(`Failed to delete workflow ${workflowId}:`, error); throw error; }) ); await Promise.all(deletePromises); } /** * Delete a message from a workflow * Endpoint: DELETE /api/workflows/{workflowId}/messages/{messageId} */ export async function deleteMessageApi( request: ApiRequestFunction, workflowId: string, messageId: string ): Promise { await request({ url: `/api/workflows/${workflowId}/messages/${messageId}`, method: 'delete' }); } /** * Delete a file reference from a message * Endpoint: DELETE /api/workflows/{workflowId}/messages/{messageId}/files/{fileId} */ export async function deleteFileFromMessageApi( request: ApiRequestFunction, workflowId: string, messageId: string, fileId: string ): Promise { await request({ url: `/api/workflows/${workflowId}/messages/${messageId}/files/${fileId}`, method: 'delete' }); } /** * Fetch attributes for a workflow type * Endpoint: GET /api/attributes/{entityType} */ export interface AttributeDefinition { name: string; type: 'text' | 'email' | 'date' | 'checkbox' | 'select' | 'multiselect' | 'number' | 'textarea'; label: string; description?: string; required?: boolean; default?: any; options?: Array<{ value: string | number; label: string | { [key: string]: string } }> | string; validation?: any; ui?: any; readonly?: boolean; editable?: boolean; visible?: boolean; order?: number; placeholder?: string; sortable?: boolean; filterable?: boolean; searchable?: boolean; width?: number; minWidth?: number; maxWidth?: number; filterOptions?: string[]; } export async function fetchAttributes( request: ApiRequestFunction, entityType: string = 'ChatWorkflow' ): Promise { const data = await request({ url: `/api/attributes/${entityType}`, method: 'get' }); // Extract attributes from response - check if response.data.attributes exists, otherwise check if response.data is an array let attrs: AttributeDefinition[] = []; if (data?.attributes && Array.isArray(data.attributes)) { attrs = data.attributes; } else if (Array.isArray(data)) { attrs = data; } else if (data && typeof data === 'object') { // Try to find any array property in the response const keys = Object.keys(data); for (const key of keys) { if (Array.isArray(data[key])) { attrs = data[key]; break; } } } return attrs; }