feat(agent): activate BOC agent layer
- Add agent orchestrator API (/api/v1/agents/*) - Connect frontend AgentContext to real backend - Add AgentLayerPage with full agent management - Implement specialist agents: finance, sales, hr, crm, legal, marketing, dashboard - Add authentication, RBAC, tenant isolation on agent endpoints - Add rate limiting and audit logging - Update sidebar with Agent Layer navigation - Build fresh web-v2 dist
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { motion } from 'framer-motion'
|
||||
import {
|
||||
Bot,
|
||||
Users,
|
||||
TrendingUp,
|
||||
Megaphone,
|
||||
Share2,
|
||||
Wallet,
|
||||
Calculator,
|
||||
Briefcase,
|
||||
FileText,
|
||||
ShieldCheck,
|
||||
HeadphonesIcon,
|
||||
FolderKanban,
|
||||
Zap,
|
||||
Sparkles,
|
||||
Activity,
|
||||
CheckCircle2,
|
||||
AlertCircle,
|
||||
XCircle
|
||||
} from 'lucide-react'
|
||||
import { getAllAgents, AgentContext } from '@/components/agent/AgentContext'
|
||||
import { AgentChat } from '@/components/agent/AgentChat'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const agentIcons: Record<string, React.ElementType> = {
|
||||
crm: Users,
|
||||
sales: TrendingUp,
|
||||
marketing: Megaphone,
|
||||
social: Share2,
|
||||
finance: Wallet,
|
||||
accounting: Calculator,
|
||||
hr: Briefcase,
|
||||
legal: FileText,
|
||||
compliance: ShieldCheck,
|
||||
support: HeadphonesIcon,
|
||||
projects: FolderKanban,
|
||||
automation: Zap,
|
||||
dashboard: Sparkles
|
||||
};
|
||||
|
||||
const statusConfig = {
|
||||
operational: { icon: CheckCircle2, color: 'text-green-500', bg: 'bg-green-500/10', label: 'Operational' },
|
||||
degraded: { icon: AlertCircle, color: 'text-yellow-500', bg: 'bg-yellow-500/10', label: 'Degraded' },
|
||||
error: { icon: XCircle, color: 'text-red-500', bg: 'bg-red-500/10', label: 'Error' },
|
||||
disabled: { icon: XCircle, color: 'text-gray-400', bg: 'bg-gray-400/10', label: 'Disabled' }
|
||||
};
|
||||
|
||||
export function AgentLayerPage() {
|
||||
const [selectedAgent, setSelectedAgent] = useState<string | null>(null);
|
||||
const [agents, setAgents] = useState<AgentContext[]>([]);
|
||||
const [chatOpen, setChatOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setAgents(getAllAgents());
|
||||
}, []);
|
||||
|
||||
const handleAgentClick = (rum: string) => {
|
||||
setSelectedAgent(rum);
|
||||
setChatOpen(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-text-primary">BOC Agent Layer</h1>
|
||||
<p className="text-text-secondary mt-1">
|
||||
Professionellt agentlager för hela organisationen
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 px-4 py-2 bg-green-500/10 rounded-lg">
|
||||
<Activity size={16} className="text-green-500" />
|
||||
<span className="text-sm font-medium text-green-600">
|
||||
{agents.filter(a => a.status === 'operational').length} / {agents.length} Operational
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Agent Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
|
||||
{agents.map((agent, index) => {
|
||||
const Icon = agentIcons[agent.rum] || Bot;
|
||||
const status = statusConfig[agent.status];
|
||||
const StatusIcon = status.icon;
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
key={agent.rum}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: index * 0.05 }}
|
||||
onClick={() => handleAgentClick(agent.rum)}
|
||||
className={cn(
|
||||
'group relative p-5 rounded-2xl border border-border bg-surface',
|
||||
'hover:border-primary/30 hover:shadow-lg hover:shadow-primary/5',
|
||||
'cursor-pointer transition-all duration-200'
|
||||
)}
|
||||
>
|
||||
{/* Status Indicator */}
|
||||
<div className="absolute top-4 right-4">
|
||||
<div className={cn('flex items-center gap-1.5 px-2 py-1 rounded-full', status.bg)}>
|
||||
<StatusIcon size={12} className={status.color} />
|
||||
<span className={cn('text-xs font-medium', status.color)}>
|
||||
{status.label}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Icon */}
|
||||
<div className="w-12 h-12 rounded-xl bg-primary/10 flex items-center justify-center mb-4 group-hover:bg-primary/20 transition-colors">
|
||||
<Icon size={24} className="text-primary" />
|
||||
</div>
|
||||
|
||||
{/* Info */}
|
||||
<h3 className="font-semibold text-text-primary mb-1">{agent.titel}</h3>
|
||||
<p className="text-sm text-text-secondary mb-3 line-clamp-2">
|
||||
{agent.kompetenser.slice(0, 3).join(', ')}
|
||||
</p>
|
||||
|
||||
{/* Capabilities */}
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{agent.capabilities.map(cap => (
|
||||
<span
|
||||
key={cap}
|
||||
className="text-xs px-2 py-0.5 rounded-md bg-bg text-text-secondary"
|
||||
>
|
||||
{cap}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Hover Action */}
|
||||
<div className="mt-4 pt-4 border-t border-border opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<div className="flex items-center gap-2 text-sm text-primary">
|
||||
<Sparkles size={14} />
|
||||
<span>Öppna agent</span>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Agent Chat */}
|
||||
{selectedAgent && (
|
||||
<AgentChat
|
||||
rum={selectedAgent}
|
||||
isOpen={chatOpen}
|
||||
onToggle={() => setChatOpen(!chatOpen)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user