@burdenoff/microfe-subscriberbot 2026.1023.1 → 2026.1029.1
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/admin/AdminDashboardPage.js +5 -5
- package/dist/subscriberbot/admin/AdminDashboardPage.js.map +1 -1
- package/dist/subscriberbot/components/PostureOptions.d.ts +10 -0
- package/dist/subscriberbot/components/PostureOptions.d.ts.map +1 -0
- package/dist/subscriberbot/components/PostureOptions.js +20 -0
- package/dist/subscriberbot/components/PostureOptions.js.map +1 -0
- package/dist/subscriberbot/components/index.d.ts +2 -0
- package/dist/subscriberbot/components/index.d.ts.map +1 -1
- package/dist/subscriberbot/components/index.js +1 -0
- package/dist/subscriberbot/hooks/index.d.ts +1 -0
- package/dist/subscriberbot/hooks/index.d.ts.map +1 -1
- package/dist/subscriberbot/hooks/index.js +2 -1
- package/dist/subscriberbot/hooks/useAutonomousAgentMode.d.ts +15 -0
- package/dist/subscriberbot/hooks/useAutonomousAgentMode.d.ts.map +1 -0
- package/dist/subscriberbot/hooks/useAutonomousAgentMode.js +13 -0
- package/dist/subscriberbot/hooks/useAutonomousAgentMode.js.map +1 -0
- package/dist/subscriberbot/hooks/useSubscriberBotApi.d.ts.map +1 -1
- package/dist/subscriberbot/hooks/useSubscriberBotApi.js +9 -0
- package/dist/subscriberbot/hooks/useSubscriberBotApi.js.map +1 -1
- package/dist/subscriberbot/pages/AssetVaultPage.js +16 -16
- package/dist/subscriberbot/pages/DashboardPage.js +23 -23
- package/dist/subscriberbot/pages/DashboardPage.js.map +1 -1
- package/dist/subscriberbot/pages/InboxPage.js +62 -62
- package/dist/subscriberbot/pages/InsightsPage.js +14 -14
- package/dist/subscriberbot/pages/InvoicesPage.js +66 -66
- package/dist/subscriberbot/pages/WorkflowsPage.js +20 -20
- package/dist/subscriberbot/pages/agents/AgentDetailPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentDetailPage.js +184 -182
- package/dist/subscriberbot/pages/agents/AgentDetailPage.js.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentNewPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentNewPage.js +77 -79
- package/dist/subscriberbot/pages/agents/AgentNewPage.js.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentsListPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentsListPage.js +134 -133
- package/dist/subscriberbot/pages/agents/AgentsListPage.js.map +1 -1
- package/dist/subscriberbot/pages/providers/ProviderDetailPage.js +64 -64
- package/dist/subscriberbot/pages/providers/ProviderNewPage.js +3 -3
- package/dist/subscriberbot/pages/providers/ProvidersListPage.js +18 -18
- package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js +122 -89
- package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipNewPage.js +6 -6
- package/dist/subscriberbot/pages/relationships/RelationshipsListPage.js +7 -7
- package/dist/subscriberbot/types/index.d.ts +1 -0
- package/dist/subscriberbot/types/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -4,17 +4,18 @@ import { MetricsCard as n } from "../../components/MetricsCard.js";
|
|
|
4
4
|
import { formatEnumLabel as r, formatRelativeTime as i } from "../../utils/formatters.js";
|
|
5
5
|
import { StatusBadge as a } from "../../components/StatusBadge.js";
|
|
6
6
|
import { EmptyState as o } from "../../components/EmptyState.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { useTr as s } from "../../hooks/useTr.js";
|
|
8
|
+
import { PostureOptions as c } from "../../components/PostureOptions.js";
|
|
9
|
+
import { ErrorView as l, LoadingView as u } from "../../components/StateViews.js";
|
|
10
|
+
import { Donut as d, Legend as f } from "../../components/viz.js";
|
|
9
11
|
import "../../components/index.js";
|
|
10
|
-
import { useAgents as
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { GlassCard as M, PagePurpose as N } from "@burdenoff/fe-libs/ui";
|
|
12
|
+
import { useAgents as p, useSetAgentStatus as m } from "../../hooks/useSubscriberBotApi.js";
|
|
13
|
+
import { useState as h } from "react";
|
|
14
|
+
import { ArrowLeftRight as g, Bot as _, Building2 as v, Clock as y, ConciergeBell as b, DollarSign as x, Handshake as S, Mail as C, Plus as w, RefreshCw as T, Search as E, ShieldCheck as D, Sparkles as O, Zap as k } from "lucide-react";
|
|
15
|
+
import { Fragment as A, jsx as j, jsxs as M } from "react/jsx-runtime";
|
|
16
|
+
import { GlassCard as N, PagePurpose as P } from "@burdenoff/fe-libs/ui";
|
|
16
17
|
//#region src/subscriberbot/pages/agents/AgentsListPage.tsx
|
|
17
|
-
var
|
|
18
|
+
var F = {
|
|
18
19
|
DISCOVERY: "agents.kinds.discovery",
|
|
19
20
|
RENEWAL: "agents.kinds.renewal",
|
|
20
21
|
COST: "agents.kinds.cost",
|
|
@@ -24,222 +25,222 @@ var P = {
|
|
|
24
25
|
NEGOTIATION: "agents.kinds.negotiation",
|
|
25
26
|
MIGRATION: "agents.kinds.migration",
|
|
26
27
|
CONCIERGE: "agents.kinds.concierge"
|
|
27
|
-
},
|
|
28
|
+
}, I = [
|
|
28
29
|
"DISABLED",
|
|
29
30
|
"SUGGEST_ONLY",
|
|
30
31
|
"AUTONOMOUS"
|
|
31
|
-
],
|
|
32
|
-
DISCOVERY:
|
|
33
|
-
RENEWAL:
|
|
34
|
-
COST:
|
|
35
|
-
NEWSLETTER:
|
|
36
|
-
VENDOR:
|
|
37
|
-
COMPLIANCE:
|
|
38
|
-
NEGOTIATION:
|
|
39
|
-
MIGRATION:
|
|
40
|
-
CONCIERGE:
|
|
41
|
-
},
|
|
32
|
+
], L = {
|
|
33
|
+
DISCOVERY: E,
|
|
34
|
+
RENEWAL: T,
|
|
35
|
+
COST: x,
|
|
36
|
+
NEWSLETTER: C,
|
|
37
|
+
VENDOR: v,
|
|
38
|
+
COMPLIANCE: D,
|
|
39
|
+
NEGOTIATION: S,
|
|
40
|
+
MIGRATION: g,
|
|
41
|
+
CONCIERGE: b
|
|
42
|
+
}, R = {
|
|
42
43
|
AUTONOMOUS: "var(--color-status-success-text)",
|
|
43
44
|
SUGGEST_ONLY: "var(--color-accent-indigo)",
|
|
44
45
|
DISABLED: "var(--color-text-secondary)"
|
|
45
|
-
},
|
|
46
|
-
function
|
|
47
|
-
let
|
|
46
|
+
}, z = 1440 * 60 * 1e3;
|
|
47
|
+
function B() {
|
|
48
|
+
let g = s(), { navigateTo: v } = e(), [b, x] = h(""), [S, C] = h(""), T = [{
|
|
48
49
|
value: "",
|
|
49
|
-
label:
|
|
50
|
-
}, ...Object.entries(
|
|
50
|
+
label: g("agents.filters.allKinds", "All kinds")
|
|
51
|
+
}, ...Object.entries(F).map(([e, t]) => ({
|
|
51
52
|
value: e,
|
|
52
|
-
label:
|
|
53
|
-
}))], { data:
|
|
54
|
-
kind:
|
|
55
|
-
status:
|
|
56
|
-
}),
|
|
53
|
+
label: g(t, r(e))
|
|
54
|
+
}))], { data: E, isLoading: D, isError: B, refetch: V } = p({
|
|
55
|
+
kind: b || void 0,
|
|
56
|
+
status: S || void 0
|
|
57
|
+
}), H = m(), U = E ?? [], W = {
|
|
57
58
|
AUTONOMOUS: 0,
|
|
58
59
|
SUGGEST_ONLY: 0,
|
|
59
60
|
DISABLED: 0
|
|
60
|
-
},
|
|
61
|
-
for (let e of
|
|
62
|
-
let
|
|
63
|
-
label:
|
|
64
|
-
value:
|
|
65
|
-
color:
|
|
61
|
+
}, G = 0, K = Date.now();
|
|
62
|
+
for (let e of U) W[e.status] += 1, e.lastRunAt && K - new Date(e.lastRunAt).getTime() <= 7 * z && (G += 1);
|
|
63
|
+
let q = I.map((e) => ({
|
|
64
|
+
label: g(`agents.postures.${e.toLowerCase()}`, r(e)),
|
|
65
|
+
value: W[e],
|
|
66
|
+
color: R[e]
|
|
66
67
|
})).filter((e) => e.value > 0);
|
|
67
|
-
return /* @__PURE__ */
|
|
68
|
-
title:
|
|
69
|
-
description:
|
|
70
|
-
actions: /* @__PURE__ */
|
|
68
|
+
return /* @__PURE__ */ M(t, {
|
|
69
|
+
title: g("agents.title", "AI Agents"),
|
|
70
|
+
description: g("agents.description", "Specialized agents that automate your recurring portfolio"),
|
|
71
|
+
actions: /* @__PURE__ */ M("button", {
|
|
71
72
|
type: "button",
|
|
72
|
-
onClick: () =>
|
|
73
|
+
onClick: () => v("/agents/new"),
|
|
73
74
|
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",
|
|
74
75
|
children: [
|
|
75
|
-
/* @__PURE__ */
|
|
76
|
+
/* @__PURE__ */ j(w, { className: "h-4 w-4" }),
|
|
76
77
|
" ",
|
|
77
|
-
|
|
78
|
+
g("agents.newAgent", "New agent")
|
|
78
79
|
]
|
|
79
80
|
}),
|
|
80
81
|
children: [
|
|
81
|
-
/* @__PURE__ */
|
|
82
|
+
/* @__PURE__ */ j(P, {
|
|
82
83
|
className: "mb-4",
|
|
83
|
-
children:
|
|
84
|
+
children: g("agents.purpose", "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.")
|
|
84
85
|
}),
|
|
85
|
-
/* @__PURE__ */
|
|
86
|
+
/* @__PURE__ */ M("div", {
|
|
86
87
|
className: "flex flex-wrap items-center gap-3",
|
|
87
|
-
children: [/* @__PURE__ */
|
|
88
|
-
"aria-label":
|
|
89
|
-
value:
|
|
90
|
-
onChange: (e) =>
|
|
88
|
+
children: [/* @__PURE__ */ j("select", {
|
|
89
|
+
"aria-label": g("agents.filters.kindLabel", "Filter by kind"),
|
|
90
|
+
value: b,
|
|
91
|
+
onChange: (e) => x(e.target.value),
|
|
91
92
|
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:
|
|
93
|
+
children: T.map((e) => /* @__PURE__ */ j("option", {
|
|
93
94
|
value: e.value,
|
|
94
95
|
children: e.label
|
|
95
96
|
}, e.value))
|
|
96
|
-
}), /* @__PURE__ */
|
|
97
|
-
"aria-label":
|
|
98
|
-
value:
|
|
99
|
-
onChange: (e) =>
|
|
97
|
+
}), /* @__PURE__ */ M("select", {
|
|
98
|
+
"aria-label": g("agents.filters.postureLabel", "Filter by posture"),
|
|
99
|
+
value: S,
|
|
100
|
+
onChange: (e) => C(e.target.value),
|
|
100
101
|
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)]",
|
|
101
|
-
children: [/* @__PURE__ */
|
|
102
|
+
children: [/* @__PURE__ */ j("option", {
|
|
102
103
|
value: "",
|
|
103
|
-
children:
|
|
104
|
-
}),
|
|
104
|
+
children: g("agents.filters.anyPosture", "Any posture")
|
|
105
|
+
}), I.map((e) => /* @__PURE__ */ j("option", {
|
|
105
106
|
value: e,
|
|
106
|
-
children:
|
|
107
|
+
children: g(`agents.postures.${e.toLowerCase()}`, r(e))
|
|
107
108
|
}, e))]
|
|
108
109
|
})]
|
|
109
110
|
}),
|
|
110
|
-
|
|
111
|
-
message:
|
|
112
|
-
onRetry: () => void
|
|
113
|
-
}) :
|
|
111
|
+
D ? /* @__PURE__ */ j(u, { label: g("agents.loading", "Loading agents…") }) : B ? /* @__PURE__ */ j(l, {
|
|
112
|
+
message: g("agents.error", "Could not load agents."),
|
|
113
|
+
onRetry: () => void V()
|
|
114
|
+
}) : U.length === 0 ? /* @__PURE__ */ j("div", {
|
|
114
115
|
className: "mt-6",
|
|
115
|
-
children: /* @__PURE__ */
|
|
116
|
+
children: /* @__PURE__ */ j(o, {
|
|
116
117
|
illustration: "empty-data",
|
|
117
|
-
icon: /* @__PURE__ */
|
|
118
|
-
title:
|
|
119
|
-
description:
|
|
120
|
-
action: /* @__PURE__ */
|
|
118
|
+
icon: /* @__PURE__ */ j(_, { className: "h-12 w-12" }),
|
|
119
|
+
title: g("agents.emptyTitle", "No agents yet"),
|
|
120
|
+
description: g("agents.emptyDescription", "Create your first AI agent to start automating discovery, renewals, or cost optimization."),
|
|
121
|
+
action: /* @__PURE__ */ j("button", {
|
|
121
122
|
type: "button",
|
|
122
|
-
onClick: () =>
|
|
123
|
+
onClick: () => v("/agents/new"),
|
|
123
124
|
className: "rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90",
|
|
124
|
-
children:
|
|
125
|
+
children: g("agents.newAgent", "New agent")
|
|
125
126
|
})
|
|
126
127
|
})
|
|
127
|
-
}) : /* @__PURE__ */
|
|
128
|
+
}) : /* @__PURE__ */ M(A, { children: [/* @__PURE__ */ M("div", {
|
|
128
129
|
className: "mt-6 grid grid-cols-1 gap-4 lg:grid-cols-3",
|
|
129
|
-
children: [/* @__PURE__ */
|
|
130
|
+
children: [/* @__PURE__ */ M(N, {
|
|
130
131
|
treatment: "insight",
|
|
131
132
|
className: "p-5 lg:col-span-1",
|
|
132
133
|
children: [
|
|
133
|
-
/* @__PURE__ */
|
|
134
|
+
/* @__PURE__ */ j("p", {
|
|
134
135
|
className: "text-sm font-semibold text-text-primary",
|
|
135
|
-
children:
|
|
136
|
+
children: g("agents.fleet.title", "Fleet posture")
|
|
136
137
|
}),
|
|
137
|
-
/* @__PURE__ */
|
|
138
|
+
/* @__PURE__ */ j("p", {
|
|
138
139
|
className: "mt-0.5 text-xs text-text-secondary",
|
|
139
|
-
children:
|
|
140
|
+
children: g("agents.fleet.description", "How your agents are allowed to act")
|
|
140
141
|
}),
|
|
141
|
-
/* @__PURE__ */
|
|
142
|
+
/* @__PURE__ */ M("div", {
|
|
142
143
|
className: "mt-4 flex items-center gap-5",
|
|
143
|
-
children: [/* @__PURE__ */
|
|
144
|
-
data:
|
|
144
|
+
children: [/* @__PURE__ */ j(d, {
|
|
145
|
+
data: q,
|
|
145
146
|
size: 132,
|
|
146
147
|
thickness: 16,
|
|
147
|
-
centerLabel: String(
|
|
148
|
-
centerSub:
|
|
149
|
-
}), /* @__PURE__ */
|
|
148
|
+
centerLabel: String(U.length),
|
|
149
|
+
centerSub: U.length === 1 ? g("agents.fleet.agentSingular", "agent") : g("agents.fleet.agentPlural", "agents")
|
|
150
|
+
}), /* @__PURE__ */ j(f, {
|
|
150
151
|
className: "flex-1",
|
|
151
|
-
data:
|
|
152
|
+
data: q
|
|
152
153
|
})]
|
|
153
154
|
})
|
|
154
155
|
]
|
|
155
|
-
}), /* @__PURE__ */
|
|
156
|
+
}), /* @__PURE__ */ M("div", {
|
|
156
157
|
className: "grid grid-cols-2 gap-4 lg:col-span-2",
|
|
157
158
|
children: [
|
|
158
|
-
/* @__PURE__ */
|
|
159
|
-
icon: /* @__PURE__ */
|
|
160
|
-
value:
|
|
161
|
-
label:
|
|
159
|
+
/* @__PURE__ */ j(n, {
|
|
160
|
+
icon: /* @__PURE__ */ j(_, { className: "h-5 w-5" }),
|
|
161
|
+
value: U.length,
|
|
162
|
+
label: g("agents.fleet.totalAgents", "Total agents")
|
|
162
163
|
}),
|
|
163
|
-
/* @__PURE__ */
|
|
164
|
-
icon: /* @__PURE__ */
|
|
165
|
-
value:
|
|
166
|
-
label:
|
|
164
|
+
/* @__PURE__ */ j(n, {
|
|
165
|
+
icon: /* @__PURE__ */ j(k, { className: "h-5 w-5" }),
|
|
166
|
+
value: W.AUTONOMOUS,
|
|
167
|
+
label: g("agents.postures.autonomous", "Autonomous")
|
|
167
168
|
}),
|
|
168
|
-
/* @__PURE__ */
|
|
169
|
-
icon: /* @__PURE__ */
|
|
170
|
-
value:
|
|
171
|
-
label:
|
|
169
|
+
/* @__PURE__ */ j(n, {
|
|
170
|
+
icon: /* @__PURE__ */ j(O, { className: "h-5 w-5" }),
|
|
171
|
+
value: W.SUGGEST_ONLY,
|
|
172
|
+
label: g("agents.postures.suggestOnly", "Suggest-only")
|
|
172
173
|
}),
|
|
173
|
-
/* @__PURE__ */
|
|
174
|
-
icon: /* @__PURE__ */
|
|
175
|
-
value:
|
|
176
|
-
label:
|
|
174
|
+
/* @__PURE__ */ j(n, {
|
|
175
|
+
icon: /* @__PURE__ */ j(y, { className: "h-5 w-5" }),
|
|
176
|
+
value: G,
|
|
177
|
+
label: g("agents.fleet.ranLast7Days", "Ran in last 7 days")
|
|
177
178
|
})
|
|
178
179
|
]
|
|
179
180
|
})]
|
|
180
|
-
}), /* @__PURE__ */
|
|
181
|
+
}), /* @__PURE__ */ j("div", {
|
|
181
182
|
className: "mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
182
|
-
children:
|
|
183
|
-
let t =
|
|
184
|
-
return /* @__PURE__ */
|
|
183
|
+
children: U.map((e) => {
|
|
184
|
+
let t = L[e.kind] ?? _;
|
|
185
|
+
return /* @__PURE__ */ M(N, {
|
|
185
186
|
treatment: "metric",
|
|
186
187
|
glow: e.status === "AUTONOMOUS",
|
|
187
188
|
className: "flex flex-col p-5",
|
|
188
|
-
children: [/* @__PURE__ */
|
|
189
|
+
children: [/* @__PURE__ */ M("button", {
|
|
189
190
|
type: "button",
|
|
190
|
-
onClick: () =>
|
|
191
|
+
onClick: () => v(`/agents/${e.id}`),
|
|
191
192
|
className: "rounded-lg text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]",
|
|
192
|
-
"aria-label":
|
|
193
|
+
"aria-label": g("agents.card.openAria", "Open {{name}}", { name: e.name }),
|
|
193
194
|
children: [
|
|
194
|
-
/* @__PURE__ */
|
|
195
|
+
/* @__PURE__ */ M("div", {
|
|
195
196
|
className: "flex items-start justify-between gap-2",
|
|
196
|
-
children: [/* @__PURE__ */
|
|
197
|
+
children: [/* @__PURE__ */ j("span", {
|
|
197
198
|
className: "flex size-10 items-center justify-center rounded-xl bg-[var(--color-accent-soft)] text-text-primary",
|
|
198
|
-
children: /* @__PURE__ */
|
|
199
|
-
}), /* @__PURE__ */
|
|
199
|
+
children: /* @__PURE__ */ j(t, { className: "h-5 w-5" })
|
|
200
|
+
}), /* @__PURE__ */ j(a, { status: e.status })]
|
|
200
201
|
}),
|
|
201
|
-
/* @__PURE__ */
|
|
202
|
+
/* @__PURE__ */ j("p", {
|
|
202
203
|
className: "mt-3 truncate text-sm font-semibold text-text-primary",
|
|
203
204
|
children: e.name
|
|
204
205
|
}),
|
|
205
|
-
/* @__PURE__ */
|
|
206
|
+
/* @__PURE__ */ M("p", {
|
|
206
207
|
className: "text-xs text-text-secondary",
|
|
207
208
|
children: [
|
|
208
|
-
|
|
209
|
+
g(F[e.kind], r(e.kind)),
|
|
209
210
|
" ",
|
|
210
|
-
|
|
211
|
+
g("agents.card.kindSuffix", "agent")
|
|
211
212
|
]
|
|
212
213
|
}),
|
|
213
|
-
/* @__PURE__ */
|
|
214
|
+
/* @__PURE__ */ j("p", {
|
|
214
215
|
className: "mt-2 line-clamp-2 min-h-[2rem] text-xs text-text-secondary",
|
|
215
|
-
children: e.description ||
|
|
216
|
+
children: e.description || g("agents.card.noDescription", "No description provided.")
|
|
216
217
|
}),
|
|
217
|
-
/* @__PURE__ */
|
|
218
|
+
/* @__PURE__ */ M("p", {
|
|
218
219
|
className: "mt-3 flex items-center gap-1.5 text-xs text-text-secondary",
|
|
219
|
-
children: [/* @__PURE__ */
|
|
220
|
+
children: [/* @__PURE__ */ j(y, {
|
|
220
221
|
className: "h-3.5 w-3.5",
|
|
221
222
|
"aria-hidden": !0
|
|
222
|
-
}), e.lastRunAt ?
|
|
223
|
+
}), e.lastRunAt ? g("agents.card.lastRun", "Last run {{when}}", { when: i(e.lastRunAt) }) : g("agents.card.neverRun", "Never run")]
|
|
223
224
|
})
|
|
224
225
|
]
|
|
225
|
-
}), /* @__PURE__ */
|
|
226
|
+
}), /* @__PURE__ */ M("div", {
|
|
226
227
|
className: "mt-4 border-t border-border-subtle pt-3",
|
|
227
|
-
children: [/* @__PURE__ */
|
|
228
|
+
children: [/* @__PURE__ */ j("label", {
|
|
228
229
|
className: "mb-1 block text-[10px] font-medium uppercase tracking-wide text-text-secondary",
|
|
229
|
-
children:
|
|
230
|
-
}), /* @__PURE__ */
|
|
231
|
-
"aria-label":
|
|
230
|
+
children: g("agents.card.posture", "Posture")
|
|
231
|
+
}), /* @__PURE__ */ j("select", {
|
|
232
|
+
"aria-label": g("agents.card.postureAria", "Posture for {{name}}", { name: e.name }),
|
|
232
233
|
value: e.status,
|
|
233
|
-
disabled:
|
|
234
|
-
onChange: (t) =>
|
|
234
|
+
disabled: H.isPending,
|
|
235
|
+
onChange: (t) => H.mutate({
|
|
235
236
|
id: e.id,
|
|
236
237
|
status: t.target.value
|
|
237
238
|
}),
|
|
238
239
|
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",
|
|
239
|
-
children:
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}
|
|
240
|
+
children: /* @__PURE__ */ j(c, {
|
|
241
|
+
options: I,
|
|
242
|
+
currentStatus: e.status
|
|
243
|
+
})
|
|
243
244
|
})]
|
|
244
245
|
})]
|
|
245
246
|
}, e.id);
|
|
@@ -249,6 +250,6 @@ function z() {
|
|
|
249
250
|
});
|
|
250
251
|
}
|
|
251
252
|
//#endregion
|
|
252
|
-
export {
|
|
253
|
+
export { B as AgentsListPage };
|
|
253
254
|
|
|
254
255
|
//# 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 {\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 { useTr } from '../../hooks/useTr';\nimport { formatEnumLabel, formatRelativeTime } from '../../utils/formatters';\nimport type { SubAgentKind, SubAgentStatus } from '../../types';\n\nconst KIND_KEY: Record<SubAgentKind, string> = {\n DISCOVERY: 'agents.kinds.discovery',\n RENEWAL: 'agents.kinds.renewal',\n COST: 'agents.kinds.cost',\n NEWSLETTER: 'agents.kinds.newsletter',\n VENDOR: 'agents.kinds.vendor',\n COMPLIANCE: 'agents.kinds.compliance',\n NEGOTIATION: 'agents.kinds.negotiation',\n MIGRATION: 'agents.kinds.migration',\n CONCIERGE: 'agents.kinds.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 tr = useTr();\n const { navigateTo } = useSubscriberBot();\n const [kind, setKind] = useState<'' | SubAgentKind>('');\n const [status, setStatus] = useState<'' | SubAgentStatus>('');\n\n const kindOptions: Array<{ value: '' | SubAgentKind; label: string }> = [\n { value: '', label: tr('agents.filters.allKinds', 'All kinds') },\n ...Object.entries(KIND_KEY).map(([value, key]) => ({\n value: value as SubAgentKind,\n label: tr(key, formatEnumLabel(value as SubAgentKind)),\n })),\n ];\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: tr(`agents.postures.${s.toLowerCase()}`, formatEnumLabel(s)),\n value: postureCounts[s],\n color: POSTURE_COLOR[s],\n })).filter((d) => d.value > 0);\n\n return (\n <PageLayout\n title={tr('agents.title', 'AI Agents')}\n description={tr(\n 'agents.description',\n 'Specialized agents that automate your recurring portfolio'\n )}\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\" /> {tr('agents.newAgent', 'New agent')}\n </button>\n }\n >\n <PagePurpose className=\"mb-4\">\n {tr(\n 'agents.purpose',\n \"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.\"\n )}\n </PagePurpose>\n\n <div className=\"flex flex-wrap items-center gap-3\">\n <select\n aria-label={tr('agents.filters.kindLabel', '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 {kindOptions.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n <select\n aria-label={tr('agents.filters.postureLabel', '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=\"\">{tr('agents.filters.anyPosture', 'Any posture')}</option>\n {STATUS_OPTIONS.map((s) => (\n <option key={s} value={s}>\n {tr(`agents.postures.${s.toLowerCase()}`, formatEnumLabel(s))}\n </option>\n ))}\n </select>\n </div>\n\n {isLoading ? (\n <LoadingView label={tr('agents.loading', 'Loading agents…')} />\n ) : isError ? (\n <ErrorView\n message={tr('agents.error', 'Could not load agents.')}\n onRetry={() => void refetch()}\n />\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={tr('agents.emptyTitle', 'No agents yet')}\n description={tr(\n 'agents.emptyDescription',\n 'Create your first AI agent to start automating discovery, renewals, or cost optimization.'\n )}\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 {tr('agents.newAgent', '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\">\n {tr('agents.fleet.title', 'Fleet posture')}\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {tr('agents.fleet.description', '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={\n agents.length === 1\n ? tr('agents.fleet.agentSingular', 'agent')\n : tr('agents.fleet.agentPlural', 'agents')\n }\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={tr('agents.fleet.totalAgents', 'Total agents')}\n />\n <MetricsCard\n icon={<Zap className=\"h-5 w-5\" />}\n value={postureCounts.AUTONOMOUS}\n label={tr('agents.postures.autonomous', 'Autonomous')}\n />\n <MetricsCard\n icon={<Sparkles className=\"h-5 w-5\" />}\n value={postureCounts.SUGGEST_ONLY}\n label={tr('agents.postures.suggestOnly', 'Suggest-only')}\n />\n <MetricsCard\n icon={<Clock className=\"h-5 w-5\" />}\n value={recentlyRun}\n label={tr('agents.fleet.ranLast7Days', '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={tr('agents.card.openAria', 'Open {{name}}', { name: 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\">\n {tr(KIND_KEY[a.kind], formatEnumLabel(a.kind))}{' '}\n {tr('agents.card.kindSuffix', 'agent')}\n </p>\n <p className=\"mt-2 line-clamp-2 min-h-[2rem] text-xs text-text-secondary\">\n {a.description || tr('agents.card.noDescription', '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\n ? tr('agents.card.lastRun', 'Last run {{when}}', {\n when: formatRelativeTime(a.lastRunAt),\n })\n : tr('agents.card.neverRun', '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 {tr('agents.card.posture', 'Posture')}\n </label>\n <select\n aria-label={tr('agents.card.postureAria', 'Posture for {{name}}', {\n name: a.name,\n })}\n value={a.status}\n disabled={setAgentStatus.isPending}\n onChange={(e) =>\n setAgentStatus.mutate({\n id: a.id,\n status: e.target.value as SubAgentStatus,\n })\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 {tr(`agents.postures.${s.toLowerCase()}`, formatEnumLabel(s))}\n </option>\n ))}\n </select>\n </div>\n </GlassCard>\n );\n })}\n </div>\n </>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA8CA,IAAM,IAAyC;CAC7C,WAAW;CACX,SAAS;CACT,MAAM;CACN,YAAY;CACZ,QAAQ;CACR,YAAY;CACZ,aAAa;CACb,WAAW;CACX,WAAW;AACb,GAEM,IAAmC;CAAC;CAAY;CAAgB;AAAY,GAG5E,IAA8C;CAClD,WAAW;CACX,SAAS;CACT,MAAM;CACN,YAAY;CACZ,QAAQ;CACR,YAAY;CACZ,aAAa;CACb,WAAW;CACX,WAAW;AACb,GAGM,IAAgD;CACpD,YAAY;CACZ,cAAc;CACd,UAAU;AACZ,GAEM,IAAS,OAAU,KAAK;AAE9B,SAAgB,IAAiB;CAC/B,IAAM,IAAK,EAAM,GACX,EAAE,kBAAe,EAAiB,GAClC,CAAC,GAAM,KAAW,EAA4B,EAAE,GAChD,CAAC,GAAQ,KAAa,EAA8B,EAAE,GAEtD,IAAkE,CACtE;EAAE,OAAO;EAAI,OAAO,EAAG,2BAA2B,WAAW;CAAE,GAC/D,GAAG,OAAO,QAAQ,CAAQ,EAAE,KAAK,CAAC,GAAO,QAAU;EAC1C;EACP,OAAO,EAAG,GAAK,EAAgB,CAAqB,CAAC;CACvD,EAAE,CACJ,GAEM,EAAE,SAAM,cAAW,YAAS,eAAY,EAAU;EACtD,MAAM,KAAQ,KAAA;EACd,QAAQ,KAAU,KAAA;CACpB,CAAC,GACK,IAAiB,EAAkB,GAEnC,IAAS,KAAQ,CAAC,GAGlB,IAAgD;EACpD,YAAY;EACZ,cAAc;EACd,UAAU;CACZ,GACI,IAAc,GACZ,IAAM,KAAK,IAAI;CACrB,KAAK,IAAM,KAAK,GAEd,AADA,EAAc,EAAE,WAAW,GACvB,EAAE,aAAa,IAAM,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,KAAK,IAAI,MAAQ,KAAe;CAEzF,IAAM,IAAyB,EAAe,KAAK,OAAO;EACxD,OAAO,EAAG,mBAAmB,EAAE,YAAY,KAAK,EAAgB,CAAC,CAAC;EAClE,OAAO,EAAc;EACrB,OAAO,EAAc;CACvB,EAAE,EAAE,QAAQ,MAAM,EAAE,QAAQ,CAAC;CAE7B,OACE,kBAAC,GAAD;EACE,OAAO,EAAG,gBAAgB,WAAW;EACrC,aAAa,EACX,sBACA,2DACF;EACA,SACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,EAAW,aAAa;GACvC,WAAU;aAHZ;IAKE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA;IAAC;IAAE,EAAG,mBAAmB,WAAW;GACzD;;YAbZ;GAgBE,kBAAC,GAAD;IAAa,WAAU;cACpB,EACC,kBACA,6KACF;GACW,CAAA;GAEb,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,cAAY,EAAG,4BAA4B,gBAAgB;KAC3D,OAAO;KACP,WAAW,MAAM,EAAQ,EAAE,OAAO,KAA0B;KAC5D,WAAU;eAET,EAAY,KAAK,MAChB,kBAAC,UAAD;MAAsB,OAAO,EAAE;gBAC5B,EAAE;KACG,GAFK,EAAE,KAEP,CACT;IACK,CAAA,GACR,kBAAC,UAAD;KACE,cAAY,EAAG,+BAA+B,mBAAmB;KACjE,OAAO;KACP,WAAW,MAAM,EAAU,EAAE,OAAO,KAA4B;KAChE,WAAU;eAJZ,CAME,kBAAC,UAAD;MAAQ,OAAM;gBAAI,EAAG,6BAA6B,aAAa;KAAU,CAAA,GACxE,EAAe,KAAK,MACnB,kBAAC,UAAD;MAAgB,OAAO;gBACpB,EAAG,mBAAmB,EAAE,YAAY,KAAK,EAAgB,CAAC,CAAC;KACtD,GAFK,CAEL,CACT,CACK;MACL;;GAEJ,IACC,kBAAC,GAAD,EAAa,OAAO,EAAG,kBAAkB,iBAAiB,EAAI,CAAA,IAC5D,IACF,kBAAC,GAAD;IACE,SAAS,EAAG,gBAAgB,wBAAwB;IACpD,eAAe,KAAK,EAAQ;GAC7B,CAAA,IACC,EAAO,WAAW,IACpB,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KACE,cAAa;KACb,MAAM,kBAAC,GAAD,EAAK,WAAU,YAAa,CAAA;KAClC,OAAO,EAAG,qBAAqB,eAAe;KAC9C,aAAa,EACX,2BACA,2FACF;KACA,QACE,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAW,aAAa;MACvC,WAAU;gBAET,EAAG,mBAAmB,WAAW;KAC5B,CAAA;IAEX,CAAA;GACE,CAAA,IAEL,kBAAA,GAAA,EAAA,UAAA,CAEE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAW,WAAU;KAAU,WAAU;eAAzC;MACE,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAG,sBAAsB,eAAe;MACxC,CAAA;MACH,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAG,4BAA4B,oCAAoC;MACnE,CAAA;MACH,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QACE,MAAM;QACN,MAAM;QACN,WAAW;QACX,aAAa,OAAO,EAAO,MAAM;QACjC,WACE,EAAO,WAAW,IACd,EAAG,8BAA8B,OAAO,IACxC,EAAG,4BAA4B,QAAQ;OAE9C,CAAA,GACD,kBAAC,GAAD;QAAQ,WAAU;QAAS,MAAM;OAAc,CAAA,CAC5C;;KACI;QAEX,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAK,WAAU,UAAW,CAAA;OAChC,OAAO,EAAO;OACd,OAAO,EAAG,4BAA4B,cAAc;MACrD,CAAA;MACD,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAK,WAAU,UAAW,CAAA;OAChC,OAAO,EAAc;OACrB,OAAO,EAAG,8BAA8B,YAAY;MACrD,CAAA;MACD,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA;OACrC,OAAO,EAAc;OACrB,OAAO,EAAG,+BAA+B,cAAc;MACxD,CAAA;MACD,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA;OAClC,OAAO;OACP,OAAO,EAAG,6BAA6B,oBAAoB;MAC5D,CAAA;KACE;MACF;OAGL,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAO,KAAK,MAAM;KACjB,IAAM,IAAO,EAAU,EAAE,SAAS;KAClC,OACE,kBAAC,GAAD;MAEE,WAAU;MACV,MAAM,EAAE,WAAW;MACnB,WAAU;gBAJZ,CAME,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAW,WAAW,EAAE,IAAI;OAC3C,WAAU;OACV,cAAY,EAAG,wBAAwB,iBAAiB,EAAE,MAAM,EAAE,KAAK,CAAC;iBAJ1E;QAME,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,QAAD;UAAM,WAAU;oBACd,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA;SACvB,CAAA,GACN,kBAAC,GAAD,EAAa,QAAQ,EAAE,OAAS,CAAA,CAC7B;;QACL,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAE;QACF,CAAA;QACH,kBAAC,KAAD;SAAG,WAAU;mBAAb;UACG,EAAG,EAAS,EAAE,OAAO,EAAgB,EAAE,IAAI,CAAC;UAAG;UAC/C,EAAG,0BAA0B,OAAO;SACpC;;QACH,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAE,eAAe,EAAG,6BAA6B,0BAA0B;QAC3E,CAAA;QACH,kBAAC,KAAD;SAAG,WAAU;mBAAb,CACE,kBAAC,GAAD;UAAO,WAAU;UAAc,eAAA;SAAa,CAAA,GAC3C,EAAE,YACC,EAAG,uBAAuB,qBAAqB,EAC7C,MAAM,EAAmB,EAAE,SAAS,EACtC,CAAC,IACD,EAAG,wBAAwB,WAAW,CACzC;;OACG;UACR,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QAAO,WAAU;kBACd,EAAG,uBAAuB,SAAS;OAC/B,CAAA,GACP,kBAAC,UAAD;QACE,cAAY,EAAG,2BAA2B,wBAAwB,EAChE,MAAM,EAAE,KACV,CAAC;QACD,OAAO,EAAE;QACT,UAAU,EAAe;QACzB,WAAW,MACT,EAAe,OAAO;SACpB,IAAI,EAAE;SACN,QAAQ,EAAE,OAAO;QACnB,CAAC;QAEH,WAAU;kBAET,EAAe,KAAK,MACnB,kBAAC,UAAD;SAAgB,OAAO;mBACpB,EAAG,mBAAmB,EAAE,YAAY,KAAK,EAAgB,CAAC,CAAC;QACtD,GAFK,CAEL,CACT;OACK,CAAA,CACL;QACI;QA7DJ,EAAE,EA6DE;IAEf,CAAC;GACE,CAAA,CACL,EAAA,CAAA;EAEM;;AAEhB"}
|
|
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 { useTr } from '../../hooks/useTr';\nimport { PostureOptions } from '../../components/PostureOptions';\nimport { formatEnumLabel, formatRelativeTime } from '../../utils/formatters';\nimport type { SubAgentKind, SubAgentStatus } from '../../types';\n\nconst KIND_KEY: Record<SubAgentKind, string> = {\n DISCOVERY: 'agents.kinds.discovery',\n RENEWAL: 'agents.kinds.renewal',\n COST: 'agents.kinds.cost',\n NEWSLETTER: 'agents.kinds.newsletter',\n VENDOR: 'agents.kinds.vendor',\n COMPLIANCE: 'agents.kinds.compliance',\n NEGOTIATION: 'agents.kinds.negotiation',\n MIGRATION: 'agents.kinds.migration',\n CONCIERGE: 'agents.kinds.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 tr = useTr();\n const { navigateTo } = useSubscriberBot();\n const [kind, setKind] = useState<'' | SubAgentKind>('');\n const [status, setStatus] = useState<'' | SubAgentStatus>('');\n\n const kindOptions: Array<{ value: '' | SubAgentKind; label: string }> = [\n { value: '', label: tr('agents.filters.allKinds', 'All kinds') },\n ...Object.entries(KIND_KEY).map(([value, key]) => ({\n value: value as SubAgentKind,\n label: tr(key, formatEnumLabel(value as SubAgentKind)),\n })),\n ];\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: tr(`agents.postures.${s.toLowerCase()}`, formatEnumLabel(s)),\n value: postureCounts[s],\n color: POSTURE_COLOR[s],\n })).filter((d) => d.value > 0);\n\n return (\n <PageLayout\n title={tr('agents.title', 'AI Agents')}\n description={tr(\n 'agents.description',\n 'Specialized agents that automate your recurring portfolio'\n )}\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\" /> {tr('agents.newAgent', 'New agent')}\n </button>\n }\n >\n <PagePurpose className=\"mb-4\">\n {tr(\n 'agents.purpose',\n \"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.\"\n )}\n </PagePurpose>\n\n <div className=\"flex flex-wrap items-center gap-3\">\n <select\n aria-label={tr('agents.filters.kindLabel', '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 {kindOptions.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n <select\n aria-label={tr('agents.filters.postureLabel', '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=\"\">{tr('agents.filters.anyPosture', 'Any posture')}</option>\n {STATUS_OPTIONS.map((s) => (\n <option key={s} value={s}>\n {tr(`agents.postures.${s.toLowerCase()}`, formatEnumLabel(s))}\n </option>\n ))}\n </select>\n </div>\n\n {isLoading ? (\n <LoadingView label={tr('agents.loading', 'Loading agents…')} />\n ) : isError ? (\n <ErrorView\n message={tr('agents.error', 'Could not load agents.')}\n onRetry={() => void refetch()}\n />\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={tr('agents.emptyTitle', 'No agents yet')}\n description={tr(\n 'agents.emptyDescription',\n 'Create your first AI agent to start automating discovery, renewals, or cost optimization.'\n )}\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 {tr('agents.newAgent', '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\">\n {tr('agents.fleet.title', 'Fleet posture')}\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {tr('agents.fleet.description', '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={\n agents.length === 1\n ? tr('agents.fleet.agentSingular', 'agent')\n : tr('agents.fleet.agentPlural', 'agents')\n }\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={tr('agents.fleet.totalAgents', 'Total agents')}\n />\n <MetricsCard\n icon={<Zap className=\"h-5 w-5\" />}\n value={postureCounts.AUTONOMOUS}\n label={tr('agents.postures.autonomous', 'Autonomous')}\n />\n <MetricsCard\n icon={<Sparkles className=\"h-5 w-5\" />}\n value={postureCounts.SUGGEST_ONLY}\n label={tr('agents.postures.suggestOnly', 'Suggest-only')}\n />\n <MetricsCard\n icon={<Clock className=\"h-5 w-5\" />}\n value={recentlyRun}\n label={tr('agents.fleet.ranLast7Days', '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={tr('agents.card.openAria', 'Open {{name}}', { name: 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\">\n {tr(KIND_KEY[a.kind], formatEnumLabel(a.kind))}{' '}\n {tr('agents.card.kindSuffix', 'agent')}\n </p>\n <p className=\"mt-2 line-clamp-2 min-h-[2rem] text-xs text-text-secondary\">\n {a.description || tr('agents.card.noDescription', '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\n ? tr('agents.card.lastRun', 'Last run {{when}}', {\n when: formatRelativeTime(a.lastRunAt),\n })\n : tr('agents.card.neverRun', '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 {tr('agents.card.posture', 'Posture')}\n </label>\n <select\n aria-label={tr('agents.card.postureAria', 'Posture for {{name}}', {\n name: a.name,\n })}\n value={a.status}\n disabled={setAgentStatus.isPending}\n onChange={(e) =>\n setAgentStatus.mutate({\n id: a.id,\n status: e.target.value as SubAgentStatus,\n })\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 <PostureOptions options={STATUS_OPTIONS} currentStatus={a.status} />\n </select>\n </div>\n </GlassCard>\n );\n })}\n </div>\n </>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|