From 22d582f72b7c4a3ebb17ea333bc13539d5401471 Mon Sep 17 00:00:00 2001 From: Ida Dittrich Date: Wed, 20 Aug 2025 07:44:55 +0200 Subject: [PATCH] added page activator and cacher --- src/App.tsx | 17 +- .../Dashboard/DashboardChat/DashboardChat.tsx | 3 - .../DashboardChat/DashboardChatArea.tsx | 216 ++--- .../DashboardChatAreaConnectedFiles.tsx | 1 - .../DashboardChatAreaFilePreview.tsx | 2 +- .../DashboardChat/DashboardChatAreaInput.tsx | 372 ++++---- .../DashboardChatAreaMessageItem.tsx | 12 +- .../DashboardChatAreaMessageList.tsx | 547 +++++++----- .../DashboardChat.module.css | 265 +++++- .../DashboardChatArea.module.css | 843 ------------------ .../DashboardChatAreaInput.module.css | 137 +++ .../DashboardChatAreaStyles/grid.css | 68 -- .../Dashboard/DashboardChat/README.md | 109 --- .../DashboardChat/useWorkflowManager.ts | 194 ++++ .../DashboardLog/DashboardLog.module.css | 338 ------- .../Dashboard/DashboardLog/DashboardLog.tsx | 215 ----- .../DashboardPrompt.module.css | 157 ---- .../DashboardPrompt/DashboardPrompt.tsx | 100 --- .../DashboardPromptSet.module.css | 127 --- .../DashboardPromptSet/DashboardPromptSet.tsx | 96 -- .../DashboardPromptSetItem.module.css | 182 ---- .../DashboardPromptSetItem.tsx | 155 ---- .../DashboardPromptSetModal.module.css | 192 ---- .../DashboardPromptSetModal.tsx | 129 --- .../PromptShareModal.module.css | 315 ------- .../DashboardPromptSet/PromptShareModal.tsx | 249 ------ .../DashboardPromptSettings.module.css | 39 - .../DashboardPromptSettings.tsx | 18 - .../FormGenerator/FormGenerator.module.css | 18 +- .../PageManager/PageManager.module.css | 30 + src/components/PageManager/PageManager.tsx | 192 ++++ src/components/PageManager/index.ts | 2 + .../PageManager/pageConfigInterface.ts | 40 + src/components/PageManager/pageConfigs.ts | 211 +++++ .../PageManager}/pages.module.css | 19 +- src/components/Sidebar/Sidebar.tsx | 4 +- src/components/Sidebar/SidebarItem.tsx | 49 +- .../SidebarStyles/SidebarItem.module.css | 44 + src/components/Sidebar/sidebarTypes.ts | 1 + src/contexts/SidebarData.tsx | 65 -- src/hooks/useSidebar.ts | 39 + src/hooks/useWorkflows.ts | 80 +- src/locales/de.ts | 22 +- src/locales/en.ts | 17 + src/locales/fr.ts | 17 + src/pages/Home/Connections.tsx | 2 +- src/pages/Home/Dashboard.tsx | 91 +- src/pages/Home/Dateien.tsx | 2 +- src/pages/Home/Einstellungen.tsx | 2 +- src/pages/Home/Home.tsx | 40 +- .../Home/HomeStyles/Dashboard.module.css | 26 +- src/pages/Home/HomeStyles/Home.module.css | 25 + src/pages/Home/TeamBereich.tsx | 2 +- src/pages/Home/TestSharepoint.tsx | 2 +- src/pages/Home/Workflows.tsx | 2 +- 55 files changed, 2094 insertions(+), 4048 deletions(-) delete mode 100644 src/components/Dashboard/DashboardChat/DashboardChatAreaStyles/DashboardChatArea.module.css create mode 100644 src/components/Dashboard/DashboardChat/DashboardChatAreaStyles/DashboardChatAreaInput.module.css delete mode 100644 src/components/Dashboard/DashboardChat/DashboardChatAreaStyles/grid.css delete mode 100644 src/components/Dashboard/DashboardChat/README.md create mode 100644 src/components/Dashboard/DashboardChat/useWorkflowManager.ts delete mode 100644 src/components/Dashboard/DashboardLog/DashboardLog.module.css delete mode 100644 src/components/Dashboard/DashboardLog/DashboardLog.tsx delete mode 100644 src/components/Dashboard/DashboardPrompt/DashboardPrompt.module.css delete mode 100644 src/components/Dashboard/DashboardPrompt/DashboardPrompt.tsx delete mode 100644 src/components/Dashboard/DashboardPrompt/DashboardPromptSet/DashboardPromptSet.module.css delete mode 100644 src/components/Dashboard/DashboardPrompt/DashboardPromptSet/DashboardPromptSet.tsx delete mode 100644 src/components/Dashboard/DashboardPrompt/DashboardPromptSet/DashboardPromptSetItem.module.css delete mode 100644 src/components/Dashboard/DashboardPrompt/DashboardPromptSet/DashboardPromptSetItem.tsx delete mode 100644 src/components/Dashboard/DashboardPrompt/DashboardPromptSet/DashboardPromptSetModal.module.css delete mode 100644 src/components/Dashboard/DashboardPrompt/DashboardPromptSet/DashboardPromptSetModal.tsx delete mode 100644 src/components/Dashboard/DashboardPrompt/DashboardPromptSet/PromptShareModal.module.css delete mode 100644 src/components/Dashboard/DashboardPrompt/DashboardPromptSet/PromptShareModal.tsx delete mode 100644 src/components/Dashboard/DashboardPrompt/DashboardPromptSettings/DashboardPromptSettings.module.css delete mode 100644 src/components/Dashboard/DashboardPrompt/DashboardPromptSettings/DashboardPromptSettings.tsx create mode 100644 src/components/PageManager/PageManager.module.css create mode 100644 src/components/PageManager/PageManager.tsx create mode 100644 src/components/PageManager/index.ts create mode 100644 src/components/PageManager/pageConfigInterface.ts create mode 100644 src/components/PageManager/pageConfigs.ts rename src/{pages/Home/HomeStyles => components/PageManager}/pages.module.css (87%) delete mode 100644 src/contexts/SidebarData.tsx create mode 100644 src/hooks/useSidebar.ts diff --git a/src/App.tsx b/src/App.tsx index 85281b3..1866622 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -11,15 +11,8 @@ import { AuthProvider } from './auth/authProvider'; import { ProtectedRoute } from './auth/ProtectedRoute'; import { LanguageProvider } from './contexts/LanguageContext'; import Home from './pages/Home/Home'; -import Dateien from './pages/Home/Dateien'; -import TeamBereich from './pages/Home/TeamBereich'; -import Dashboard from './pages/Home/Dashboard'; -import Einstellungen from './pages/Home/Einstellungen'; // Import the global light theme CSS variables as default import './assets/styles/light.css'; -import Connections from './pages/Home/Connections'; -import Workflows from './pages/Home/Workflows'; -import TestSharepoint from './pages/Home/TestSharepoint'; function App() { // Load saved theme preference on app mount @@ -49,14 +42,8 @@ function App() { }> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> + {/* All page routing is now handled by the Page Loader in Home.tsx */} + diff --git a/src/components/Dashboard/DashboardChat/DashboardChat.tsx b/src/components/Dashboard/DashboardChat/DashboardChat.tsx index 2f60cb7..cb3691d 100644 --- a/src/components/Dashboard/DashboardChat/DashboardChat.tsx +++ b/src/components/Dashboard/DashboardChat/DashboardChat.tsx @@ -24,13 +24,10 @@ const DashboardChat: React.FC = ({ onWorkflowResume }) => { const { t } = useLanguage(); - const [activeTab, setActiveTab] = useState(t('dashboard.chat.area')); const [resumeWorkflowId, setResumeWorkflowId] = useState(null); const handleWorkflowResume = (workflowId: string) => { // Switch to Chat Area tab first - setActiveTab(t('dashboard.chat.area')); - // Set the workflow ID to resume setResumeWorkflowId(workflowId); // Then call the parent's resume handler if (onWorkflowResume) { diff --git a/src/components/Dashboard/DashboardChat/DashboardChatArea.tsx b/src/components/Dashboard/DashboardChat/DashboardChatArea.tsx index ffb103d..da5c9b2 100644 --- a/src/components/Dashboard/DashboardChat/DashboardChatArea.tsx +++ b/src/components/Dashboard/DashboardChat/DashboardChatArea.tsx @@ -3,151 +3,105 @@ import MessageList from "./DashboardChatAreaMessageList"; import FilePreview from "./DashboardChatAreaFilePreview"; import InputArea from "./DashboardChatAreaInput"; import ConnectedFiles from "./DashboardChatAreaConnectedFiles"; -import "./DashboardChatAreaStyles/grid.css"; import { DashboardChatAreaProps } from "./dashboardChatAreaTypes"; +import { useWorkflowManager } from "./useWorkflowManager"; +import styles from './DashboardChatAreaStyles/DashboardChat.module.css'; const DashboardChatArea: React.FC = ({ - selectedPrompt, - onPromptUsed, - onWorkflowIdChange, - onWorkflowCompletedChange, - resumeWorkflowId + selectedPrompt, + onPromptUsed, + onWorkflowIdChange, + onWorkflowCompletedChange, + resumeWorkflowId }) => { - // Grid sizing state - const [horizontalSplit, setHorizontalSplit] = useState(60); // percentage - const [verticalSplit, setVerticalSplit] = useState(60); // percentage - const [isDragging, setIsDragging] = useState<'horizontal' | 'vertical' | null>(null); + // Fixed grid layout - no resizing - // File selection state - const [selectedFile, setSelectedFile] = useState(null); - const [attachedFiles, setAttachedFiles] = useState([]); - - // Workflow state - const [currentWorkflowId, setCurrentWorkflowId] = useState(resumeWorkflowId || null); + // File selection state + const [selectedFile, setSelectedFile] = useState(null); + const [attachedFiles, setAttachedFiles] = useState([]); + + // Centralized workflow management + const [workflowState, workflowActions] = useWorkflowManager(resumeWorkflowId); - // Update current workflow ID when resumeWorkflowId changes - React.useEffect(() => { - if (resumeWorkflowId !== currentWorkflowId) { - setCurrentWorkflowId(resumeWorkflowId); - } - }, [resumeWorkflowId, currentWorkflowId]); + // Notify parent when workflow ID changes + React.useEffect(() => { + if (onWorkflowIdChange && workflowState.currentWorkflowId !== resumeWorkflowId) { + onWorkflowIdChange(workflowState.currentWorkflowId); + } + }, [workflowState.currentWorkflowId, onWorkflowIdChange, resumeWorkflowId]); - // Handle workflow ID changes - const handleWorkflowIdChange = React.useCallback((workflowId: string | null) => { - setCurrentWorkflowId(workflowId); - if (onWorkflowIdChange) { - onWorkflowIdChange(workflowId); - } - }, [onWorkflowIdChange]); + // Notify parent when workflow is completed + React.useEffect(() => { + if (onWorkflowCompletedChange && workflowState.workflow) { + const isCompleted = ['completed', 'failed', 'stopped'].includes(workflowState.workflow.status); + onWorkflowCompletedChange(isCompleted); + } + }, [workflowState.workflow?.status, onWorkflowCompletedChange]); - // Handle resizing - const handleMouseDown = (direction: 'horizontal' | 'vertical') => (e: React.MouseEvent) => { - e.preventDefault(); - setIsDragging(direction); - }; + // Auto-load workflow when resumeWorkflowId changes externally + React.useEffect(() => { + if (resumeWorkflowId && resumeWorkflowId !== workflowState.currentWorkflowId) { + console.log(`🔄 Loading workflow from external prop: ${resumeWorkflowId}`); + workflowActions.loadWorkflow(resumeWorkflowId); + } + }, [resumeWorkflowId, workflowState.currentWorkflowId, workflowActions]); - const handleMouseMove = (e: MouseEvent) => { - if (!isDragging) return; + // No resizing functionality needed - const container = document.querySelector('.chat-grid') as HTMLElement; - if (!container) return; + console.log('🎯 DashboardChatArea render:', { + currentWorkflowId: workflowState.currentWorkflowId, + resumeWorkflowId, + workflowStatus: workflowState.workflow?.status, + messagesCount: workflowState.messages?.length || 0, + isLoading: workflowState.isLoading, + isPolling: workflowState.isPolling + }); - const rect = container.getBoundingClientRect(); + return ( +
+ {/* Top Left: Message List */} +
+ +
- if (isDragging === 'horizontal') { - const newSplit = ((e.clientY - rect.top) / rect.height) * 100; - setHorizontalSplit(Math.max(20, Math.min(80, newSplit))); - } else if (isDragging === 'vertical') { - const newSplit = ((e.clientX - rect.left) / rect.width) * 100; - setVerticalSplit(Math.max(20, Math.min(80, newSplit))); - } - }; + {/* Top Right: File Preview */} +
+ +
- const handleMouseUp = () => { - setIsDragging(null); - }; + {/* Bottom Left: Input Area */} +
+ +
- // Event listeners - React.useEffect(() => { - if (isDragging) { - document.addEventListener('mousemove', handleMouseMove); - document.addEventListener('mouseup', handleMouseUp); - document.body.style.cursor = isDragging === 'horizontal' ? 'ns-resize' : 'ew-resize'; - document.body.style.userSelect = 'none'; - - return () => { - document.removeEventListener('mousemove', handleMouseMove); - document.removeEventListener('mouseup', handleMouseUp); - document.body.style.cursor = ''; - document.body.style.userSelect = ''; - }; - } - }, [isDragging]); - - return ( -
- {/* Top Left: Message List */} -
- -
- - {/* Vertical Divider */} -
- - {/* Top Right: File Preview */} -
- -
- - {/* Horizontal Divider */} -
- - {/* Bottom Left: Input Area */} -
- -
- - {/* Bottom Right: Connected Files */} -
- { - // If the removed file is currently selected, clear the selection - if (selectedFile?.id === fileId) { - setSelectedFile(null); - } - // Remove the file from attached files - setAttachedFiles(files => files.filter(f => f.id !== fileId)); - }} - /> -
-
- ); + {/* Bottom Right: Connected Files */} +
+ { + // If the removed file is currently selected, clear the selection + if (selectedFile?.id === fileId) { + setSelectedFile(null); + } + // Remove the file from attached files + setAttachedFiles(files => files.filter(f => f.id !== fileId)); + }} + /> +
+
+ ); }; export default DashboardChatArea; diff --git a/src/components/Dashboard/DashboardChat/DashboardChatAreaConnectedFiles.tsx b/src/components/Dashboard/DashboardChat/DashboardChatAreaConnectedFiles.tsx index e659a77..f68dafb 100644 --- a/src/components/Dashboard/DashboardChat/DashboardChatAreaConnectedFiles.tsx +++ b/src/components/Dashboard/DashboardChat/DashboardChatAreaConnectedFiles.tsx @@ -75,7 +75,6 @@ const ConnectedFiles: React.FC = ({ return (
-

Connected Files

{/* Show attached files count */} {attachedFiles.length > 0 && ( diff --git a/src/components/Dashboard/DashboardChat/DashboardChatAreaFilePreview.tsx b/src/components/Dashboard/DashboardChat/DashboardChatAreaFilePreview.tsx index aafa126..fd9bbb1 100644 --- a/src/components/Dashboard/DashboardChat/DashboardChatAreaFilePreview.tsx +++ b/src/components/Dashboard/DashboardChat/DashboardChatAreaFilePreview.tsx @@ -91,7 +91,7 @@ const FilePreview: React.FC = ({ selectedFile }) => { return (
-

File Preview

+ {!selectedFile && (

diff --git a/src/components/Dashboard/DashboardChat/DashboardChatAreaInput.tsx b/src/components/Dashboard/DashboardChat/DashboardChatAreaInput.tsx index 6202509..ce1eb5b 100644 --- a/src/components/Dashboard/DashboardChat/DashboardChatAreaInput.tsx +++ b/src/components/Dashboard/DashboardChat/DashboardChatAreaInput.tsx @@ -1,187 +1,243 @@ -import React, { useState, useEffect } from 'react'; -import { useWorkflowOperations } from '../../../hooks/useWorkflows'; +import React, { useState, useEffect, useRef } from 'react'; import { Prompt } from '../../../hooks/usePrompts'; import FileAttachmentPopup from './FileAttachmentPopup'; +import { WorkflowManagerState, WorkflowManagerActions } from './useWorkflowManager'; +import { useLanguage } from '../../../contexts/LanguageContext'; + +import styles from './DashboardChatAreaStyles/DashboardChatAreaInput.module.css'; +import sharedStyles from './DashboardChatAreaStyles/DashboardChat.module.css'; interface InputAreaProps { - selectedPrompt?: Prompt | null; - onPromptUsed?: () => void; - onWorkflowIdChange?: (workflowId: string | null) => void; - onAttachedFilesChange?: (files: AttachedFile[]) => void; - attachedFiles?: AttachedFile[]; + selectedPrompt?: Prompt | null; + onPromptUsed?: () => void; + workflowState: WorkflowManagerState; + workflowActions: WorkflowManagerActions; + onAttachedFilesChange?: (files: AttachedFile[]) => void; + attachedFiles?: AttachedFile[]; } interface AttachedFile { - id: number; - name: string; - size: number; - type: string; - fileData?: File; - objectUrl?: string; + id: number; + name: string; + size: number; + type: string; + fileData?: File; + objectUrl?: string; } const InputArea: React.FC = ({ - selectedPrompt, - onPromptUsed, - onWorkflowIdChange, - onAttachedFilesChange, - attachedFiles: externalAttachedFiles = [] + selectedPrompt, + onPromptUsed, + workflowState, + workflowActions, + onAttachedFilesChange, + attachedFiles: externalAttachedFiles = [] }) => { - const [inputValue, setInputValue] = useState(''); - const [showFilePopup, setShowFilePopup] = useState(false); + const { t } = useLanguage(); + const [inputValue, setInputValue] = useState(''); + const [showFilePopup, setShowFilePopup] = useState(false); + const [isSending, setIsSending] = useState(false); + const [sendError, setSendError] = useState(null); + const [isFocused, setIsFocused] = useState(false); + const textareaRef = useRef(null); - // Always use external attached files from parent component - const currentAttachedFiles = externalAttachedFiles; - const { startWorkflow, startingWorkflow, startError } = useWorkflowOperations(); + // Always use external attached files from parent component + const currentAttachedFiles = externalAttachedFiles; - // Auto-fill input when prompt is selected - useEffect(() => { - if (selectedPrompt) { - setInputValue(selectedPrompt.content); - } - }, [selectedPrompt]); + // Auto-resize textarea function + const adjustTextareaHeight = () => { + const textarea = textareaRef.current; + if (!textarea) return; - const handleSend = async () => { - if (!inputValue.trim() || startingWorkflow) return; + // Reset height to auto to get the actual scroll height + textarea.style.height = 'auto'; + + // Calculate the height based on content + const scrollHeight = textarea.scrollHeight; + const lineHeight = 1.5 * 14; // 1.5em * 14px font size + const padding = 32; // 16px top + 16px bottom padding + const minHeight = lineHeight * 4 + padding; // 4 rows + const maxHeight = lineHeight * 8 + padding; // 8 rows + + // Set height within constraints + const newHeight = Math.min(Math.max(scrollHeight, minHeight), maxHeight); + textarea.style.height = `${newHeight}px`; + }; - try { - const result = await startWorkflow({ - prompt: inputValue, - listFileId: currentAttachedFiles.map(f => f.id) - }); + // Auto-fill input when prompt is selected + useEffect(() => { + if (selectedPrompt) { + setInputValue(selectedPrompt.content); + } + }, [selectedPrompt]); - if (result.success) { - setInputValue(''); - if (onAttachedFilesChange) { - onAttachedFilesChange([]); - } - if (onPromptUsed) onPromptUsed(); - if (onWorkflowIdChange && result.data?.id) { - onWorkflowIdChange(result.data.id); - } - } - } catch (error) { - console.error('Failed to start workflow:', error); - } - }; + // Adjust height when input value changes + useEffect(() => { + adjustTextareaHeight(); + }, [inputValue]); - const handleKeyPress = (e: React.KeyboardEvent) => { - if (e.key === 'Enter' && !e.shiftKey) { - e.preventDefault(); - handleSend(); - } - }; + // Initial resize on mount + useEffect(() => { + adjustTextareaHeight(); + }, []); - const handleFilesAttached = (files: AttachedFile[]) => { - setShowFilePopup(false); + const handleSend = async () => { + if (!inputValue.trim() || isSending) return; + + setIsSending(true); + setSendError(null); + + try { + const fileIds = currentAttachedFiles.map(f => f.id); + let success = false; + + if (workflowState.currentWorkflowId) { + // Continue existing workflow + console.log(`➡️ Continuing workflow ${workflowState.currentWorkflowId}`); + success = await workflowActions.continueWorkflow(inputValue, fileIds); + } else { + // Start new workflow + console.log('🚀 Starting new workflow'); + const newWorkflowId = await workflowActions.startNewWorkflow(inputValue, fileIds); + success = !!newWorkflowId; + } + + if (success) { + setInputValue(''); if (onAttachedFilesChange) { - onAttachedFilesChange(files); + onAttachedFilesChange([]); } - }; + if (onPromptUsed) onPromptUsed(); + } else { + setSendError('Failed to send message. Please try again.'); + } + } catch (error: any) { + console.error('Failed to send message:', error); + setSendError(error.message || 'Failed to send message. Please try again.'); + } finally { + setIsSending(false); + } + }; - const formatFileSize = (bytes: number): string => { - if (bytes < 1024) return bytes + ' B'; - if (bytes < 1024 * 1024) return Math.round(bytes / 1024) + ' KB'; - return Math.round(bytes / (1024 * 1024)) + ' MB'; - }; + const handleKeyPress = (e: React.KeyboardEvent) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + handleSend(); + } + }; - return ( -

-

Input

- - {startError && ( -
- Error: {startError} -
- )} + const handleFilesAttached = (files: AttachedFile[]) => { + setShowFilePopup(false); + if (onAttachedFilesChange) { + onAttachedFilesChange(files); + } + }; - {/* Show attached files count */} - {currentAttachedFiles.length > 0 && ( -
- 📎 {currentAttachedFiles.length} file{currentAttachedFiles.length !== 1 ? 's' : ''} attached -
- )} -
-