import { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { Plus, Trash2, MessageSquare, Workflow as WorkflowIcon } from 'lucide-react'; import apiClient from '../../api/client'; import type { Workflow } from '../../types'; import { useChatStore } from '../../store/useChatStore'; interface LeftPanelProps { activeTab: string; } export function LeftPanel({ activeTab }: LeftPanelProps) { const { t } = useTranslation(); const [workflows, setWorkflows] = useState([]); const [loadingWorkflows, setLoadingWorkflows] = useState(false); const { sessions, activeSessionId, setActiveSessionId, removeSession, createChat, selectedWorkflow, setSelectedWorkflow, } = useChatStore(); useEffect(() => { let intervalId: ReturnType; const fetchWorkflows = async (isInitial = false) => { if (isInitial) setLoadingWorkflows(true); try { const response = await apiClient.get('/api/v1/workflow/list'); const data = response.data; let parsedWorkflows: Workflow[] = []; if (Array.isArray(data)) { parsedWorkflows = data; } else if (data && typeof data === 'object') { parsedWorkflows = data.workflows || Object.values(data); } setWorkflows(parsedWorkflows); } catch (error) { console.error('Failed to fetch workflows', error); setWorkflows([]); } finally { if (isInitial) setLoadingWorkflows(false); } }; if (activeTab === 'workflows') { fetchWorkflows(true); intervalId = setInterval(() => fetchWorkflows(false), 2000); } return () => { if (intervalId) clearInterval(intervalId); }; }, [activeTab]); const handleNewChat = async () => { await createChat(t('chat.newChat'), '你好'); }; const handleDeleteChat = (e: React.MouseEvent, id: string) => { e.stopPropagation(); removeSession(id); }; const isChats = activeTab === 'chats'; return (
{isChats ? t('chat.chatHistory') : t('nav.workflow')}
{isChats ? ( sessions.length === 0 ? (
{t('chat.noHistory')}
) : ( sessions.map((session) => { const isActive = activeSessionId === session.id; return (
setActiveSessionId(session.id)} className={`group flex items-center gap-2.5 px-2.5 py-2 rounded-lg cursor-pointer transition-all mb-px ${ isActive ? 'bg-bg-card shadow-[0_1px_3px_rgba(0,0,0,0.04)]' : 'hover:bg-white/60 dark:hover:bg-white/[0.04]' }`} >

{session.title}

); }) ) ) : ( loadingWorkflows ? (
{t('workflow.loading')}
) : workflows.length === 0 ? (
{t('workflow.noWorkflows')}
) : ( workflows.map((wf) => { const isActive = selectedWorkflow === wf.trace_id; return (
setSelectedWorkflow(wf.trace_id)} className={`group flex items-center gap-2.5 px-2.5 py-2 rounded-lg cursor-pointer transition-all mb-px ${ isActive ? 'bg-bg-card shadow-[0_1px_3px_rgba(0,0,0,0.04)]' : 'hover:bg-white/60 dark:hover:bg-white/[0.04]' }`} >

{wf.title || t('common.unnamed')}

); }) ) )}
); }