'use client' import { useCallback, useEffect, useMemo, useState } from 'react' import { ChevronDown, GraduationCap, Loader2 } from 'lucide-react' import ReactMarkdown from 'react-markdown' import remarkGfm from 'remark-gfm' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Badge } from '@/components/ui/badge' import { EmptyState } from '@/components/ui/empty-state' import { Skeleton } from '@/components/ui/skeleton' import { useToast } from '@/components/ui/use-toast' type Tier = 'horizontal' | 'vertical' | 'modifier' interface AtomMeta { id: string tier: Tier title: string description: string active: boolean } const TIER_ORDER: Tier[] = ['horizontal', 'vertical', 'modifier'] const TIER_SECTION: Record = { horizontal: { title: 'Kärnkompetens', blurb: 'Svenska bokförings- och skatteregler som assistenten alltid har med sig.', }, vertical: { title: 'Anpassat för din bransch', blurb: 'Branschkunskap som valts utifrån vad ditt företag gör. Vilande områden finns men används inte för dig.', }, modifier: { title: 'Din bolagssituation', blurb: 'Särskilda regler för hur just ditt bolag är uppbyggt.', }, } // Mirror of the chat surface's markdown styling (components/agent/AgentChat.tsx), // trimmed for a wider settings column. const PROSE = 'prose prose-sm max-w-none text-foreground [&>*:first-child]:mt-0 [&>*:last-child]:mb-0 ' + 'prose-headings:font-display prose-headings:font-normal prose-headings:tracking-tight ' + 'prose-h1:text-lg prose-h2:text-base prose-h3:text-sm prose-p:my-2 prose-p:leading-6 ' + 'prose-strong:font-semibold prose-strong:text-foreground prose-ul:my-2 prose-li:my-0.5 ' + 'prose-a:text-foreground prose-a:underline prose-a:underline-offset-2 ' + 'prose-code:bg-secondary prose-code:rounded prose-code:px-1 prose-code:py-0.5 prose-code:text-xs ' + 'prose-code:before:content-none prose-code:after:content-none ' + 'prose-table:my-2 prose-table:text-xs [&_table]:w-full ' + '[&_th]:border-b [&_th]:border-border [&_th]:py-1.5 [&_th]:px-2 [&_th]:text-left [&_th]:font-medium ' + '[&_th]:text-muted-foreground [&_th]:uppercase [&_th]:tracking-wider [&_th]:text-[10px] ' + '[&_td]:border-b [&_td]:border-border/60 [&_td]:py-1.5 [&_td]:px-2 [&_td]:align-top' export function AgentSkillsPanel() { const { toast } = useToast() const [atoms, setAtoms] = useState(null) const [expandedId, setExpandedId] = useState(null) const [bodies, setBodies] = useState>({}) const [loadingBody, setLoadingBody] = useState(null) const load = useCallback(async () => { const res = await fetch('/api/agent/skills') const json = await res.json() if (!res.ok) { toast({ title: 'Kunde inte hämta kunskap', description: json.error, variant: 'destructive' }) setAtoms([]) return } setAtoms(json.data as AtomMeta[]) }, [toast]) useEffect(() => { void load() }, [load]) const grouped = useMemo(() => { const map: Record = { horizontal: [], vertical: [], modifier: [] } for (const a of atoms ?? []) map[a.tier]?.push(a) return map }, [atoms]) const counts = useMemo(() => { const total = atoms?.length ?? 0 const active = atoms?.filter((a) => a.active).length ?? 0 return { total, active } }, [atoms]) async function toggle(atom: AtomMeta) { if (expandedId === atom.id) { setExpandedId(null) return } setExpandedId(atom.id) if (bodies[atom.id] !== undefined) return setLoadingBody(atom.id) try { const res = await fetch(`/api/agent/skills?slug=${encodeURIComponent(atom.id)}`) const json = await res.json() if (!res.ok) { toast({ title: 'Kunde inte läsa kunskapen', description: json.error, variant: 'destructive' }) return } setBodies((prev) => ({ ...prev, [atom.id]: (json.data?.body as string) ?? '' })) } finally { setLoadingBody(null) } } return ( Vad min assistent kan Utöver vad den minns om ditt företag bygger assistenten på en uppsättning kunskapsområden om svensk bokföring och skatt. Kärnkompetensen gäller alla; bransch- och bolagsanpassningen väljs utifrån ditt företag. Klicka för att läsa hela kunskapen. {atoms && (
{counts.total} kunskapsområden · {counts.active} aktiva för ditt företag
)} {atoms === null && (
{[0, 1, 2, 3].map((i) => ( ))}
)} {atoms && atoms.length === 0 && ( )} {atoms && atoms.length > 0 && TIER_ORDER.filter((tier) => grouped[tier].length > 0).map((tier) => (

{TIER_SECTION[tier].title}

{TIER_SECTION[tier].blurb}

    {grouped[tier].map((atom) => { const isOpen = expandedId === atom.id const isLoading = loadingBody === atom.id const body = bodies[atom.id] const dormant = !atom.active return (
  • {isOpen && (
    {isLoading && (
    Läser in…
    )} {!isLoading && body !== undefined && body.length > 0 && (
    {body}
    )} {!isLoading && body !== undefined && body.length === 0 && (

    Innehållet kunde inte läsas in.

    )}
    )}
  • ) })}
))}
) }