334 lines
No EOL
8.8 KiB
TypeScript
334 lines
No EOL
8.8 KiB
TypeScript
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
import { useApiRequest } from './useApi';
|
|
|
|
// Workflow interfaces
|
|
export interface Workflow {
|
|
id: string;
|
|
name?: string;
|
|
status: string;
|
|
startedAt?: string;
|
|
lastActivity?: string;
|
|
currentRound?: number;
|
|
dataStats?: Record<string, any>;
|
|
userId?: number;
|
|
messageIds?: string[];
|
|
}
|
|
|
|
export interface WorkflowMessage {
|
|
id: string;
|
|
content: string;
|
|
role: 'user' | 'assistant' | 'system';
|
|
timestamp?: string;
|
|
sequenceNo?: number;
|
|
fileIds?: number[];
|
|
}
|
|
|
|
export interface StartWorkflowRequest {
|
|
prompt: string;
|
|
listFileId: number[];
|
|
}
|
|
|
|
export interface StartWorkflowResponse {
|
|
id: string;
|
|
status: string;
|
|
message: string;
|
|
}
|
|
|
|
// Workflows list hook
|
|
export function useWorkflows() {
|
|
const [workflows, setWorkflows] = useState<Workflow[]>([]);
|
|
const { request, isLoading: loading, error } = useApiRequest<null, Workflow[]>();
|
|
|
|
const fetchWorkflows = async () => {
|
|
try {
|
|
const data = await request({
|
|
url: '/api/workflows',
|
|
method: 'get'
|
|
});
|
|
|
|
setWorkflows(data);
|
|
} catch (error) {
|
|
// Error is already handled by useApiRequest
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchWorkflows();
|
|
}, []);
|
|
|
|
return { workflows, loading, error, refetch: fetchWorkflows };
|
|
}
|
|
|
|
// Workflow operations hook
|
|
export function useWorkflowOperations() {
|
|
const [startingWorkflow, setStartingWorkflow] = useState(false);
|
|
const [stoppingWorkflows, setStoppingWorkflows] = useState<Set<string>>(new Set());
|
|
const [deletingWorkflows, setDeletingWorkflows] = useState<Set<string>>(new Set());
|
|
const { request, error: apiError, isLoading } = useApiRequest();
|
|
const [startError, setStartError] = useState<string | null>(null);
|
|
const [stopError, setStopError] = useState<string | null>(null);
|
|
const [deleteError, setDeleteError] = useState<string | null>(null);
|
|
|
|
const startWorkflow = async (workflowData: StartWorkflowRequest, workflowId?: string) => {
|
|
setStartError(null);
|
|
setStartingWorkflow(true);
|
|
|
|
try {
|
|
const response = await request({
|
|
url: '/api/workflows/start',
|
|
method: 'post',
|
|
data: workflowData,
|
|
params: workflowId ? { workflowId } : undefined
|
|
}) as StartWorkflowResponse;
|
|
|
|
return { success: true, data: response };
|
|
} catch (error: any) {
|
|
setStartError(error.message);
|
|
return { success: false, error: error.message };
|
|
} finally {
|
|
setStartingWorkflow(false);
|
|
}
|
|
};
|
|
|
|
const stopWorkflow = async (workflowId: string) => {
|
|
setStopError(null);
|
|
setStoppingWorkflows(prev => new Set(prev).add(workflowId));
|
|
|
|
try {
|
|
await request({
|
|
url: `/api/workflows/${workflowId}/stop`,
|
|
method: 'post'
|
|
});
|
|
|
|
return true;
|
|
} catch (error: any) {
|
|
setStopError(error.message);
|
|
return false;
|
|
} finally {
|
|
setStoppingWorkflows(prev => {
|
|
const newSet = new Set(prev);
|
|
newSet.delete(workflowId);
|
|
return newSet;
|
|
});
|
|
}
|
|
};
|
|
|
|
const deleteWorkflow = async (workflowId: string) => {
|
|
setDeleteError(null);
|
|
setDeletingWorkflows(prev => new Set(prev).add(workflowId));
|
|
|
|
try {
|
|
await request({
|
|
url: `/api/workflows/${workflowId}`,
|
|
method: 'delete'
|
|
});
|
|
|
|
return true;
|
|
} catch (error: any) {
|
|
setDeleteError(error.message);
|
|
return false;
|
|
} finally {
|
|
setDeletingWorkflows(prev => {
|
|
const newSet = new Set(prev);
|
|
newSet.delete(workflowId);
|
|
return newSet;
|
|
});
|
|
}
|
|
};
|
|
|
|
return {
|
|
startingWorkflow,
|
|
stoppingWorkflows,
|
|
deletingWorkflows,
|
|
startError,
|
|
stopError,
|
|
deleteError,
|
|
startWorkflow,
|
|
stopWorkflow,
|
|
deleteWorkflow,
|
|
isLoading
|
|
};
|
|
}
|
|
|
|
// Workflow status hook
|
|
export function useWorkflowStatus(workflowId: string | null) {
|
|
const [status, setStatus] = useState<Workflow | null>(null);
|
|
const { request, isLoading: loading, error } = useApiRequest<null, Workflow>();
|
|
|
|
const fetchStatus = async () => {
|
|
if (!workflowId) {
|
|
// Clear status when no workflow is selected
|
|
setStatus(null);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const data = await request({
|
|
url: `/api/workflows/${workflowId}/status`,
|
|
method: 'get'
|
|
});
|
|
|
|
setStatus(data);
|
|
} catch (error) {
|
|
// Error is already handled by useApiRequest
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchStatus();
|
|
}, [workflowId]);
|
|
|
|
return { status, loading, error, refetch: fetchStatus };
|
|
}
|
|
|
|
// Workflow messages hook
|
|
export function useWorkflowMessages(workflowId: string | null, messageId?: string) {
|
|
const [messages, setMessages] = useState<WorkflowMessage[]>([]);
|
|
const { request, isLoading: loading, error } = useApiRequest<null, WorkflowMessage[]>();
|
|
|
|
const fetchMessages = async () => {
|
|
if (!workflowId) {
|
|
// Clear messages when no workflow is selected
|
|
setMessages([]);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const data = await request({
|
|
url: `/api/workflows/${workflowId}/messages`,
|
|
method: 'get',
|
|
params: messageId ? { messageId } : undefined
|
|
});
|
|
|
|
setMessages(data);
|
|
} catch (error) {
|
|
// Error is already handled by useApiRequest
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchMessages();
|
|
}, [workflowId, messageId]);
|
|
|
|
return { messages, loading, error, refetch: fetchMessages };
|
|
}
|
|
|
|
// Workflow logs hook
|
|
export function useWorkflowLogs(workflowId: string | null, logId?: string) {
|
|
const [logs, setLogs] = useState<any[]>([]);
|
|
const { request, isLoading: loading, error } = useApiRequest<null, any[]>();
|
|
|
|
const fetchLogs = async () => {
|
|
if (!workflowId) return;
|
|
|
|
try {
|
|
const data = await request({
|
|
url: `/api/workflows/${workflowId}/logs`,
|
|
method: 'get',
|
|
params: logId ? { logId } : undefined
|
|
});
|
|
|
|
setLogs(data);
|
|
} catch (error) {
|
|
// Error is already handled by useApiRequest
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchLogs();
|
|
}, [workflowId, logId]);
|
|
|
|
return { logs, loading, error, refetch: fetchLogs };
|
|
}
|
|
|
|
// File preview hook
|
|
export function useFilePreview() {
|
|
const [previewContent, setPreviewContent] = useState<string | null>(null);
|
|
const [fileMetadata, setFileMetadata] = useState<any>(null);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const { request } = useApiRequest();
|
|
|
|
const fetchPreview = async (fileId: string | number) => {
|
|
console.log('fetchPreview called with fileId:', fileId, 'type:', typeof fileId);
|
|
|
|
if (!fileId) {
|
|
setError("File ID not available");
|
|
return;
|
|
}
|
|
|
|
setIsLoading(true);
|
|
setError(null);
|
|
setPreviewContent(null);
|
|
setFileMetadata(null);
|
|
|
|
try {
|
|
// Convert fileId to number since backend expects integer
|
|
let numericFileId: number;
|
|
|
|
if (typeof fileId === 'number') {
|
|
numericFileId = fileId;
|
|
} else {
|
|
numericFileId = parseInt(String(fileId), 10);
|
|
}
|
|
|
|
console.log('Parsed fileId:', numericFileId, 'isNaN:', isNaN(numericFileId));
|
|
|
|
if (isNaN(numericFileId)) {
|
|
throw new Error(`Invalid file ID format: "${fileId}" (type: ${typeof fileId}). Expected a numeric file ID, but got a document UUID. Make sure the document object has a 'fileId' property with the numeric file ID.`);
|
|
}
|
|
|
|
console.log('Making API request to:', `/api/workflows/files/${numericFileId}/preview`);
|
|
|
|
const response = await request({
|
|
url: `/api/workflows/files/${numericFileId}/preview`,
|
|
method: 'get'
|
|
});
|
|
|
|
console.log('API response:', response);
|
|
|
|
// Handle response as object with metadata and preview content
|
|
if (typeof response === 'object' && response !== null) {
|
|
setFileMetadata(response);
|
|
// Try different possible property names for the content
|
|
const content = response.preview || response.content || response.data || response.previewContent;
|
|
|
|
// Debug for PDF issues only
|
|
if (response.mimeType === 'application/pdf') {
|
|
console.log('PDF Preview Debug:', {
|
|
hasPreview: !!response.preview,
|
|
previewLength: response.preview?.length,
|
|
hasBase64Flag: response.base64Encoded,
|
|
mimeType: response.mimeType
|
|
});
|
|
}
|
|
|
|
setPreviewContent(content || null);
|
|
} else {
|
|
// Fallback if response is just the content
|
|
setPreviewContent(response);
|
|
}
|
|
} catch (err: any) {
|
|
console.error('File preview error:', err);
|
|
setError(err.message || "Failed to load preview");
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
const clearPreview = () => {
|
|
setPreviewContent(null);
|
|
setFileMetadata(null);
|
|
setError(null);
|
|
setIsLoading(false);
|
|
};
|
|
|
|
return {
|
|
previewContent,
|
|
fileMetadata,
|
|
isLoading,
|
|
error,
|
|
fetchPreview,
|
|
clearPreview
|
|
};
|
|
}
|