@burdenoff/microfe-subscriberbot 2026.706.1 → 2026.706.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/subscriberbot/SubscriberBotRoutes.js +14 -6
- package/dist/subscriberbot/SubscriberBotRoutes.js.map +1 -1
- package/dist/subscriberbot/components/Modal.js +51 -0
- package/dist/subscriberbot/components/Modal.js.map +1 -0
- package/dist/subscriberbot/components/index.js +7 -0
- package/dist/subscriberbot/components/viz.js +196 -0
- package/dist/subscriberbot/components/viz.js.map +1 -0
- package/dist/subscriberbot/hooks/useSubscriberBotApi.js +364 -161
- package/dist/subscriberbot/hooks/useSubscriberBotApi.js.map +1 -1
- package/dist/subscriberbot/pages/AssetVaultPage.js +182 -0
- package/dist/subscriberbot/pages/AssetVaultPage.js.map +1 -0
- package/dist/subscriberbot/pages/DashboardPage.js +272 -199
- package/dist/subscriberbot/pages/DashboardPage.js.map +1 -1
- package/dist/subscriberbot/pages/InboxPage.js +682 -126
- package/dist/subscriberbot/pages/InboxPage.js.map +1 -1
- package/dist/subscriberbot/pages/InsightsPage.js +220 -0
- package/dist/subscriberbot/pages/InsightsPage.js.map +1 -0
- package/dist/subscriberbot/pages/InvoicesPage.js +592 -81
- package/dist/subscriberbot/pages/InvoicesPage.js.map +1 -1
- package/dist/subscriberbot/pages/WorkflowsPage.js +344 -169
- package/dist/subscriberbot/pages/WorkflowsPage.js.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentDetailPage.js +401 -116
- package/dist/subscriberbot/pages/agents/AgentDetailPage.js.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentsListPage.js +185 -93
- package/dist/subscriberbot/pages/agents/AgentsListPage.js.map +1 -1
- package/dist/subscriberbot/pages/providers/ProviderDetailPage.js +735 -135
- package/dist/subscriberbot/pages/providers/ProviderDetailPage.js.map +1 -1
- package/dist/subscriberbot/pages/providers/ProviderNewPage.js +217 -153
- package/dist/subscriberbot/pages/providers/ProviderNewPage.js.map +1 -1
- package/dist/subscriberbot/pages/providers/ProvidersListPage.js +216 -111
- package/dist/subscriberbot/pages/providers/ProvidersListPage.js.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js +902 -250
- package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipNewPage.js +242 -192
- package/dist/subscriberbot/pages/relationships/RelationshipNewPage.js.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipsListPage.js +174 -131
- package/dist/subscriberbot/pages/relationships/RelationshipsListPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentDetailPage.js","names":[],"sources":["../../../../src/subscriberbot/pages/agents/AgentDetailPage.tsx"],"sourcesContent":["/**\n * Agent detail\n * @module subscriberbot/pages/agents\n *\n * A single AI agent: its kind, posture, last run, and the autonomous policies\n * that bound it. Reads `subAgent(id)`; changes posture via `subSetAgentStatus`.\n */\n\nimport { useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { ArrowLeft, Bot, ShieldCheck } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView, Section } from '../../components/StateViews';\nimport { useSubscriberBot } from '../../context/SubscriberBotContext';\nimport { useAgent, useSetAgentStatus } from '../../hooks/useSubscriberBotApi';\nimport { formatEnumLabel, formatRelativeTime } from '../../utils/formatters';\nimport type { SubAgentStatus } from '../../types';\n\nconst STATUS_OPTIONS: SubAgentStatus[] = ['DISABLED', 'SUGGEST_ONLY', 'AUTONOMOUS'];\n\nexport function AgentDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useSubscriberBot();\n const { data: agent, isLoading, isError, refetch } = useAgent(id);\n const setAgentStatus = useSetAgentStatus();\n const [actionError, setActionError] = useState<string | null>(null);\n\n if (isLoading) {\n return (\n <PageLayout title=\"Agent\">\n <LoadingView label=\"Loading agent…\" />\n </PageLayout>\n );\n }\n\n if (isError) {\n return (\n <PageLayout title=\"Agent\">\n <ErrorView message=\"Could not load this agent.\" onRetry={() => void refetch()} />\n </PageLayout>\n );\n }\n\n if (!agent) {\n return (\n <PageLayout title=\"Agent\">\n <EmptyState\n illustration=\"empty-data\"\n title=\"Agent not found\"\n description=\"It may have been removed, or you may not have access to it.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/agents')}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n Back to agents\n </button>\n }\n />\n </PageLayout>\n );\n }\n\n const a = agent;\n\n return (\n <PageLayout\n title={a.name}\n description={`${formatEnumLabel(a.kind)} agent`}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/agents')}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back\n </button>\n }\n >\n <div className=\"flex flex-wrap items-center gap-3\">\n <span className=\"flex size-10 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <Bot className=\"h-5 w-5\" />\n </span>\n <StatusBadge status={a.status} />\n <span className=\"text-sm text-text-secondary\">\n {a.lastRunAt ? `Last run ${formatRelativeTime(a.lastRunAt)}` : 'Never run'}\n </span>\n </div>\n\n {a.description && (\n <p className=\"mt-4 max-w-3xl text-sm text-text-secondary\">{a.description}</p>\n )}\n\n {actionError && (\n <div className=\"mt-4 rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text\">\n {actionError}\n </div>\n )}\n\n {/* Posture control */}\n <div className=\"mt-6 max-w-sm\">\n <label\n htmlFor=\"agent-posture\"\n className=\"mb-1 block text-xs font-medium text-text-secondary\"\n >\n Operating posture\n </label>\n <select\n id=\"agent-posture\"\n value={a.status}\n disabled={setAgentStatus.isPending}\n onChange={(e) => {\n setActionError(null);\n setAgentStatus.mutate(\n { id: a.id, status: e.target.value as SubAgentStatus },\n {\n onError: (err) =>\n setActionError(err instanceof Error ? err.message : 'Could not change posture.'),\n }\n );\n }}\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] disabled:opacity-50\"\n >\n {STATUS_OPTIONS.map((s) => (\n <option key={s} value={s}>\n {formatEnumLabel(s)}\n </option>\n ))}\n </select>\n <p className=\"mt-1.5 text-xs text-text-secondary\">\n Disabled never acts; Suggest-only proposes; Autonomous acts within its policies.\n </p>\n </div>\n\n {/* Policies */}\n <Section title=\"Policies\" description=\"The autonomous rules that bound this agent\">\n {!a.policies || a.policies.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No policies\"\n description=\"Policies define what this agent may do autonomously, and what it must alert on.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n {a.policies.map((policy) => (\n <GlassCard key={policy.id} treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-start justify-between gap-2\">\n <p className=\"text-sm font-medium text-text-primary\">{policy.name}</p>\n <StatusBadge status={policy.isEnabled ? 'ACTIVE' : 'DISABLED'} />\n </div>\n <p className=\"mt-1 flex items-center gap-1.5 text-xs text-text-secondary\">\n <ShieldCheck className=\"h-3.5 w-3.5\" /> Action: {formatEnumLabel(policy.action)}\n </p>\n {policy.description && (\n <p className=\"mt-2 text-xs text-text-secondary\">{policy.description}</p>\n )}\n <p className=\"mt-2 text-xs text-text-secondary\">\n Triggered {policy.triggerCount}×\n {policy.lastTriggeredAt\n ? ` · last ${formatRelativeTime(policy.lastTriggeredAt)}`\n : ''}\n </p>\n </GlassCard>\n ))}\n </div>\n )}\n </Section>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AgentDetailPage.js","names":[],"sources":["../../../../src/subscriberbot/pages/agents/AgentDetailPage.tsx"],"sourcesContent":["/**\n * Agent detail\n * @module subscriberbot/pages/agents\n *\n * A single AI agent: its kind, posture, last run, and the autonomous policies\n * that bound it. Reads `subAgent(id)`; changes posture via `subSetAgentStatus`.\n * The \"Run history\" timeline is derived from the agent's real `lastRunAt` and\n * each policy's real `lastTriggeredAt` — never mocked.\n */\n\nimport { useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n Bot,\n Search,\n RefreshCw,\n DollarSign,\n Mail,\n Building2,\n ShieldCheck,\n Handshake,\n ArrowLeftRight,\n ConciergeBell,\n Plus,\n Clock,\n Zap,\n ListChecks,\n History,\n type LucideIcon,\n} from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n StatusBadge,\n EmptyState,\n Modal,\n MetricsCard,\n Timeline,\n LoadingView,\n ErrorView,\n Section,\n type TimelineItem,\n} from '../../components';\nimport { useSubscriberBot } from '../../context/SubscriberBotContext';\nimport { useAgent, useSetAgentStatus, useCreatePolicy } from '../../hooks/useSubscriberBotApi';\nimport { formatEnumLabel, formatRelativeTime } from '../../utils/formatters';\nimport type { SubAgentKind, SubAgentStatus, SubPolicyAction, SubPolicy } from '../../types';\n\nconst STATUS_OPTIONS: SubAgentStatus[] = ['DISABLED', 'SUGGEST_ONLY', 'AUTONOMOUS'];\n\nconst POLICY_ACTIONS: SubPolicyAction[] = [\n 'ALERT',\n 'CREATE_TASK',\n 'RENEW',\n 'CANCEL',\n 'PAUSE',\n 'UPGRADE',\n 'DOWNGRADE',\n];\n\nconst KIND_ICON: Record<SubAgentKind, LucideIcon> = {\n DISCOVERY: Search,\n RENEWAL: RefreshCw,\n COST: DollarSign,\n NEWSLETTER: Mail,\n VENDOR: Building2,\n COMPLIANCE: ShieldCheck,\n NEGOTIATION: Handshake,\n MIGRATION: ArrowLeftRight,\n CONCIERGE: ConciergeBell,\n};\n\n/** Map a policy action to a timeline tone (semantic only). */\nfunction actionTone(action: SubPolicyAction): NonNullable<TimelineItem['tone']> {\n switch (action) {\n case 'RENEW':\n case 'UPGRADE':\n return 'positive';\n case 'CANCEL':\n case 'PAUSE':\n case 'DOWNGRADE':\n case 'ALERT':\n return 'warning';\n default:\n return 'default';\n }\n}\n\nconst inputCls =\n 'w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\nconst labelCls = 'mb-1 block text-xs font-medium text-text-secondary';\n\n// ---------------------------------------------------------------------------\n// Read-only enabled indicator (there is no policy-toggle mutation on the svc\n// contract, so this reflects the real `isEnabled` state without pretending to\n// mutate it).\n// ---------------------------------------------------------------------------\nfunction EnabledPill({ enabled }: { enabled: boolean }) {\n return (\n <span\n className={\n enabled\n ? 'inline-flex items-center gap-1.5 rounded-full bg-status-success-bg-subtle px-2.5 py-0.5 text-xs font-medium text-status-success-text'\n : 'inline-flex items-center gap-1.5 rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary'\n }\n >\n <span\n aria-hidden\n className=\"size-1.5 rounded-full bg-current\"\n style={{ opacity: enabled ? 1 : 0.6 }}\n />\n {enabled ? 'Enabled' : 'Disabled'}\n </span>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Add policy modal → subCreatePolicy\n// ---------------------------------------------------------------------------\n\nfunction AddPolicyModal({ agentId, onClose }: { agentId: string; onClose: () => void }) {\n const create = useCreatePolicy();\n const [name, setName] = useState('');\n const [action, setAction] = useState<SubPolicyAction>('ALERT');\n const [description, setDescription] = useState('');\n const [condition, setCondition] = useState('');\n const [isEnabled, setIsEnabled] = useState(true);\n const [formError, setFormError] = useState<string | null>(null);\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n setFormError(null);\n if (!name.trim()) {\n setFormError('A policy name is required.');\n return;\n }\n let parsedCondition: Record<string, unknown> | undefined;\n if (condition.trim()) {\n try {\n parsedCondition = JSON.parse(condition) as Record<string, unknown>;\n } catch {\n setFormError('Condition must be valid JSON (e.g. {\"amountOver\": 100}).');\n return;\n }\n }\n create.mutate(\n {\n agentId,\n name: name.trim(),\n action,\n isEnabled,\n ...(description.trim() ? { description: description.trim() } : {}),\n ...(parsedCondition ? { condition: parsedCondition } : {}),\n },\n {\n onSuccess: () => onClose(),\n onError: (err) =>\n setFormError(err instanceof Error ? err.message : 'Could not add policy.'),\n }\n );\n };\n\n return (\n <Modal title=\"Add policy\" onClose={onClose} maxWidthClassName=\"max-w-xl\">\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {formError && (\n <div className=\"rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text\">\n {formError}\n </div>\n )}\n <div>\n <label htmlFor=\"policy-name\" className={labelCls}>\n Name <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n id=\"policy-name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. Alert on price hike over 20%\"\n className={inputCls}\n required\n />\n </div>\n <div>\n <label htmlFor=\"policy-action\" className={labelCls}>\n Action\n </label>\n <select\n id=\"policy-action\"\n value={action}\n onChange={(e) => setAction(e.target.value as SubPolicyAction)}\n className={inputCls}\n >\n {POLICY_ACTIONS.map((a) => (\n <option key={a} value={a}>\n {a.replace(/_/g, ' ')}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor=\"policy-condition\" className={labelCls}>\n Condition <span className=\"text-text-secondary\">(optional JSON)</span>\n </label>\n <textarea\n id=\"policy-condition\"\n value={condition}\n onChange={(e) => setCondition(e.target.value)}\n rows={3}\n placeholder={'{ \"priceIncreasePct\": 20 }'}\n className={`${inputCls} font-mono`}\n />\n </div>\n <div>\n <label htmlFor=\"policy-desc\" className={labelCls}>\n Description\n </label>\n <textarea\n id=\"policy-desc\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n className={inputCls}\n />\n </div>\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={isEnabled}\n onChange={(e) => setIsEnabled(e.target.checked)}\n className=\"size-4 rounded border-border-subtle\"\n />\n Enabled\n </label>\n <div className=\"flex justify-end gap-3\">\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={create.isPending}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50\"\n >\n {create.isPending ? 'Adding…' : 'Add policy'}\n </button>\n </div>\n </form>\n </Modal>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Policy card\n// ---------------------------------------------------------------------------\nfunction PolicyCard({ policy }: { policy: SubPolicy }) {\n return (\n <GlassCard treatment=\"metric\" className=\"flex flex-col p-4\">\n <div className=\"flex items-start justify-between gap-2\">\n <p className=\"text-sm font-semibold text-text-primary\">{policy.name}</p>\n <EnabledPill enabled={policy.isEnabled} />\n </div>\n <div className=\"mt-2 flex flex-wrap items-center gap-2\">\n <span className=\"inline-flex items-center gap-1.5 rounded-md bg-[var(--color-accent-soft)] px-2 py-0.5 text-[11px] font-medium text-text-primary\">\n <ShieldCheck className=\"h-3 w-3\" aria-hidden /> {formatEnumLabel(policy.action)}\n </span>\n <span className=\"inline-flex items-center gap-1 rounded-md bg-bg-sunken px-2 py-0.5 text-[11px] font-medium text-text-secondary tabular-nums\">\n Triggered {policy.triggerCount}×\n </span>\n </div>\n {policy.description && (\n <p className=\"mt-2 line-clamp-2 text-xs text-text-secondary\">{policy.description}</p>\n )}\n <p className=\"mt-2 flex items-center gap-1.5 text-xs text-text-secondary\">\n <Clock className=\"h-3.5 w-3.5\" aria-hidden />\n {policy.lastTriggeredAt\n ? `Last triggered ${formatRelativeTime(policy.lastTriggeredAt)}`\n : 'Not yet triggered'}\n </p>\n </GlassCard>\n );\n}\n\nexport function AgentDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useSubscriberBot();\n const { data: agent, isLoading, isError, refetch } = useAgent(id);\n const setAgentStatus = useSetAgentStatus();\n const [actionError, setActionError] = useState<string | null>(null);\n const [showAddPolicy, setShowAddPolicy] = useState(false);\n\n if (isLoading) {\n return (\n <PageLayout title=\"Agent\">\n <LoadingView label=\"Loading agent…\" />\n </PageLayout>\n );\n }\n\n if (isError) {\n return (\n <PageLayout title=\"Agent\">\n <ErrorView message=\"Could not load this agent.\" onRetry={() => void refetch()} />\n </PageLayout>\n );\n }\n\n if (!agent) {\n return (\n <PageLayout title=\"Agent\">\n <EmptyState\n illustration=\"empty-data\"\n title=\"Agent not found\"\n description=\"It may have been removed, or you may not have access to it.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/agents')}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n Back to agents\n </button>\n }\n />\n </PageLayout>\n );\n }\n\n const a = agent;\n const Icon = KIND_ICON[a.kind] ?? Bot;\n const policies = a.policies ?? [];\n const enabledCount = policies.filter((p) => p.isEnabled).length;\n const totalTriggers = policies.reduce((s, p) => s + (p.triggerCount || 0), 0);\n\n // --- Run history: derived from real timestamps, sorted most-recent first --\n const events: Array<{ ts: number; item: TimelineItem }> = [];\n if (a.lastRunAt) {\n events.push({\n ts: new Date(a.lastRunAt).getTime(),\n item: {\n title: 'Agent run',\n sub: 'Latest autonomous cycle',\n meta: formatRelativeTime(a.lastRunAt),\n tone: 'positive',\n },\n });\n }\n for (const p of policies) {\n if (p.lastTriggeredAt) {\n events.push({\n ts: new Date(p.lastTriggeredAt).getTime(),\n item: {\n title: p.name,\n sub: `${formatEnumLabel(p.action)} · fired ${p.triggerCount}×`,\n meta: formatRelativeTime(p.lastTriggeredAt),\n tone: actionTone(p.action),\n },\n });\n }\n }\n events.sort((x, y) => y.ts - x.ts);\n const timelineItems = events.map((e) => e.item);\n\n return (\n <PageLayout\n title={a.name}\n description={`${formatEnumLabel(a.kind)} agent`}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/agents')}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back\n </button>\n }\n >\n {/* Identity + posture header */}\n <GlassCard treatment=\"glass\" glow={a.status === 'AUTONOMOUS'} className=\"p-5\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"flex items-start gap-4\">\n <span className=\"flex size-12 shrink-0 items-center justify-center rounded-xl bg-[var(--color-accent-soft)] text-text-primary\">\n <Icon className=\"h-6 w-6\" />\n </span>\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <p className=\"text-lg font-semibold text-text-primary\">{a.name}</p>\n <StatusBadge status={a.status} />\n </div>\n <p className=\"mt-1 flex items-center gap-1.5 text-xs text-text-secondary\">\n <Clock className=\"h-3.5 w-3.5\" aria-hidden />\n {a.lastRunAt ? `Last run ${formatRelativeTime(a.lastRunAt)}` : 'Never run'}\n </p>\n {a.description && (\n <p className=\"mt-2 max-w-2xl text-sm text-text-secondary\">{a.description}</p>\n )}\n </div>\n </div>\n\n {/* Posture control */}\n <div className=\"w-full sm:w-56 sm:shrink-0\">\n <label\n htmlFor=\"agent-posture\"\n className=\"mb-1 block text-xs font-medium text-text-secondary\"\n >\n Operating posture\n </label>\n <select\n id=\"agent-posture\"\n value={a.status}\n disabled={setAgentStatus.isPending}\n onChange={(e) => {\n setActionError(null);\n setAgentStatus.mutate(\n { id: a.id, status: e.target.value as SubAgentStatus },\n {\n onError: (err) =>\n setActionError(\n err instanceof Error ? err.message : 'Could not change posture.'\n ),\n }\n );\n }}\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] disabled:opacity-50\"\n >\n {STATUS_OPTIONS.map((s) => (\n <option key={s} value={s}>\n {formatEnumLabel(s)}\n </option>\n ))}\n </select>\n <p className=\"mt-1.5 text-[11px] leading-snug text-text-secondary\">\n Disabled never acts · Suggest-only proposes · Autonomous acts within its policies.\n </p>\n </div>\n </div>\n </GlassCard>\n\n {actionError && (\n <div className=\"mt-4 rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text\">\n {actionError}\n </div>\n )}\n\n {/* Policy stats (computed) */}\n <div className=\"mt-6 grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <MetricsCard\n icon={<ListChecks className=\"h-5 w-5\" />}\n value={policies.length}\n label=\"Policies\"\n />\n <MetricsCard\n icon={<ShieldCheck className=\"h-5 w-5\" />}\n value={enabledCount}\n label=\"Enabled\"\n />\n <MetricsCard\n icon={<Zap className=\"h-5 w-5\" />}\n value={totalTriggers}\n label=\"Total triggers\"\n />\n </div>\n\n {/* Policies + Run history */}\n <div className=\"mt-8 grid grid-cols-1 gap-6 lg:grid-cols-3\">\n <div className=\"lg:col-span-2\">\n <Section\n title=\"Policies\"\n description=\"The autonomous rules that bound this agent\"\n action={\n <button\n type=\"button\"\n onClick={() => setShowAddPolicy(true)}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <Plus className=\"h-3.5 w-3.5\" /> Add policy\n </button>\n }\n >\n {policies.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No policies\"\n description=\"Policies define what this agent may do autonomously, and what it must alert on.\"\n action={\n <button\n type=\"button\"\n onClick={() => setShowAddPolicy(true)}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n Add policy\n </button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n {policies.map((policy) => (\n <PolicyCard key={policy.id} policy={policy} />\n ))}\n </div>\n )}\n </Section>\n </div>\n\n <div className=\"lg:col-span-1\">\n <Section title=\"Run history\" description=\"Recent agent activity\">\n <GlassCard treatment=\"metric\" className=\"p-5\">\n {timelineItems.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center gap-2 py-6 text-center\">\n <History className=\"h-8 w-8 text-text-secondary\" aria-hidden />\n <p className=\"text-sm font-medium text-text-primary\">No runs yet</p>\n <p className=\"text-xs text-text-secondary\">\n Activity appears here once the agent runs or a policy fires.\n </p>\n </div>\n ) : (\n <Timeline items={timelineItems} />\n )}\n </GlassCard>\n </Section>\n </div>\n </div>\n\n {showAddPolicy && <AddPolicyModal agentId={a.id} onClose={() => setShowAddPolicy(false)} />}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import { useSubscriberBot as e } from "../../context/SubscriberBotContext.js";
|
|
2
2
|
import { PageLayout as t } from "../../components/PageLayout.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
3
|
+
import { MetricsCard as n } from "../../components/MetricsCard.js";
|
|
4
|
+
import { formatEnumLabel as r, formatRelativeTime as i } from "../../utils/formatters.js";
|
|
5
|
+
import { StatusBadge as a } from "../../components/StatusBadge.js";
|
|
6
|
+
import { EmptyState as o } from "../../components/EmptyState.js";
|
|
7
|
+
import { ErrorView as s, LoadingView as c } from "../../components/StateViews.js";
|
|
8
|
+
import { Donut as l, Legend as u } from "../../components/viz.js";
|
|
9
|
+
import "../../components/index.js";
|
|
10
|
+
import { useAgents as d, useSetAgentStatus as f } from "../../hooks/useSubscriberBotApi.js";
|
|
11
|
+
import { useState as p } from "react";
|
|
12
|
+
import { ArrowLeftRight as m, Bot as h, Building2 as g, Clock as _, ConciergeBell as v, DollarSign as y, Handshake as b, Mail as x, Plus as S, RefreshCw as C, Search as w, ShieldCheck as T, Sparkles as E, Zap as D } from "lucide-react";
|
|
13
|
+
import { Fragment as O, jsx as k, jsxs as A } from "react/jsx-runtime";
|
|
14
|
+
import { GlassCard as j, PagePurpose as M } from "@burdenoff/fe-libs/ui";
|
|
12
15
|
//#region src/subscriberbot/pages/agents/AgentsListPage.tsx
|
|
13
|
-
var
|
|
16
|
+
var N = [
|
|
14
17
|
{
|
|
15
18
|
value: "",
|
|
16
19
|
label: "All kinds"
|
|
@@ -51,128 +54,217 @@ var g = [
|
|
|
51
54
|
value: "CONCIERGE",
|
|
52
55
|
label: "Concierge"
|
|
53
56
|
}
|
|
54
|
-
],
|
|
57
|
+
], P = [
|
|
55
58
|
"DISABLED",
|
|
56
59
|
"SUGGEST_ONLY",
|
|
57
60
|
"AUTONOMOUS"
|
|
58
|
-
]
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
], F = {
|
|
62
|
+
DISCOVERY: w,
|
|
63
|
+
RENEWAL: C,
|
|
64
|
+
COST: y,
|
|
65
|
+
NEWSLETTER: x,
|
|
66
|
+
VENDOR: g,
|
|
67
|
+
COMPLIANCE: T,
|
|
68
|
+
NEGOTIATION: b,
|
|
69
|
+
MIGRATION: m,
|
|
70
|
+
CONCIERGE: v
|
|
71
|
+
}, I = {
|
|
72
|
+
AUTONOMOUS: "var(--color-status-success-text)",
|
|
73
|
+
SUGGEST_ONLY: "var(--color-accent-indigo)",
|
|
74
|
+
DISABLED: "var(--color-text-secondary)"
|
|
75
|
+
}, L = 1440 * 60 * 1e3;
|
|
76
|
+
function R() {
|
|
77
|
+
let { navigateTo: m } = e(), [g, v] = p(""), [y, b] = p(""), { data: x, isLoading: C, isError: w, refetch: T } = d({
|
|
78
|
+
kind: g || void 0,
|
|
79
|
+
status: y || void 0
|
|
80
|
+
}), R = f(), z = x ?? [], B = {
|
|
81
|
+
AUTONOMOUS: 0,
|
|
82
|
+
SUGGEST_ONLY: 0,
|
|
83
|
+
DISABLED: 0
|
|
84
|
+
}, V = 0, H = Date.now();
|
|
85
|
+
for (let e of z) B[e.status] += 1, e.lastRunAt && H - new Date(e.lastRunAt).getTime() <= 7 * L && (V += 1);
|
|
86
|
+
let U = P.map((e) => ({
|
|
87
|
+
label: r(e),
|
|
88
|
+
value: B[e],
|
|
89
|
+
color: I[e]
|
|
90
|
+
})).filter((e) => e.value > 0);
|
|
91
|
+
return /* @__PURE__ */ A(t, {
|
|
65
92
|
title: "AI Agents",
|
|
66
93
|
description: "Specialized agents that automate your recurring portfolio",
|
|
67
|
-
actions: /* @__PURE__ */
|
|
94
|
+
actions: /* @__PURE__ */ A("button", {
|
|
68
95
|
type: "button",
|
|
69
|
-
onClick: () =>
|
|
96
|
+
onClick: () => m("/agents/new"),
|
|
70
97
|
className: "inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90",
|
|
71
|
-
children: [/* @__PURE__ */
|
|
98
|
+
children: [/* @__PURE__ */ k(S, { className: "h-4 w-4" }), " New agent"]
|
|
72
99
|
}),
|
|
73
100
|
children: [
|
|
74
|
-
/* @__PURE__ */
|
|
101
|
+
/* @__PURE__ */ k(M, {
|
|
75
102
|
className: "mb-4",
|
|
76
103
|
children: "Agents propose; your policies decide what they may do autonomously. Set each agent's posture — Disabled, Suggest-only, or Autonomous — and open one to review its policies."
|
|
77
104
|
}),
|
|
78
|
-
/* @__PURE__ */
|
|
105
|
+
/* @__PURE__ */ A("div", {
|
|
79
106
|
className: "flex flex-wrap items-center gap-3",
|
|
80
|
-
children: [/* @__PURE__ */
|
|
81
|
-
|
|
82
|
-
|
|
107
|
+
children: [/* @__PURE__ */ k("select", {
|
|
108
|
+
"aria-label": "Filter by kind",
|
|
109
|
+
value: g,
|
|
110
|
+
onChange: (e) => v(e.target.value),
|
|
83
111
|
className: "rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
|
|
84
|
-
children:
|
|
112
|
+
children: N.map((e) => /* @__PURE__ */ k("option", {
|
|
85
113
|
value: e.value,
|
|
86
114
|
children: e.label
|
|
87
115
|
}, e.value))
|
|
88
|
-
}), /* @__PURE__ */
|
|
89
|
-
|
|
90
|
-
|
|
116
|
+
}), /* @__PURE__ */ A("select", {
|
|
117
|
+
"aria-label": "Filter by posture",
|
|
118
|
+
value: y,
|
|
119
|
+
onChange: (e) => b(e.target.value),
|
|
91
120
|
className: "rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
|
|
92
|
-
children: [/* @__PURE__ */
|
|
121
|
+
children: [/* @__PURE__ */ k("option", {
|
|
93
122
|
value: "",
|
|
94
123
|
children: "Any posture"
|
|
95
|
-
}),
|
|
124
|
+
}), P.map((e) => /* @__PURE__ */ k("option", {
|
|
96
125
|
value: e,
|
|
97
|
-
children:
|
|
126
|
+
children: r(e)
|
|
98
127
|
}, e))]
|
|
99
128
|
})]
|
|
100
129
|
}),
|
|
101
|
-
|
|
130
|
+
C ? /* @__PURE__ */ k(c, { label: "Loading agents…" }) : w ? /* @__PURE__ */ k(s, {
|
|
102
131
|
message: "Could not load agents.",
|
|
103
|
-
onRetry: () => void
|
|
104
|
-
}) :
|
|
132
|
+
onRetry: () => void T()
|
|
133
|
+
}) : z.length === 0 ? /* @__PURE__ */ k("div", {
|
|
105
134
|
className: "mt-6",
|
|
106
|
-
children: /* @__PURE__ */
|
|
135
|
+
children: /* @__PURE__ */ k(o, {
|
|
107
136
|
illustration: "empty-data",
|
|
108
|
-
icon: /* @__PURE__ */
|
|
137
|
+
icon: /* @__PURE__ */ k(h, { className: "h-12 w-12" }),
|
|
109
138
|
title: "No agents yet",
|
|
110
139
|
description: "Create your first AI agent to start automating discovery, renewals, or cost optimization.",
|
|
111
|
-
action: /* @__PURE__ */
|
|
140
|
+
action: /* @__PURE__ */ k("button", {
|
|
112
141
|
type: "button",
|
|
113
|
-
onClick: () =>
|
|
142
|
+
onClick: () => m("/agents/new"),
|
|
114
143
|
className: "rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90",
|
|
115
144
|
children: "New agent"
|
|
116
145
|
})
|
|
117
146
|
})
|
|
118
|
-
}) : /* @__PURE__ */
|
|
147
|
+
}) : /* @__PURE__ */ A(O, { children: [/* @__PURE__ */ A("div", {
|
|
148
|
+
className: "mt-6 grid grid-cols-1 gap-4 lg:grid-cols-3",
|
|
149
|
+
children: [/* @__PURE__ */ A(j, {
|
|
150
|
+
treatment: "insight",
|
|
151
|
+
className: "p-5 lg:col-span-1",
|
|
152
|
+
children: [
|
|
153
|
+
/* @__PURE__ */ k("p", {
|
|
154
|
+
className: "text-sm font-semibold text-text-primary",
|
|
155
|
+
children: "Fleet posture"
|
|
156
|
+
}),
|
|
157
|
+
/* @__PURE__ */ k("p", {
|
|
158
|
+
className: "mt-0.5 text-xs text-text-secondary",
|
|
159
|
+
children: "How your agents are allowed to act"
|
|
160
|
+
}),
|
|
161
|
+
/* @__PURE__ */ A("div", {
|
|
162
|
+
className: "mt-4 flex items-center gap-5",
|
|
163
|
+
children: [/* @__PURE__ */ k(l, {
|
|
164
|
+
data: U,
|
|
165
|
+
size: 132,
|
|
166
|
+
thickness: 16,
|
|
167
|
+
centerLabel: String(z.length),
|
|
168
|
+
centerSub: z.length === 1 ? "agent" : "agents"
|
|
169
|
+
}), /* @__PURE__ */ k(u, {
|
|
170
|
+
className: "flex-1",
|
|
171
|
+
data: U
|
|
172
|
+
})]
|
|
173
|
+
})
|
|
174
|
+
]
|
|
175
|
+
}), /* @__PURE__ */ A("div", {
|
|
176
|
+
className: "grid grid-cols-2 gap-4 lg:col-span-2",
|
|
177
|
+
children: [
|
|
178
|
+
/* @__PURE__ */ k(n, {
|
|
179
|
+
icon: /* @__PURE__ */ k(h, { className: "h-5 w-5" }),
|
|
180
|
+
value: z.length,
|
|
181
|
+
label: "Total agents"
|
|
182
|
+
}),
|
|
183
|
+
/* @__PURE__ */ k(n, {
|
|
184
|
+
icon: /* @__PURE__ */ k(D, { className: "h-5 w-5" }),
|
|
185
|
+
value: B.AUTONOMOUS,
|
|
186
|
+
label: "Autonomous"
|
|
187
|
+
}),
|
|
188
|
+
/* @__PURE__ */ k(n, {
|
|
189
|
+
icon: /* @__PURE__ */ k(E, { className: "h-5 w-5" }),
|
|
190
|
+
value: B.SUGGEST_ONLY,
|
|
191
|
+
label: "Suggest-only"
|
|
192
|
+
}),
|
|
193
|
+
/* @__PURE__ */ k(n, {
|
|
194
|
+
icon: /* @__PURE__ */ k(_, { className: "h-5 w-5" }),
|
|
195
|
+
value: V,
|
|
196
|
+
label: "Ran in last 7 days"
|
|
197
|
+
})
|
|
198
|
+
]
|
|
199
|
+
})]
|
|
200
|
+
}), /* @__PURE__ */ k("div", {
|
|
119
201
|
className: "mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
120
|
-
children:
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
className: "
|
|
126
|
-
children: [
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
202
|
+
children: z.map((e) => {
|
|
203
|
+
let t = F[e.kind] ?? h;
|
|
204
|
+
return /* @__PURE__ */ A(j, {
|
|
205
|
+
treatment: "metric",
|
|
206
|
+
glow: e.status === "AUTONOMOUS",
|
|
207
|
+
className: "flex flex-col p-5",
|
|
208
|
+
children: [/* @__PURE__ */ A("button", {
|
|
209
|
+
type: "button",
|
|
210
|
+
onClick: () => m(`/agents/${e.id}`),
|
|
211
|
+
className: "rounded-lg text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]",
|
|
212
|
+
"aria-label": `Open ${e.name}`,
|
|
213
|
+
children: [
|
|
214
|
+
/* @__PURE__ */ A("div", {
|
|
215
|
+
className: "flex items-start justify-between gap-2",
|
|
216
|
+
children: [/* @__PURE__ */ k("span", {
|
|
217
|
+
className: "flex size-10 items-center justify-center rounded-xl bg-[var(--color-accent-soft)] text-text-primary",
|
|
218
|
+
children: /* @__PURE__ */ k(t, { className: "h-5 w-5" })
|
|
219
|
+
}), /* @__PURE__ */ k(a, { status: e.status })]
|
|
220
|
+
}),
|
|
221
|
+
/* @__PURE__ */ k("p", {
|
|
222
|
+
className: "mt-3 truncate text-sm font-semibold text-text-primary",
|
|
223
|
+
children: e.name
|
|
224
|
+
}),
|
|
225
|
+
/* @__PURE__ */ A("p", {
|
|
226
|
+
className: "text-xs text-text-secondary",
|
|
227
|
+
children: [r(e.kind), " agent"]
|
|
228
|
+
}),
|
|
229
|
+
/* @__PURE__ */ k("p", {
|
|
230
|
+
className: "mt-2 line-clamp-2 min-h-[2rem] text-xs text-text-secondary",
|
|
231
|
+
children: e.description || "No description provided."
|
|
232
|
+
}),
|
|
233
|
+
/* @__PURE__ */ A("p", {
|
|
234
|
+
className: "mt-3 flex items-center gap-1.5 text-xs text-text-secondary",
|
|
235
|
+
children: [/* @__PURE__ */ k(_, {
|
|
236
|
+
className: "h-3.5 w-3.5",
|
|
237
|
+
"aria-hidden": !0
|
|
238
|
+
}), e.lastRunAt ? `Last run ${i(e.lastRunAt)}` : "Never run"]
|
|
239
|
+
})
|
|
240
|
+
]
|
|
241
|
+
}), /* @__PURE__ */ A("div", {
|
|
242
|
+
className: "mt-4 border-t border-border-subtle pt-3",
|
|
243
|
+
children: [/* @__PURE__ */ k("label", {
|
|
244
|
+
className: "mb-1 block text-[10px] font-medium uppercase tracking-wide text-text-secondary",
|
|
245
|
+
children: "Posture"
|
|
246
|
+
}), /* @__PURE__ */ k("select", {
|
|
247
|
+
"aria-label": `Posture for ${e.name}`,
|
|
248
|
+
value: e.status,
|
|
249
|
+
disabled: R.isPending,
|
|
250
|
+
onChange: (t) => R.mutate({
|
|
251
|
+
id: e.id,
|
|
252
|
+
status: t.target.value
|
|
253
|
+
}),
|
|
254
|
+
className: "w-full rounded-lg border border-border-subtle bg-bg-surface px-2 py-1.5 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] disabled:opacity-50",
|
|
255
|
+
children: P.map((e) => /* @__PURE__ */ k("option", {
|
|
256
|
+
value: e,
|
|
257
|
+
children: r(e)
|
|
258
|
+
}, e))
|
|
259
|
+
})]
|
|
168
260
|
})]
|
|
169
|
-
})
|
|
170
|
-
}
|
|
171
|
-
})
|
|
261
|
+
}, e.id);
|
|
262
|
+
})
|
|
263
|
+
})] })
|
|
172
264
|
]
|
|
173
265
|
});
|
|
174
266
|
}
|
|
175
267
|
//#endregion
|
|
176
|
-
export {
|
|
268
|
+
export { R as AgentsListPage };
|
|
177
269
|
|
|
178
270
|
//# sourceMappingURL=AgentsListPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentsListPage.js","names":[],"sources":["../../../../src/subscriberbot/pages/agents/AgentsListPage.tsx"],"sourcesContent":["/**\n * AI Agents list\n * @module subscriberbot/pages/agents\n *\n * The AI Agents Framework — specialized agents (Discovery, Renewal, Cost,\n * Newsletter, Vendor, Compliance, Negotiation, Migration, Concierge) that each\n * own a slice of the recurring-relationship problem. Filter by kind / posture.\n * Reads `subListAgents`; sets posture inline via `subSetAgentStatus`.\n */\n\nimport { useState } from 'react';\nimport { Plus, Bot } from 'lucide-react';\nimport { PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { useSubscriberBot } from '../../context/SubscriberBotContext';\nimport { useAgents, useSetAgentStatus } from '../../hooks/useSubscriberBotApi';\nimport { formatEnumLabel, formatRelativeTime } from '../../utils/formatters';\nimport type { SubAgentKind, SubAgentStatus } from '../../types';\n\nconst KIND_OPTIONS: Array<{ value: '' | SubAgentKind; label: string }> = [\n { value: '', label: 'All kinds' },\n { value: 'DISCOVERY', label: 'Discovery' },\n { value: 'RENEWAL', label: 'Renewal' },\n { value: 'COST', label: 'Cost' },\n { value: 'NEWSLETTER', label: 'Newsletter' },\n { value: 'VENDOR', label: 'Vendor' },\n { value: 'COMPLIANCE', label: 'Compliance' },\n { value: 'NEGOTIATION', label: 'Negotiation' },\n { value: 'MIGRATION', label: 'Migration' },\n { value: 'CONCIERGE', label: 'Concierge' },\n];\n\nconst STATUS_OPTIONS: SubAgentStatus[] = ['DISABLED', 'SUGGEST_ONLY', 'AUTONOMOUS'];\n\nexport function AgentsListPage() {\n const { navigateTo } = useSubscriberBot();\n const [kind, setKind] = useState<'' | SubAgentKind>('');\n const [status, setStatus] = useState<'' | SubAgentStatus>('');\n\n const { data, isLoading, isError, refetch } = useAgents({\n kind: kind || undefined,\n status: status || undefined,\n });\n const setAgentStatus = useSetAgentStatus();\n\n const agents = data ?? [];\n\n return (\n <PageLayout\n title=\"AI Agents\"\n description=\"Specialized agents that automate your recurring portfolio\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/agents/new')}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n <Plus className=\"h-4 w-4\" /> New agent\n </button>\n }\n >\n <PagePurpose className=\"mb-4\">\n Agents propose; your policies decide what they may do autonomously. Set each agent's posture\n — Disabled, Suggest-only, or Autonomous — and open one to review its policies.\n </PagePurpose>\n\n <div className=\"flex flex-wrap items-center gap-3\">\n <select\n value={kind}\n onChange={(e) => setKind(e.target.value as '' | SubAgentKind)}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n {KIND_OPTIONS.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n <select\n value={status}\n onChange={(e) => setStatus(e.target.value as '' | SubAgentStatus)}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">Any posture</option>\n {STATUS_OPTIONS.map((s) => (\n <option key={s} value={s}>\n {formatEnumLabel(s)}\n </option>\n ))}\n </select>\n </div>\n\n {isLoading ? (\n <LoadingView label=\"Loading agents…\" />\n ) : isError ? (\n <ErrorView message=\"Could not load agents.\" onRetry={() => void refetch()} />\n ) : agents.length === 0 ? (\n <div className=\"mt-6\">\n <EmptyState\n illustration=\"empty-data\"\n icon={<Bot className=\"h-12 w-12\" />}\n title=\"No agents yet\"\n description=\"Create your first AI agent to start automating discovery, renewals, or cost optimization.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/agents/new')}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n New agent\n </button>\n }\n />\n </div>\n ) : (\n <div className=\"mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {agents.map((a) => (\n <div\n key={a.id}\n className=\"flex flex-col rounded-lg border border-border-subtle bg-bg-surface p-4\"\n >\n <button\n type=\"button\"\n onClick={() => navigateTo(`/agents/${a.id}`)}\n className=\"text-left\"\n >\n <div className=\"flex items-start justify-between gap-2\">\n <span className=\"flex size-9 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <Bot className=\"h-4 w-4\" />\n </span>\n <StatusBadge status={a.status} />\n </div>\n <p className=\"mt-3 truncate text-sm font-semibold text-text-primary\">{a.name}</p>\n <p className=\"text-xs text-text-secondary\">{formatEnumLabel(a.kind)} agent</p>\n {a.description && (\n <p className=\"mt-2 line-clamp-2 text-xs text-text-secondary\">{a.description}</p>\n )}\n <p className=\"mt-2 text-xs text-text-secondary\">\n {a.lastRunAt ? `Last run ${formatRelativeTime(a.lastRunAt)}` : 'Never run'}\n </p>\n </button>\n <div className=\"mt-3 border-t border-border-subtle pt-3\">\n <label className=\"mb-1 block text-[10px] font-medium uppercase tracking-wide text-text-secondary\">\n Posture\n </label>\n <select\n value={a.status}\n disabled={setAgentStatus.isPending}\n onChange={(e) =>\n setAgentStatus.mutate({ id: a.id, status: e.target.value as SubAgentStatus })\n }\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface px-2 py-1.5 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] disabled:opacity-50\"\n >\n {STATUS_OPTIONS.map((s) => (\n <option key={s} value={s}>\n {formatEnumLabel(s)}\n </option>\n ))}\n </select>\n </div>\n </div>\n ))}\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AgentsListPage.js","names":[],"sources":["../../../../src/subscriberbot/pages/agents/AgentsListPage.tsx"],"sourcesContent":["/**\n * AI Agents list\n * @module subscriberbot/pages/agents\n *\n * The AI Agents Framework — specialized agents (Discovery, Renewal, Cost,\n * Newsletter, Vendor, Compliance, Negotiation, Migration, Concierge) that each\n * own a slice of the recurring-relationship problem. Filter by kind / posture.\n * Reads `subListAgents`; sets posture inline via `subSetAgentStatus`.\n */\n\nimport { useState } from 'react';\nimport {\n Plus,\n Bot,\n Search,\n RefreshCw,\n DollarSign,\n Mail,\n Building2,\n ShieldCheck,\n Handshake,\n ArrowLeftRight,\n ConciergeBell,\n Clock,\n Zap,\n Sparkles,\n type LucideIcon,\n} from 'lucide-react';\nimport { PagePurpose, GlassCard } from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n StatusBadge,\n EmptyState,\n MetricsCard,\n Donut,\n Legend,\n LoadingView,\n ErrorView,\n type Segment,\n} from '../../components';\nimport { useSubscriberBot } from '../../context/SubscriberBotContext';\nimport { useAgents, useSetAgentStatus } from '../../hooks/useSubscriberBotApi';\nimport { formatEnumLabel, formatRelativeTime } from '../../utils/formatters';\nimport type { SubAgentKind, SubAgentStatus } from '../../types';\n\nconst KIND_OPTIONS: Array<{ value: '' | SubAgentKind; label: string }> = [\n { value: '', label: 'All kinds' },\n { value: 'DISCOVERY', label: 'Discovery' },\n { value: 'RENEWAL', label: 'Renewal' },\n { value: 'COST', label: 'Cost' },\n { value: 'NEWSLETTER', label: 'Newsletter' },\n { value: 'VENDOR', label: 'Vendor' },\n { value: 'COMPLIANCE', label: 'Compliance' },\n { value: 'NEGOTIATION', label: 'Negotiation' },\n { value: 'MIGRATION', label: 'Migration' },\n { value: 'CONCIERGE', label: 'Concierge' },\n];\n\nconst STATUS_OPTIONS: SubAgentStatus[] = ['DISABLED', 'SUGGEST_ONLY', 'AUTONOMOUS'];\n\n/** Kind → glyph. Purely presentational; falls back to the generic Bot mark. */\nconst KIND_ICON: Record<SubAgentKind, LucideIcon> = {\n DISCOVERY: Search,\n RENEWAL: RefreshCw,\n COST: DollarSign,\n NEWSLETTER: Mail,\n VENDOR: Building2,\n COMPLIANCE: ShieldCheck,\n NEGOTIATION: Handshake,\n MIGRATION: ArrowLeftRight,\n CONCIERGE: ConciergeBell,\n};\n\n/** Posture → semantic chart colour (tokens only) for the fleet donut. */\nconst POSTURE_COLOR: Record<SubAgentStatus, string> = {\n AUTONOMOUS: 'var(--color-status-success-text)',\n SUGGEST_ONLY: 'var(--color-accent-indigo)',\n DISABLED: 'var(--color-text-secondary)',\n};\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\n\nexport function AgentsListPage() {\n const { navigateTo } = useSubscriberBot();\n const [kind, setKind] = useState<'' | SubAgentKind>('');\n const [status, setStatus] = useState<'' | SubAgentStatus>('');\n\n const { data, isLoading, isError, refetch } = useAgents({\n kind: kind || undefined,\n status: status || undefined,\n });\n const setAgentStatus = useSetAgentStatus();\n\n const agents = data ?? [];\n\n // --- Real, computed fleet aggregates (never hardcoded) --------------------\n const postureCounts: Record<SubAgentStatus, number> = {\n AUTONOMOUS: 0,\n SUGGEST_ONLY: 0,\n DISABLED: 0,\n };\n let recentlyRun = 0;\n const now = Date.now();\n for (const a of agents) {\n postureCounts[a.status] += 1;\n if (a.lastRunAt && now - new Date(a.lastRunAt).getTime() <= 7 * DAY_MS) recentlyRun += 1;\n }\n const postureData: Segment[] = STATUS_OPTIONS.map((s) => ({\n label: formatEnumLabel(s),\n value: postureCounts[s],\n color: POSTURE_COLOR[s],\n })).filter((d) => d.value > 0);\n\n return (\n <PageLayout\n title=\"AI Agents\"\n description=\"Specialized agents that automate your recurring portfolio\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/agents/new')}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n <Plus className=\"h-4 w-4\" /> New agent\n </button>\n }\n >\n <PagePurpose className=\"mb-4\">\n Agents propose; your policies decide what they may do autonomously. Set each agent's posture\n — Disabled, Suggest-only, or Autonomous — and open one to review its policies.\n </PagePurpose>\n\n <div className=\"flex flex-wrap items-center gap-3\">\n <select\n aria-label=\"Filter by kind\"\n value={kind}\n onChange={(e) => setKind(e.target.value as '' | SubAgentKind)}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n {KIND_OPTIONS.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n <select\n aria-label=\"Filter by posture\"\n value={status}\n onChange={(e) => setStatus(e.target.value as '' | SubAgentStatus)}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">Any posture</option>\n {STATUS_OPTIONS.map((s) => (\n <option key={s} value={s}>\n {formatEnumLabel(s)}\n </option>\n ))}\n </select>\n </div>\n\n {isLoading ? (\n <LoadingView label=\"Loading agents…\" />\n ) : isError ? (\n <ErrorView message=\"Could not load agents.\" onRetry={() => void refetch()} />\n ) : agents.length === 0 ? (\n <div className=\"mt-6\">\n <EmptyState\n illustration=\"empty-data\"\n icon={<Bot className=\"h-12 w-12\" />}\n title=\"No agents yet\"\n description=\"Create your first AI agent to start automating discovery, renewals, or cost optimization.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/agents/new')}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n New agent\n </button>\n }\n />\n </div>\n ) : (\n <>\n {/* --- Fleet overview: posture mix + live counts (computed) ------ */}\n <div className=\"mt-6 grid grid-cols-1 gap-4 lg:grid-cols-3\">\n <GlassCard treatment=\"insight\" className=\"p-5 lg:col-span-1\">\n <p className=\"text-sm font-semibold text-text-primary\">Fleet posture</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n How your agents are allowed to act\n </p>\n <div className=\"mt-4 flex items-center gap-5\">\n <Donut\n data={postureData}\n size={132}\n thickness={16}\n centerLabel={String(agents.length)}\n centerSub={agents.length === 1 ? 'agent' : 'agents'}\n />\n <Legend className=\"flex-1\" data={postureData} />\n </div>\n </GlassCard>\n\n <div className=\"grid grid-cols-2 gap-4 lg:col-span-2\">\n <MetricsCard\n icon={<Bot className=\"h-5 w-5\" />}\n value={agents.length}\n label=\"Total agents\"\n />\n <MetricsCard\n icon={<Zap className=\"h-5 w-5\" />}\n value={postureCounts.AUTONOMOUS}\n label=\"Autonomous\"\n />\n <MetricsCard\n icon={<Sparkles className=\"h-5 w-5\" />}\n value={postureCounts.SUGGEST_ONLY}\n label=\"Suggest-only\"\n />\n <MetricsCard\n icon={<Clock className=\"h-5 w-5\" />}\n value={recentlyRun}\n label=\"Ran in last 7 days\"\n />\n </div>\n </div>\n\n {/* --- Agent cards ------------------------------------------------ */}\n <div className=\"mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {agents.map((a) => {\n const Icon = KIND_ICON[a.kind] ?? Bot;\n return (\n <GlassCard\n key={a.id}\n treatment=\"metric\"\n glow={a.status === 'AUTONOMOUS'}\n className=\"flex flex-col p-5\"\n >\n <button\n type=\"button\"\n onClick={() => navigateTo(`/agents/${a.id}`)}\n className=\"rounded-lg text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n aria-label={`Open ${a.name}`}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <span className=\"flex size-10 items-center justify-center rounded-xl bg-[var(--color-accent-soft)] text-text-primary\">\n <Icon className=\"h-5 w-5\" />\n </span>\n <StatusBadge status={a.status} />\n </div>\n <p className=\"mt-3 truncate text-sm font-semibold text-text-primary\">\n {a.name}\n </p>\n <p className=\"text-xs text-text-secondary\">{formatEnumLabel(a.kind)} agent</p>\n <p className=\"mt-2 line-clamp-2 min-h-[2rem] text-xs text-text-secondary\">\n {a.description || 'No description provided.'}\n </p>\n <p className=\"mt-3 flex items-center gap-1.5 text-xs text-text-secondary\">\n <Clock className=\"h-3.5 w-3.5\" aria-hidden />\n {a.lastRunAt ? `Last run ${formatRelativeTime(a.lastRunAt)}` : 'Never run'}\n </p>\n </button>\n <div className=\"mt-4 border-t border-border-subtle pt-3\">\n <label className=\"mb-1 block text-[10px] font-medium uppercase tracking-wide text-text-secondary\">\n Posture\n </label>\n <select\n aria-label={`Posture for ${a.name}`}\n value={a.status}\n disabled={setAgentStatus.isPending}\n onChange={(e) =>\n setAgentStatus.mutate({ id: a.id, status: e.target.value as SubAgentStatus })\n }\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface px-2 py-1.5 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] disabled:opacity-50\"\n >\n {STATUS_OPTIONS.map((s) => (\n <option key={s} value={s}>\n {formatEnumLabel(s)}\n </option>\n ))}\n </select>\n </div>\n </GlassCard>\n );\n })}\n </div>\n </>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|