'use client' import { useAgentSheet } from './AgentSheetProvider' import { usePathname, useRouter } from 'next/navigation' import { Loader2 } from 'lucide-react' import AgentAvatar from './AgentAvatar' import { collapsedStatusLabel } from './agent-status' import { routeToIntent } from '@/lib/agent/intents/route-mapping' import { useCapability } from '@/contexts/CompanyContext' import { CAPABILITY } from '@/lib/entitlements/keys' // Floating trigger sits above the page bottom-right, opens the AgentSheet when // clicked. Hidden when the sheet is already open so the icon doesn't double up. // // Route-aware: routeToIntent(pathname) picks the right intent + intentArgs so // clicking the FAB on /invoices/abc-123 opens invoice.draft with that invoice // id (rather than the page-agnostic general.help with just the URL). The // label suffix renders "Fråga Anna om denna faktura" so the user can tell at // a glance that the agent is going to know which entity they're on. // // Reads the agent's display_name + avatar_id from the AgentSheet context so // the button reads "Fråga Anna" (with Anna's face) rather than the generic // "Fråga min assistent". // // Page-specific triggers (e.g. "Granska med assistent" on a supplier invoice) // still call useAgentSheet() directly from their own buttons because they // know exactly which entity to pass. (Per-transaction help has its own // row-level "Fråga [namn]" button in TransactionInboxCard, and the matching // "Fråga assistenten" in Dokumentinkorgen: both passing a transaction_id the // pathname-only FAB can't know.) export default function AgentTrigger({ hidden = false }: { hidden?: boolean }) { const { openAgentSheet, expandAgentSheet, isOpen, collapsed, status, identity } = useAgentSheet() const pathname = usePathname() const router = useRouter() const hasAi = useCapability(CAPABILITY.ai) // User opt-out (Inställningar → Assistenten): the sidebar entry stays, the // floating button goes. A collapsed session keeps its reopen handle even // when hidden: it's the only way back to a minimized conversation, and its // existence implies the user is actively using the assistant right now. if (hidden && !collapsed) return null // Sheet open AND visible → hide the FAB so the icon doesn't double up. When // the session is merely collapsed we KEEP the FAB: it's the handle that // brings the minimized conversation back. if (isOpen && !collapsed) return null // The page-suppression rules below apply only to a FRESH open. A collapsed // session always gets its reopen handle, regardless of page: otherwise a // conversation minimized on /chat or /bookkeeping/[id] could never be // brought back. if (!collapsed) { // The /chat surface IS the chat: a floating "Fråga …" pill on top of it // is redundant and overlaps the input. Suppress while the user is here. if (pathname?.startsWith('/chat')) return null // The verifikation editor is a dense regulatory surface (debits/credits, // BAS codes, period locks): a floating "Fråga … om denna verifikation" // pill on top of it adds noise without earning its place. Suppress on // /bookkeeping/[id] specifically; /bookkeeping (list), /bookkeeping/new, // and /bookkeeping/year-end still get the FAB. const segs = pathname?.split('/').filter(Boolean) ?? [] if (segs[0] === 'bookkeeping' && segs[1] && segs[1] !== 'year-end' && segs[1] !== 'new') { return null } } // Pre-onboarding: no agent_profile.verified_at yet. The FAB would lead // into a generic chat with no specialization. Better to hide it until // the user has finished /onboarding/agent. (A collapsed session implies the // agent is already in use, so this only gates fresh opens in practice.) if (!identity.isVerified) return null const name = identity.displayName?.trim() || 'min assistent' const dispatch = routeToIntent(pathname) // AI assistant runs on a paid cloud service. Without the capability, opening // the sheet would land the user in a chat whose send is dead. Keep the FAB // visible (it's the conversion surface) but route it to billing instead. // A minimized session used to be silent: the agent could be three tool calls // into a booking, or finished ten minutes ago, and the pill said "Fortsätt // med Anna" either way. While hidden, the status channel does the talking. const statusText = collapsed ? collapsedStatusLabel(status, name) : null const working = status.activity === 'working' || status.activity === 'detached' const finished = collapsed && status.activity === 'done' const labelText = statusText ?? (collapsed ? `Fortsätt med ${name}` : !hasAi ? `Uppgradera för att använda ${name}` : dispatch.labelSuffix ? `Fråga ${name} ${dispatch.labelSuffix}` : `Fråga ${name}`) const handleClick = () => { // Collapsed → bring the existing session back, don't start a new one. if (collapsed) { expandAgentSheet() return } if (!hasAi) { router.push('/settings/billing') return } openAgentSheet({ intentId: dispatch.intentId, intentArgs: dispatch.intentArgs, contextRef: dispatch.contextRef, }) } return ( ) }