From 18d03e4e78207e2941bdc53303149dc15e256925 Mon Sep 17 00:00:00 2001 From: Ida Dittrich Date: Thu, 21 Aug 2025 08:41:05 +0200 Subject: [PATCH] basic progress bar --- .../DashboardChatAreaLogItem.tsx | 29 +- .../DashboardChatAreaMessageItem.tsx | 41 +- .../DashboardChatAreaMessageList.tsx | 525 +++++++++++------- .../DashboardChatMessages.module.css | 8 + 4 files changed, 331 insertions(+), 272 deletions(-) diff --git a/src/components/Dashboard/DashboardChat/DashboardChatAreaLogItem.tsx b/src/components/Dashboard/DashboardChat/DashboardChatAreaLogItem.tsx index 3901bcc..d1c8589 100644 --- a/src/components/Dashboard/DashboardChat/DashboardChatAreaLogItem.tsx +++ b/src/components/Dashboard/DashboardChat/DashboardChatAreaLogItem.tsx @@ -12,14 +12,10 @@ const LogItem: React.FC = ({ log }) => { // Format timestamp with robust parsing (same logic as MessageList) const formatTimestamp = (timestamp: any) => { - console.log(`⏰ LogItem formatTimestamp called with:`, { - timestamp, - type: typeof timestamp, - hasValue: !!timestamp - }); + if (!timestamp) { - console.log(`⏰ LogItem: No timestamp provided`); + return 'No timestamp'; } @@ -62,13 +58,7 @@ const LogItem: React.FC = ({ log }) => { return `Invalid: ${timestamp}`; } - console.log(`✅ LogItem: Successfully parsed timestamp:`, { - original: timestamp, - originalType: typeof timestamp, - processed: dateToTry, - wasConverted: dateToTry !== timestamp, - parsed: date.toISOString() - }); + const now = new Date(); const isToday = date.toDateString() === now.toDateString(); @@ -81,7 +71,6 @@ const LogItem: React.FC = ({ log }) => { date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); } - console.log(`⏰ LogItem: Formatted timestamp:`, { formatted }); return formatted; }; @@ -89,17 +78,7 @@ const LogItem: React.FC = ({ log }) => { const logLevel = log.level || (log.type?.toLowerCase() as 'info' | 'warning' | 'error' | 'debug') || 'info'; // Debug: Log what the LogItem is receiving - console.log(`📋 LogItem rendering:`, { - logId: log.id, - logType: log.type, - logLevel: logLevel, - message: log.message?.substring(0, 50) + (log.message?.length > 50 ? '...' : ''), - timestamp: log.timestamp, - timestampType: typeof log.timestamp, - fullLogObject: log, - allLogKeys: Object.keys(log), - hasDetails: !!(log.details || log.performance) - }); + return (
diff --git a/src/components/Dashboard/DashboardChat/DashboardChatAreaMessageItem.tsx b/src/components/Dashboard/DashboardChat/DashboardChatAreaMessageItem.tsx index a5f8fd1..3517d0f 100644 --- a/src/components/Dashboard/DashboardChat/DashboardChatAreaMessageItem.tsx +++ b/src/components/Dashboard/DashboardChat/DashboardChatAreaMessageItem.tsx @@ -63,26 +63,8 @@ const getFileIcon = (type?: string, ext?: string): string => { const MessageItem: React.FC = ({ message, onFilePreview }) => { const { downloadFile, isDownloading } = useFileDownload(); - // Debug: Log what the MessageItem is receiving - console.log(`🎭 MessageItem rendering:`, { - messageId: message.id, - messageRole: message.role, - content: message.content?.substring(0, 50) + (message.content?.length > 50 ? '...' : ''), - contentLength: message.content?.length || 0, - hasContent: !!message.content, - hasDocuments: !!(message.documents), - documentsArray: message.documents, - documentsLength: message.documents?.length || 0, - documentsCheck: message.documents && message.documents.length > 0, - // Timestamp debugging - timestamp: message.timestamp, - hasTimestamp: !!message.timestamp, - timestampType: typeof message.timestamp, - - }); const handleDocumentClick = (document: Document) => { - console.log(`🖱️ Document clicked:`, document); // If there's a downloadUrl, use it; otherwise try the url const downloadLink = document.downloadUrl || document.url; @@ -95,7 +77,6 @@ const MessageItem: React.FC = ({ message, onFilePreview }) => const handlePreview = (document: Document, e: React.MouseEvent) => { e.stopPropagation(); - console.log(`👁️ Preview requested for:`, document); // Use fileId if available, otherwise try to use id as fallback const fileId = document.fileId || parseInt(document.id || '0'); @@ -105,7 +86,6 @@ const MessageItem: React.FC = ({ message, onFilePreview }) => return; } - console.log('✅ MessageItem - Previewing file:', { fileId, document }); // Call the parent callback to show preview in the file preview quadrant if (onFilePreview) { @@ -135,37 +115,22 @@ const MessageItem: React.FC = ({ message, onFilePreview }) => // Construct filename with extension if available const fileName = document.ext ? `${document.name}.${document.ext}` : document.name; - console.log(`💾 Downloading file ${fileId} as "${fileName}"`); await downloadFile(fileId, fileName); }; // Debug: Log document check before rendering const hasDocuments = message.documents && message.documents.length > 0; - console.log(`🔍 About to check documents:`, { - hasDocuments: !!(message.documents), - documentsLength: message.documents?.length || 0, - willRenderFiles: hasDocuments - }); - // Log if no documents - if (!hasDocuments) { - console.log(`📭 No documents to render for message ${message.id}`); - } // Format timestamp const formatTimestamp = (timestamp?: string) => { - console.log(`⏰ formatTimestamp called with:`, { timestamp, type: typeof timestamp, hasValue: !!timestamp }); - if (!timestamp) { - console.log(`⏰ No timestamp provided, returning empty string`); return ''; } - + const date = new Date(timestamp); - console.log(`⏰ Parsed date:`, { date, isValid: !isNaN(date.getTime()) }); - + if (isNaN(date.getTime())) { - console.log(`⏰ Invalid date, returning empty string`); return ''; } @@ -180,7 +145,6 @@ const MessageItem: React.FC = ({ message, onFilePreview }) => date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); } - console.log(`⏰ Formatted timestamp:`, { formatted }); return formatted; }; @@ -221,7 +185,6 @@ const MessageItem: React.FC = ({ message, onFilePreview }) =>
{message.documents!.map((document, docIndex) => { - console.log(`📄 Rendering document ${docIndex + 1}:`, document); return (
{ - // Get the first line of the message - const firstLine = content.split('\n')[0].trim(); +// Helper function to parse task and action progress from log messages +const parseLogProgress = (logMessage: string): { + taskNumber?: number; + totalTasks?: number; + actionNumber?: number; + totalActions?: number; + isCompleted?: boolean; + type: 'task_start' | 'action_start' | 'action_complete' | 'task_complete' | 'unknown'; +} => { + const message = logMessage.trim(); - // Look for patterns like "Starting Task 1/5", "Task 2/10 Completed Successfully!", etc. - const taskPattern = /(?:Starting\s+)?Task\s+(\d+)\/(\d+)/i; - const match = firstLine.match(taskPattern); - - if (match) { - const current = parseInt(match[1]); - const total = parseInt(match[2]); - const percentage = Math.round((current / total) * 100); - - console.log(`📊 Parsed task progress from first line:`, { - firstLine, - current, - total, - percentage, - originalText: match[0] - }); - return { current, total, percentage }; + // Pattern: "Executing task X/Y" + const taskStartPattern = /^Executing task (\d+)\/(\d+)$/i; + const taskStartMatch = message.match(taskStartPattern); + if (taskStartMatch) { + return { + taskNumber: parseInt(taskStartMatch[1]), + totalTasks: parseInt(taskStartMatch[2]), + type: 'task_start' + }; } - return null; + // Pattern: "Task X - Starting action Y/Z" + const actionStartPattern = /^Task (\d+) - Starting action (\d+)\/(\d+)$/i; + const actionStartMatch = message.match(actionStartPattern); + if (actionStartMatch) { + return { + taskNumber: parseInt(actionStartMatch[1]), + actionNumber: parseInt(actionStartMatch[2]), + totalActions: parseInt(actionStartMatch[3]), + type: 'action_start' + }; + } + + // Pattern: "Task X - Action Y/Z completed" or "✅ Task X - Action Y/Z completed" + const actionCompletePattern = /^(?:✅\s+)?Task (\d+) - Action (\d+)\/(\d+) completed$/i; + const actionCompleteMatch = message.match(actionCompletePattern); + if (actionCompleteMatch) { + return { + taskNumber: parseInt(actionCompleteMatch[1]), + actionNumber: parseInt(actionCompleteMatch[2]), + totalActions: parseInt(actionCompleteMatch[3]), + isCompleted: true, + type: 'action_complete' + }; + } + + // Pattern: "Task X/Y completed" or "🎯 Task X/Y completed" + const taskCompletePattern = /^(?:🎯\s+)?Task (\d+)\/(\d+) completed$/i; + const taskCompleteMatch = message.match(taskCompletePattern); + if (taskCompleteMatch) { + return { + taskNumber: parseInt(taskCompleteMatch[1]), + totalTasks: parseInt(taskCompleteMatch[2]), + isCompleted: true, + type: 'task_complete' + }; + } + + return { type: 'unknown' }; }; -// Helper function to analyze workflow progress from all messages -const analyzeWorkflowProgress = (messages: any[]): { +// Helper function to analyze workflow progress from messages and logs +const analyzeWorkflowProgressFromMessages = (messages: any[], logs: any[] = []): { current: number; total: number; percentage: number; isLoading: boolean; + taskBreakdown?: string; } | null => { - if (messages.length === 0) return null; + console.log('🚨 analyzeWorkflowProgressFromMessages called:', { + messagesLength: messages.length, + logsLength: logs.length, + logMessages: logs.map(l => l.message) + }); + + if (messages.length === 0 && logs.length === 0) return null; // Check if the last message is from user (indicating we're waiting for assistant response) const lastMessage = messages[messages.length - 1]; const isWaitingForAssistant = lastMessage?.role === 'user'; - let latestProgress: { current: number; total: number; percentage: number } | null = null; - - // Go through messages in reverse order (latest first) to find the most recent progress - for (let i = messages.length - 1; i >= 0; i--) { - const message = messages[i]; - if (message.role === 'assistant' && message.content) { - const progress = parseTaskProgress(message.content); - if (progress) { - latestProgress = progress; - break; // Use the most recent progress found - } - } - } - - console.log(`🔄 Analyzed workflow progress:`, { - totalMessages: messages.length, - lastMessageRole: lastMessage?.role, - isWaitingForAssistant, - latestProgress - }); - - // If we're waiting for assistant response after a user message, show loading state + // If we're waiting for assistant response, show loading state if (isWaitingForAssistant) { return { current: 0, @@ -76,11 +97,168 @@ const analyzeWorkflowProgress = (messages: any[]): { }; } - // If we have progress and we're not waiting, show the progress - if (latestProgress) { + // Parse logs first (they contain the structure info) + let totalTasks = 0; + const taskActionCounts: { [taskNumber: number]: { total: number; completedActions: Set } } = {}; + + // Parse logs for structure information + logs.forEach((log) => { + if (!log.message) return; + + const content = log.message.trim(); + + // Debug: log message content for analysis + if (content.includes('✅') || content.includes('🚀') || content.includes('⚡') || content.includes('🎯') || content.includes('Executing task') || content.includes('Starting action')) { + console.log('📊 Progress: Analyzing log content:', content); + } + + // Look for task execution patterns: "Executing task X/Y" + const taskExecMatch = content.match(/Executing task (\d+)\/(\d+)/i); + if (taskExecMatch) { + const totalTasksFound = parseInt(taskExecMatch[2]); + if (totalTasksFound > totalTasks) { + totalTasks = totalTasksFound; + } + console.log('📊 Found total tasks:', totalTasksFound); + } + + // Look for action start patterns: "Task X - Starting action Y/Z" + const actionStartMatch = content.match(/Task (\d+) - Starting action (\d+)\/(\d+)/i); + if (actionStartMatch) { + const taskNumber = parseInt(actionStartMatch[1]); + const totalActions = parseInt(actionStartMatch[3]); + if (!taskActionCounts[taskNumber]) { + taskActionCounts[taskNumber] = { total: 0, completedActions: new Set() }; + } + if (totalActions > taskActionCounts[taskNumber].total) { + taskActionCounts[taskNumber].total = totalActions; + } + console.log(`📊 Task ${taskNumber} has ${totalActions} actions`); + } + + // Look for action completion patterns: "✅ Task X - Action Y/Z completed" + const actionCompleteMatch = content.match(/✅\s+Task (\d+) - Action (\d+)\/(\d+) completed/i); + if (actionCompleteMatch) { + const taskNumber = parseInt(actionCompleteMatch[1]); + const actionNumber = parseInt(actionCompleteMatch[2]); + const totalActions = parseInt(actionCompleteMatch[3]); + + if (!taskActionCounts[taskNumber]) { + taskActionCounts[taskNumber] = { total: totalActions, completedActions: new Set() }; + } + // Add this specific action to the completed set + taskActionCounts[taskNumber].completedActions.add(actionNumber); + // Update total if needed + if (totalActions > taskActionCounts[taskNumber].total) { + taskActionCounts[taskNumber].total = totalActions; + } + console.log(`📊 Task ${taskNumber} action ${actionNumber} completed`); + } + + // Look for task completion patterns: "🎯 Task X/Y completed" + const taskCompleteMatch = content.match(/🎯\s+Task (\d+)\/(\d+) completed/i); + if (taskCompleteMatch) { + const taskNumber = parseInt(taskCompleteMatch[1]); + const totalTasksFound = parseInt(taskCompleteMatch[2]); + if (totalTasksFound > totalTasks) { + totalTasks = totalTasksFound; + } + // Mark all actions for this task as completed + if (taskActionCounts[taskNumber]) { + const task = taskActionCounts[taskNumber]; + for (let i = 1; i <= task.total; i++) { + task.completedActions.add(i); + } + } + console.log(`📊 Task ${taskNumber} completed (all actions)`); + } + }); + + // Also parse assistant messages for any additional patterns + messages.forEach((message) => { + if (message.role !== 'assistant' || !message.content) return; + + const content = message.content.trim(); + + // Look for action completion in messages: "✅ Task X - Action document.generateReport completed" + const actionCompleteMatch = content.match(/✅\s+Task (\d+) - Action\s+(?:(\d+)\/(\d+)|.*completed)/i); + if (actionCompleteMatch) { + const taskNumber = parseInt(actionCompleteMatch[1]); + + // If we have the action number format (e.g., "1/1") + if (actionCompleteMatch[2] && actionCompleteMatch[3]) { + const actionNumber = parseInt(actionCompleteMatch[2]); + const totalActions = parseInt(actionCompleteMatch[3]); + + if (!taskActionCounts[taskNumber]) { + taskActionCounts[taskNumber] = { total: totalActions, completedActions: new Set() }; + } + taskActionCounts[taskNumber].completedActions.add(actionNumber); + if (totalActions > taskActionCounts[taskNumber].total) { + taskActionCounts[taskNumber].total = totalActions; + } + } else { + // If it's a named action without numbers, treat it as action 1/1 for this task + if (!taskActionCounts[taskNumber]) { + taskActionCounts[taskNumber] = { total: 1, completedActions: new Set() }; + } + taskActionCounts[taskNumber].completedActions.add(1); + if (taskActionCounts[taskNumber].total === 0) { + taskActionCounts[taskNumber].total = 1; + } + } + console.log(`📊 Message: Task ${taskNumber} action completed`); + } + }); + + // Calculate completed tasks (a task is complete when all its actions are done) + let completedTasks = 0; + const taskStatuses = []; + + // Check each task's completion status + for (let taskNum = 1; taskNum <= totalTasks; taskNum++) { + const task = taskActionCounts[taskNum]; + if (task) { + const isTaskComplete = task.total > 0 && task.completedActions.size === task.total; + if (isTaskComplete) { + completedTasks++; + } + taskStatuses.push(`Task ${taskNum}: ${task.completedActions.size}/${task.total} actions`); + } else { + taskStatuses.push(`Task ${taskNum}: Not started`); + } + } + + console.log('📊 Final calculation (task-based):', { + totalTasks, + completedTasks, + taskActionCounts: Object.fromEntries( + Object.entries(taskActionCounts).map(([taskNum, task]) => [ + taskNum, + { + total: task.total, + completed: task.completedActions.size, + completedActions: Array.from(task.completedActions), + isComplete: task.total > 0 && task.completedActions.size === task.total + } + ]) + ) + }); + + // Create task breakdown string + const taskBreakdown = taskStatuses.length > 0 ? taskStatuses.join(', ') : undefined; + + // If we have total tasks, always use task-based progress + if (totalTasks > 0) { + const percentage = Math.round((completedTasks / totalTasks) * 100); + console.log(`📊 Task-based progress: ${completedTasks}/${totalTasks} tasks (${percentage}%)`); + return { - ...latestProgress, - isLoading: false + current: completedTasks, + total: totalTasks, + percentage: percentage, + isLoading: false, + taskBreakdown: taskBreakdown || `${completedTasks}/${totalTasks} tasks completed` }; } @@ -135,13 +313,6 @@ const safeParseDate = (timestamp: any, fallback: number = Date.now()): Date => { return new Date(fallback); } - console.log(`✅ Successfully parsed timestamp:`, { - original: timestamp, - originalType: typeof timestamp, - processed: dateToTry, - wasConverted: dateToTry !== timestamp, - parsed: date.toISOString() - }); return date; }; @@ -150,23 +321,12 @@ const safeParseDate = (timestamp: any, fallback: number = Date.now()): Date => { const mergeMessagesAndLogs = (messages: any[], logs: WorkflowLog[]): Array<{type: 'message' | 'log', item: any, timestamp: Date}> => { const combined: Array<{type: 'message' | 'log', item: any, timestamp: Date}> = []; - console.log(`🔄 Starting merge process:`, { - messagesCount: messages.length, - logsCount: logs.length, - firstMessage: messages[0], - firstLog: logs[0] - }); + // Add messages messages.forEach((message, index) => { const rawTimestamp = message.timestamp || message.publishedAt; - console.log(`📨 Processing message ${index + 1}/${messages.length}:`, { - messageId: message.id, - rawTimestamp, - timestampType: typeof rawTimestamp - }); - // Use current time minus index for fallback to maintain order const fallbackTime = Date.now() - ((messages.length + logs.length) - index) * 1000; const timestamp = safeParseDate(rawTimestamp, fallbackTime); @@ -180,12 +340,6 @@ const mergeMessagesAndLogs = (messages: any[], logs: WorkflowLog[]): Array<{type // Add logs logs.forEach((log, index) => { - console.log(`📋 Processing log ${index + 1}/${logs.length}:`, { - logId: log.id, - rawTimestamp: log.timestamp, - timestampType: typeof log.timestamp, - logObject: log - }); // Use current time minus index for fallback to maintain order const fallbackTime = Date.now() - ((logs.length) - index) * 1000; @@ -201,19 +355,6 @@ const mergeMessagesAndLogs = (messages: any[], logs: WorkflowLog[]): Array<{type // Sort by timestamp (chronological order) combined.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime()); - console.log(`🔄 Final merged timeline:`, { - totalMessages: messages.length, - totalLogs: logs.length, - totalCombined: combined.length, - sortedTimeline: combined.map((item, index) => ({ - index, - type: item.type, - timestamp: item.timestamp.toISOString(), - content: item.type === 'message' ? - item.item.content?.substring(0, 30) + '...' : - item.item.message?.substring(0, 30) + '...' - })) - }); return combined; }; @@ -241,7 +382,6 @@ const transformWorkflowMessage = async (workflowMessage: WorkflowMessage, reques })); } else if (workflowMessage.fileIds && workflowMessage.fileIds.length > 0) { // Fallback to legacy fileIds approach - console.log(`📎 Processing ${workflowMessage.fileIds.length} files for message ${workflowMessage.id}`); const documentPromises = workflowMessage.fileIds.map(async (fileId, fileIndex) => { try { @@ -296,40 +436,6 @@ const transformWorkflowMessage = async (workflowMessage: WorkflowMessage, reques documents: documents }; - console.log(`🔄 Transformation result for ${workflowMessage.id}:`, { - role: workflowMessage.role, - originalMessage: workflowMessage, - originalContent: workflowMessage.content, - originalContentType: typeof workflowMessage.content, - possibleContent: possibleContent, - possibleContentType: typeof possibleContent, - transformedContent: transformedMessage.content, - transformedContentType: typeof transformedMessage.content, - documentsCount: documents.length, - hasDocuments: documents.length > 0, - // Timestamp debugging - publishedAt: workflowMessage.publishedAt, - timestamp: workflowMessage.timestamp, - finalTimestamp: transformedMessage.timestamp, - hasTimestamp: !!transformedMessage.timestamp, - allOriginalKeys: Object.keys(workflowMessage), - // Check for alternative field names - alternativeFields: { - message: (workflowMessage as any).message, - text: (workflowMessage as any).text, - body: (workflowMessage as any).body, - data: (workflowMessage as any).data - } - }); - - // Special logging for user messages with documents - if (workflowMessage.role === 'user' && documents.length > 0) { - console.log(`👤📎 USER message with ${documents.length} documents:`, { - messageId: workflowMessage.id, - documents: documents.map(doc => ({ name: doc.name, fileId: doc.fileId, type: doc.type })) - }); - } - return transformedMessage; }; @@ -337,17 +443,36 @@ const MessageList: React.FC = ({ workflowState, onFilePreview }) => { + + const { request } = useApiRequest(); const [transformedMessages, setTransformedMessages] = React.useState([]); const [isTransforming, setIsTransforming] = React.useState(false); const scrollContainerRef = React.useRef(null); const [isUserScrolledUp, setIsUserScrolledUp] = React.useState(false); const lastMessageCountRef = React.useRef(0); - - // Analyze workflow progress from all messages - const workflowProgress = React.useMemo(() => { - return analyzeWorkflowProgress(transformedMessages); - }, [transformedMessages]); + const [workflowProgress, setWorkflowProgress] = React.useState<{ + current: number; + total: number; + percentage: number; + isLoading: boolean; + taskBreakdown?: string; + } | null>(null); + + // Calculate workflow progress when messages or logs change + React.useEffect(() => { + console.log('🔄 Progress calculation triggered:', { + messagesCount: transformedMessages.length, + logsCount: workflowState.logs?.length || 0, + logs: workflowState.logs?.map(l => l.message) || [] + }); + + const progress = analyzeWorkflowProgressFromMessages(transformedMessages, workflowState.logs || []); + + console.log('📊 Progress result:', progress); + + setWorkflowProgress(progress); + }, [transformedMessages.length, JSON.stringify(transformedMessages.map(m => m.content)), workflowState.logs?.length, JSON.stringify(workflowState.logs?.map(l => l.message))]); // Create merged timeline of messages and logs const timeline = React.useMemo(() => { @@ -363,30 +488,14 @@ const MessageList: React.FC = ({ } setIsTransforming(true); - console.log(`🔄 Transforming ${workflowState.messages.length} workflow messages...`); try { const transformed = await Promise.all( - workflowState.messages.map(async (msg: WorkflowMessage, index: number) => { - console.log(`🔄 Transforming message ${index + 1}/${workflowState.messages.length}: ${msg.id}`); - const content = msg.message || msg.content || ''; - console.log(`📝 RAW API Message (${msg.role}):`, { - id: msg.id, - rawMessage: msg, - contentType: typeof content, - contentValue: content, - contentLength: content.length, - hasContent: !!content, - contentPreview: content.substring(0, 100) + (content.length > 100 ? '...' : ''), - fileCount: msg.fileIds?.length || 0, - allKeys: Object.keys(msg) - }); + workflowState.messages.map(async (msg: WorkflowMessage) => { return await transformWorkflowMessage(msg, request); }) ); - - console.log(`✅ Successfully transformed ${transformed.length} messages`); setTransformedMessages(transformed); } catch (error) { console.error('❌ Error transforming messages:', error); @@ -411,21 +520,12 @@ const MessageList: React.FC = ({ const isNearBottom = distanceFromBottom < 100; setIsUserScrolledUp(!isNearBottom); - console.log('📏 Scroll position:', { - scrollTop, - scrollHeight, - clientHeight, - distanceFromBottom, - isNearBottom, - isUserScrolledUp: !isNearBottom - }); }, []); // Scroll to bottom function const scrollToBottom = React.useCallback(() => { const container = scrollContainerRef.current; if (container) { - console.log('⬇️ Auto-scrolling to bottom'); container.scrollTop = container.scrollHeight; } }, []); @@ -437,7 +537,6 @@ const MessageList: React.FC = ({ const hasNewItems = currentTimelineCount > lastMessageCountRef.current; if (hasNewItems && hadItems && !isUserScrolledUp) { - console.log('🆕 New timeline items detected, auto-scrolling to bottom'); // Small delay to ensure DOM is updated setTimeout(scrollToBottom, 100); } @@ -448,7 +547,6 @@ const MessageList: React.FC = ({ // Scroll to bottom on initial load React.useEffect(() => { if (timeline.length > 0 && lastMessageCountRef.current === 0) { - console.log('📜 Initial load, scrolling to bottom'); setTimeout(scrollToBottom, 100); } }, [timeline.length, scrollToBottom]); @@ -457,44 +555,33 @@ const MessageList: React.FC = ({ return (
-
+ onScroll={checkScrollPosition} + > {error && (
Error: {error}
)} - +
{timeline.map((timelineItem, index) => { if (timelineItem.type === 'message') { const message = timelineItem.item; - console.log(`🎨 Rendering timeline message ${message.id}:`, { - role: message.role, - contentLength: message.content?.length || 0, - hasContent: !!message.content, - documentsCount: message.documents?.length || 0 - }); - - return ( - - ); + message={message} + index={index} + onFilePreview={onFilePreview} + /> + ); } else if (timelineItem.type === 'log') { const log = timelineItem.item; - console.log(`📋 Rendering timeline log ${log.id}:`, { - logLevel: log.level || log.type, - message: log.message?.substring(0, 50), - timestamp: log.timestamp - }); return ( = ({ {(isLoading || isTransforming) && (
- {isTransforming ? 'Processing messages...' : 'Loading messages...'} + {isTransforming ? 'Processing messages...' : 'Loading messages...'}
)} @@ -526,43 +613,65 @@ const MessageList: React.FC = ({
)} -
- +
+ {/* Workflow Progress Bar - positioned outside scrollable area */} - {workflowProgress && ( + {(workflowProgress || workflowState.logs?.length > 0) && (
-
- Workflow Progress - - {workflowProgress.isLoading - ? 'Loading tasks...' - : `${workflowProgress.current}/${workflowProgress.total} Tasks (${workflowProgress.percentage}%)` - } - -
-
-
-
+ {workflowProgress ? ( + <> +
+ Workflow Progress + + {workflowProgress.isLoading + ? 'Loading tasks...' + : `${workflowProgress.current}/${workflowProgress.total} Actions (${workflowProgress.percentage}%)` + } + +
+ {workflowProgress.taskBreakdown && !workflowProgress.isLoading && ( +
+ {workflowProgress.taskBreakdown} +
+ )} +
+
+
+ + ) : ( + <> +
+ Workflow Progress + Analyzing workflow... ({workflowState.logs?.length || 0} logs) +
+
+
+
+ + )}
)} {/* Scroll to bottom button - positioned relative to message list container */} - + onClick={scrollToBottom} + title="Scroll to bottom" + > + ⬇️ +
); }; diff --git a/src/components/Dashboard/DashboardChat/DashboardChatAreaStyles/DashboardChatMessages.module.css b/src/components/Dashboard/DashboardChat/DashboardChatAreaStyles/DashboardChatMessages.module.css index 44e55b1..78970cd 100644 --- a/src/components/Dashboard/DashboardChat/DashboardChatAreaStyles/DashboardChatMessages.module.css +++ b/src/components/Dashboard/DashboardChat/DashboardChatAreaStyles/DashboardChatMessages.module.css @@ -372,6 +372,14 @@ font-weight: 600; } +.workflow_progress_breakdown { + font-size: 10px; + color: var(--color-gray); + margin-bottom: 6px; + text-align: center; + opacity: 0.8; +} + .workflow_progress_bar { width: 100%; height: 8px;