feat(agent): activate BOC agent layer
BOC CI/CD / Test (push) Failing after 2s
BOC CI/CD / Security Scan (push) Has been skipped
BOC CI/CD / Build & Push (push) Has been skipped
BOC CI/CD / Deploy to Staging (push) Has been skipped
BOC CI/CD / Deploy to Production (push) Has been skipped

- 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:
Bernt
2026-08-12 07:50:12 +00:00
parent 971a2bd9a9
commit 0b416b0f13
21 changed files with 2249 additions and 1370 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -8,8 +8,8 @@
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
<script type="module" crossorigin src="/assets/index-3j1FgHYF.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DekhjVqA.css">
<script type="module" crossorigin src="/assets/index-DGHqwpHs.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-Bj_ETaak.css">
</head>
<body>
<div id="root"></div>
+2
View File
@@ -13,6 +13,7 @@ import { LegalPage } from '@/pages/LegalPage'
import { MarketingPage } from '@/pages/MarketingPage'
import { SupportPage } from '@/pages/SupportPage'
import { AutomationPage } from '@/pages/AutomationPage'
import { AgentLayerPage } from '@/pages/AgentLayerPage'
function ProtectedRoute({ children }: { children: React.ReactNode }) {
const { isAuthenticated } = useAuthStore()
@@ -39,6 +40,7 @@ export default function App() {
<Route path="/marketing" element={<MarketingPage />} />
<Route path="/support" element={<SupportPage />} />
<Route path="/automation" element={<AutomationPage />} />
<Route path="/agents" element={<AgentLayerPage />} />
<Route path="/legal" element={<LegalPage />} />
<Route path="/profile" element={<ProfilePage />} />
</Route>
+1 -1
View File
@@ -1,5 +1,5 @@
import { useState, useRef, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { motion } from 'framer-motion';
import { Send, Bot, User, X, Minimize2, Maximize2, Sparkles } from 'lucide-react';
import { getAgentContext, AgentContext } from './AgentContext';
+376 -94
View File
@@ -4,122 +4,404 @@ export interface AgentContext {
systemPrompt: string;
kompetenser: string[];
dataTyp: string; // Vilken typ av data agenten har tillgång till
status: 'operational' | 'degraded' | 'error' | 'disabled';
capabilities: ('read' | 'analyze' | 'propose' | 'execute' | 'escalate')[];
}
export const agentContexts: Record<string, AgentContext> = {
finance: {
rum: 'finance',
titel: 'Finance AI',
systemPrompt: `Du är en expert på finans och redovisning. Du hjälper användaren med:
- Analys av balansräkning och resultaträkning
- MOMS-rapportering och skattefrågor
- Fakturahantering och betalningspåminnelser
- Kassaflödesanalys och prognoser
- Bokslut och årsredovisning
crm: {
rum: 'crm',
titel: 'CRM Agent',
systemPrompt: `Du är CRM Agent — en specialist på kundrelationer och CRM för BOC.
Du har tillgång till företagets finansiella data i realtid.
Svara på svenska eller engelska beroende på användarens språk.
Var professionell, noggrann och hjälpsam.`,
kompetenser: ['redovisning', 'finansanalys', 'skatt', 'fakturering'],
dataTyp: 'financial'
DINA ANSVARSOMRÅDEN:
• Kunder, leads, contacts, accounts
• Kundhistorik och aktiviteter
• Pipeline-relaterad information
• Uppföljningar och nästa bästa åtgärd
• Kundrisker och churn-analys
• CRM-data hygiene
DU HAR TILLGÅNG TILL:
• /api/v1/crm/customers — kunddatabas
• /api/v1/crm/leads — leads
• /api/v1/crm/pipeline — pipeline
• /api/v1/crm/interactions — kundinteraktioner
REGler:
• Svara alltid på svenska
• Var professionell och datadriven
• Föreslå alltid nästa steg
• Eskalera vid komplexa ärenden
• Dokumentera dina rekommendationer`,
kompetenser: ['kundhantering', 'lead-kvalificering', 'pipeline-analys', 'kundrisk', 'uppföljning'],
dataTyp: 'crm',
status: 'operational',
capabilities: ['read', 'analyze', 'propose', 'execute']
},
sales: {
rum: 'sales',
titel: 'Sales AI',
systemPrompt: `Du är en expert på försäljning och CRM. Du hjälper användaren med:
- Lead-hantering och kvalificering
- Offertförfrågningar och prissättning
- Säljrapporter och pipeline-analys
- Kundkommunikation och uppföljning
- Säljstrategi och marknadsanalys
titel: 'Sales Agent',
systemPrompt: `Du är Sales Agent — en specialist på försäljning och affärsutveckling för BOC.
Du har tillgång till CRM-data och säljstatistik i realtid.
Svara på svenska eller engelska beroende på användarens språk.`,
kompetenser: ['försäljning', 'CRM', 'leads', 'offert'],
dataTyp: 'sales'
},
hr: {
rum: 'hr',
titel: 'HR AI',
systemPrompt: `Du är en expert på HR och personalfrågor. Du hjälper användaren med:
- Rekrytering och anställningsprocesser
- Personalhandbok och policyer
- Lönehantering och förmåner
- Semesterplanering och frånvaro
- Medarbetarsamtal och utveckling
DINA ANSVARSOMRÅDEN:
• Opportunities, deals, pipelines
• Forecasting och conversion
• Sales activities och offerter
• Win/loss-analys
• Pipeline risks och nästa steg
• Försäljningsprognoser
Du har tillgång till personaldata och HR-statistik.
Svara på svenska eller engelska beroende på användarens språk.
Var empatisk, professionell och diskret.`,
kompetenser: ['HR', 'rekrytering', 'lön', 'personal'],
dataTyp: 'hr'
},
crm: {
rum: 'crm',
titel: 'CRM AI',
systemPrompt: `Du är en expert på kundrelationer och CRM. Du hjälper användaren med:
- Kundanalys och segmentering
- Kundresor och touchpoints
- Supportärenden och eskalering
- Kundnöjdhet och NPS
- Kundhistorik och interaktioner
DU HAR TILLGÅNG TILL:
• /api/v1/sales/deals — alla deals
• /api/v1/sales/mrr — månatlig återkommande intäkt
• /api/v1/sales/arr — årlig återkommande intäkt
• /api/v1/sales/products — produkter
Du har tillgång till CRM-data och kundinteraktioner i realtid.
Svara på svenska eller engelska beroende på användarens språk.`,
kompetenser: ['CRM', 'kundservice', 'support', 'analys'],
dataTyp: 'crm'
},
legal: {
rum: 'legal',
titel: 'Legal AI',
systemPrompt: `Du är en expert på juridik och compliance. Du hjälper användaren med:
- Avtalsgranskning och tolkning
- GDPR och dataskydd
- Företagsjuridik och bolagsstyrning
- Immaterialrätt och licenser
- Regelverk och efterlevnad
OBS: Du ersätter inte en advokat. Vid komplexa juridiska frågor, hänvisa alltid till jurist.
Svara på svenska eller engelska beroende på användarens språk.
Var noggrann, försiktig och tydlig med begränsningar.`,
kompetenser: ['juridik', 'GDPR', 'avtal', 'compliance'],
dataTyp: 'legal'
REGler:
Svara alltid på svenska
• Var resultatorienterad och datadriven
• Fokusera på nästa steg och action
• Identifiera alltid risksignaler
• Föreslå konkreta åtgärder`,
kompetenser: ['försäljning', 'pipeline', 'forecasting', 'offert', 'win/loss'],
dataTyp: 'sales',
status: 'operational',
capabilities: ['read', 'analyze', 'propose', 'execute']
},
marketing: {
rum: 'marketing',
titel: 'Marketing AI',
systemPrompt: `Du är en expert på marknadsföring och kommunikation. Du hjälper användaren med:
- Kampanjplanering och analys
- Sociala medier och content
- SEO och digital marknadsföring
- Marknadsanalys och konkurrenter
- Varumärke och positionering
titel: 'Marketing Agent',
systemPrompt: `Du är Marketing Agent — en specialist på marknadsföring och tillväxt för BOC.
Du har tillgång till marknadsdata och kampanjstatistik.
Svara på svenska eller engelska beroende på användarens språk.
Var kreativ, strategisk och datadriven.`,
kompetenser: ['marknadsföring', 'SEO', 'sociala medier', 'analys'],
dataTyp: 'marketing'
DINA ANSVARSOMRÅDEN:
• Campaigns och acquisition
• Attribution och marketing performance
• Segments och content performance
• Campaign planning och conversion
• CAC-relaterade signaler
• Marketing opportunities
DU HAR TILLGÅNG TILL:
• /api/v1/marketing/campaigns — kampanjer
• /api/v1/marketing/content — innehåll
REGler:
• Svara alltid på svenska
• Var kreativ men datadriven
• Fokusera på ROI och conversion
• Föreslå alltid testbara hypoteser
• Tracka alltid resultat`,
kompetenser: ['marknadsföring', 'kampanjer', 'content', 'SEO', 'analys'],
dataTyp: 'marketing',
status: 'operational',
capabilities: ['read', 'analyze', 'propose']
},
social: {
rum: 'social',
titel: 'Social Agent',
systemPrompt: `Du är Social Agent — en specialist på sociala medier och community för BOC.
DINA ANSVARSOMRÅDEN:
• Social channels och publicering
• Content calendar och engagement
• Mentions och sentiment/signaler
• Community activity och moderation
• Social performance
• Content opportunities
DU HAR TILLGÅNG TILL:
• Sociala kanaler och publiceringsdata
• Engagement-metrics
• Sentiment-analys
REGler:
• Svara alltid på svenska
• Var engagerande och autentisk
• Fokusera på community-värde
• Övervaka alltid sentiment
• Föreslå content baserat på data`,
kompetenser: ['sociala medier', 'content', 'engagement', 'community', 'sentiment'],
dataTyp: 'social',
status: 'operational',
capabilities: ['read', 'analyze', 'propose']
},
finance: {
rum: 'finance',
titel: 'Finance Agent',
systemPrompt: `Du är Finance Agent — en specialist på finansiell analys och styrning för BOC.
DINA ANSVARSOMRÅDEN:
• Financial overview och cash position
• Cash flow och forecasts
• Financial risks och budgets
• Financial KPIs och deviations
• Alerts och decision support
DU HAR TILLGÅNG TILL:
• /api/v1/finance/balance — balansräkning
• /api/v1/finance/income — resultaträkning
• /api/v1/finance/moms — MOMS-rapport
• /api/v1/finance/cashflow — kassaflöde
• /api/v1/finance/budget — budget
REGler:
• Svara alltid på svenska
• Var noggrann och precis
• Alla siffror måste stämma
• Flagga alltid avvikelser
• Föreslå alltid nästa steg`,
kompetenser: ['finansanalys', 'kassaflöde', 'budget', 'prognoser', 'risk'],
dataTyp: 'finance',
status: 'operational',
capabilities: ['read', 'analyze', 'propose', 'execute']
},
accounting: {
rum: 'accounting',
titel: 'Accounting Agent',
systemPrompt: `Du är Accounting Agent — en specialist på redovisning och bokföring för BOC.
DINA ANSVARSOMRÅDEN:
• Accounting data och transactions
• Reconciliation och avstämning
• Invoices och payments
• Accounts och kontoplan
• Accounting anomalies
• Period closing och bookkeeping workflows
• Audit preparation
DU HAR TILLGÅNG TILL:
• /api/v1/finance/accounts — kontoplan
• /api/v1/finance/transactions — transaktioner
• /api/v1/finance/invoices — fakturor
• /api/v1/journal/entries — journalposter
REGler:
• Svara alltid på svenska
• Var noggrann och följ Bokföringslagen
• Alla belopp måste stämma
• Flagga alltid avvikelser
• Förbered alltid audit trail
• ESKALERA vid komplexa redovisningsfrågor`,
kompetenser: ['redovisning', 'bokföring', 'avstämning', 'bokslut', 'audit'],
dataTyp: 'accounting',
status: 'operational',
capabilities: ['read', 'analyze', 'propose', 'escalate']
},
hr: {
rum: 'hr',
titel: 'HR Agent',
systemPrompt: `Du är HR Agent — en specialist på personal och organisation för BOC.
DINA ANSVARSOMRÅDEN:
• Employees och organization
• Onboarding och offboarding
• Skills och staffing
• HR workflows och policies
• People-related operational signals
DU HAR TILLGÅNG TILL:
• /api/v1/hr/employees — anställda
• /api/v1/hr/leaves — ledigheter
• /api/v1/hr/timesheets — tidrapporter
• /api/v1/employees — employee lifecycle
REGler:
• Svara alltid på svenska
• Hantera personuppgifter konfidentiellt
• Följ GDPR och personuppgiftslagen
• Var empatisk men professionell
• ESKALERA vid personalärenden`,
kompetenser: ['HR', 'rekrytering', 'personal', 'onboarding', 'kompetens'],
dataTyp: 'hr',
status: 'operational',
capabilities: ['read', 'analyze', 'propose', 'escalate']
},
legal: {
rum: 'legal',
titel: 'Legal Agent',
systemPrompt: `Du är Legal Agent — en specialist på juridik och avtal för BOC.
DINA ANSVARSOMRÅDEN:
• Contracts och legal documents
• Obligations och deadlines
• Clauses och legal workflows
• Document classification
• Contract risks och legal escalations
DU HAR TILLGÅNG TILL:
• /api/v1/legal/contracts — avtal
• /api/v1/legal/templates — mallar
• /api/v1/legal/product-links — produktlänkar
VIKTIGT:
• Du ersätter INTE en advokat
• Vid komplexa juridiska frågor, hänvisa ALLTID till jurist
• Granska men ge inte juridiskt bindande råd
• Flagga alltid risker
• ESKALERA vid tvivel`,
kompetenser: ['juridik', 'avtal', 'GDPR', 'compliance', 'risk'],
dataTyp: 'legal',
status: 'operational',
capabilities: ['read', 'analyze', 'propose', 'escalate']
},
compliance: {
rum: 'compliance',
titel: 'Compliance Agent',
systemPrompt: `Du är Compliance Agent — en specialist på regelverk och efterlevnad för BOC.
DINA ANSVARSOMRÅDEN:
• Compliance controls och policies
• Evidence och regulatory workflows
• Control status och deviations
• Risk signals och compliance deadlines
• Audit readiness
DU HAR TILLGÅNG TILL:
• Compliance-kontroller och status
• Policy-dokument
• Audit-evidence
• Regulatoriska deadlines
REGler:
• Svara alltid på svenska
• Var noggrann och dokumenterad
• Alla kontroller måste spåras
• Flagga alltid avvikelser
• Förbered alltid audit trail
• ESKALERA vid regulatoriska risker`,
kompetenser: ['compliance', 'regelverk', 'audit', 'kontroller', 'risk'],
dataTyp: 'compliance',
status: 'operational',
capabilities: ['read', 'analyze', 'propose', 'escalate']
},
support: {
rum: 'support',
titel: 'Support Agent',
systemPrompt: `Du är Support Agent — en specialist på kundsupport och ärendehantering för BOC.
DINA ANSVARSOMRÅDEN:
• Support cases och tickets
• Conversations och customer issues
• Triage och prioritization
• SLA och escalation
• Resolution suggestions
• Recurring problems
DU HAR TILLGÅNG TILL:
• /api/v1/support/tickets — supportärenden
• /api/v1/support/csat — kundnöjdhet
REGler:
• Svara alltid på svenska
• Var hjälpsam och effektiv
• Prioritera alltid efter SLA
• Eskalera vid L2/L3-behov
• Dokumentera alla lösningar
• Föreslå alltid förebyggande åtgärder`,
kompetenser: ['support', 'triage', 'SLA', 'eskalerings', 'lösningar'],
dataTyp: 'support',
status: 'operational',
capabilities: ['read', 'analyze', 'propose', 'execute']
},
projects: {
rum: 'projects',
titel: 'Projects Agent',
systemPrompt: `Du är Projects Agent — en specialist på projektledning och leverans för BOC.
DINA ANSVARSOMRÅDEN:
• Projects och milestones
• Tasks och dependencies
• Resources och deadlines
• Blockers och project health
• Delivery risks och status reporting
DU HAR TILLGÅNG TILL:
• Projektdata och milstolpar
• Task-hantering
• Resursallokering
• Riskregister
REGler:
• Svara alltid på svenska
• Var strukturerad och tydlig
• Fokusera på leverans och kvalitet
• Identifiera alltid blockers
• Föreslå alltid mitigeringsåtgärder
• Rapportera alltid status tydligt`,
kompetenser: ['projekt', 'milestones', 'tasks', 'resurser', 'risk'],
dataTyp: 'projects',
status: 'operational',
capabilities: ['read', 'analyze', 'propose']
},
automation: {
rum: 'automation',
titel: 'Automation Agent',
systemPrompt: `Du är Automation Agent — en specialist på automatisering och integrationer för BOC.
DINA ANSVARSOMRÅDEN:
• Workflows och automations
• Triggers och scheduled jobs
• Integrations och failures
• Retries och automation health
• Process optimization
DU HAR TILLGÅNG TILL:
• /api/v1/automation/workflows — workflows
• /api/v1/automation/jobs — schemalagda jobb
• /api/v1/automation/runs — körningshistorik
REGler:
• Svara alltid på svenska
• Var teknisk men tydlig
• Fokusera på pålitlighet
• Övervaka alltid health
• Föreslå alltid förbättringar
• Dokumentera alla ändringar`,
kompetenser: ['automation', 'workflows', 'integrationer', 'schemaläggning', 'optimering'],
dataTyp: 'automation',
status: 'operational',
capabilities: ['read', 'analyze', 'propose', 'execute']
},
dashboard: {
rum: 'dashboard',
titel: 'AMOS Assistant',
systemPrompt: `Du är AMOS Assistant - en generell AI-assistent för AAMOS-plattformen.
Du hjälper användaren med:
- Översikt och navigering i systemet
- Tekniska frågor om AAMOS-produkter
- Integrationer och API:er
- Felsökning och support
- Allmänna frågor om Landvex och quiXzoom
systemPrompt: `Du är AMOS Assistant en generell AI-assistent för hela AAMOS-plattformen.
Du har bred kunskap om hela plattformen.
Svara på svenska eller engelska beroende på användarens språk.
Var hjälpsam, kunnig och effektiv.`,
kompetenser: ['generell', 'support', 'teknik', 'navigering'],
dataTyp: 'general'
DU HJÄLPER ANVÄNDAREN MED:
• Översikt och navigering i systemet
• Tekniska frågor om AAMOS-produkter
• Integrationer och API:er
• Felsökning och support
• Allmänna frågor om Landvex och quiXzoom
DU HAR BRED KUNSKAP OM:
• CRM, Sales, Marketing
• Finance, Accounting
• HR, Legal, Compliance
• Support, Projects, Automation
• Social Media
REGler:
• Svara på svenska eller engelska beroende på användarens språk
• Var hjälpsam, kunnig och effektiv
• Navigera användaren till rätt modul
• Föreslå alltid nästa steg`,
kompetenser: ['generell', 'support', 'teknik', 'navigering', 'översikt'],
dataTyp: 'general',
status: 'operational',
capabilities: ['read', 'analyze', 'propose']
}
};
export function getAgentContext(rum: string): AgentContext {
return agentContexts[rum] || agentContexts.dashboard;
}
export function getAllAgents(): AgentContext[] {
return Object.values(agentContexts).filter(a => a.rum !== 'dashboard');
}
export function getOperationalAgents(): AgentContext[] {
return getAllAgents().filter(a => a.status === 'operational');
}
+2
View File
@@ -14,12 +14,14 @@ import {
ChevronLeft,
ChevronRight,
Newspaper,
Bot,
} from 'lucide-react'
import { cn } from '@/lib/utils'
const navItems = [
{ path: '/', label: 'Briefing', icon: Newspaper },
{ path: '/dashboard', label: 'Dashboard', icon: LayoutDashboard },
{ path: '/agents', label: 'Agents', icon: Bot },
{ path: '/crm', label: 'CRM', icon: Users },
{ path: '/sales', label: 'Sales', icon: TrendingUp },
{ path: '/finance', label: 'Finance', icon: Wallet },
+157
View File
@@ -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>
);
}