aura-web/src/pages/chat/components/AgentSidebar.tsx

137 lines
4.3 KiB
TypeScript

import { Button, Spin, Empty, Tabs } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { useEffect, useState, useMemo } from 'react';
import type { Agent } from '../../../api';
import './AgentSidebar.css';
const isImageUrl = (url: string) => url?.startsWith('http') || url?.startsWith('/');
export default function AgentSidebar(props: {
agentList: Agent[];
activeAgentId?: string;
onCreate: () => void;
onSelect: (agentId: string) => void;
isSidebar?: boolean;
}) {
const { agentList, activeAgentId, onCreate, onSelect, isSidebar = true } = props;
const [loadingTimeout, setLoadingTimeout] = useState(false);
const [activeTab, setActiveTab] = useState('mine');
useEffect(() => {
if (agentList.length > 0) return;
const timer = setTimeout(() => {
setLoadingTimeout(true);
}, 5000);
return () => clearTimeout(timer);
}, [agentList.length]);
const filteredList = useMemo(() => {
if (activeTab === 'public') return agentList.filter(a => a.visibility === 'public');
if (activeTab === 'private') return agentList.filter(a => a.visibility === 'private');
return agentList; // 'mine' tab shows all for now or filter by owner_id if available
}, [agentList, activeTab]);
// 模拟分组逻辑
const groupedAgents = useMemo(() => {
const groups: Record<string, Agent[]> = {
'我的智能体': [],
'推荐': []
};
filteredList.forEach(a => {
if (a.visibility === 'public') {
groups['推荐'].push(a);
} else {
groups['我的智能体'].push(a);
}
});
return Object.entries(groups).filter(([_, list]) => list.length > 0);
}, [filteredList]);
return (
<aside className={`chat-side${isSidebar ? '' : ' is-full'}`}>
<div className="chat-agent-sidebar-header">
<Button
block
type="primary"
icon={<PlusOutlined />}
onClick={onCreate}
className="chat-agent-create-btn"
>
</Button>
</div>
<div className="chat-agent-tabs">
<Tabs
activeKey={activeTab}
onChange={setActiveTab}
items={[
{ key: 'mine', label: '我的' },
{ key: 'public', label: '公开' },
{ key: 'private', label: '私有' }
]}
/>
</div>
<div className="chat-agent-sidebar-list">
{agentList.length > 0 ? (
groupedAgents.map(([groupName, list]) => (
<div key={groupName} className="chat-agent-group">
<div className="chat-agent-group-label">
<span>{groupName}</span>
<span className="chat-agent-group-count">{list.length}</span>
</div>
{list.map((a) => {
const isActive = a.id === activeAgentId;
return (
<div
key={a.id}
onClick={() => onSelect(a.id)}
className={`chat-agent-item ${isActive ? 'active' : ''}`}
>
<div
className="chat-agent-avatar-wrap"
style={{ background: a.avatar || 'var(--gradient-brand)' }}
>
{isImageUrl(a.avatar) ? (
<img src={a.avatar} className="chat-agent-avatar-img" alt="avatar" />
) : (
(a.name?.charAt(0) || '?').toUpperCase()
)}
</div>
<div className="chat-agent-info">
<div className="chat-agent-name">{a.name}</div>
</div>
</div>
);
})}
</div>
))
) : !loadingTimeout ? (
<div className="chat-agent-sidebar-status">
<Spin />
<div className="chat-agent-loading-text">...</div>
</div>
) : (
<div className="chat-agent-sidebar-status">
<Empty
image={Empty.PRESENTED_IMAGE_SIMPLE}
description={
<span className="chat-agent-empty-text">
<br />
</span>
}
/>
</div>
)}
</div>
</aside>
);
}