@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.
Files changed (38) hide show
  1. package/dist/subscriberbot/SubscriberBotRoutes.js +14 -6
  2. package/dist/subscriberbot/SubscriberBotRoutes.js.map +1 -1
  3. package/dist/subscriberbot/components/Modal.js +51 -0
  4. package/dist/subscriberbot/components/Modal.js.map +1 -0
  5. package/dist/subscriberbot/components/index.js +7 -0
  6. package/dist/subscriberbot/components/viz.js +196 -0
  7. package/dist/subscriberbot/components/viz.js.map +1 -0
  8. package/dist/subscriberbot/hooks/useSubscriberBotApi.js +364 -161
  9. package/dist/subscriberbot/hooks/useSubscriberBotApi.js.map +1 -1
  10. package/dist/subscriberbot/pages/AssetVaultPage.js +182 -0
  11. package/dist/subscriberbot/pages/AssetVaultPage.js.map +1 -0
  12. package/dist/subscriberbot/pages/DashboardPage.js +272 -199
  13. package/dist/subscriberbot/pages/DashboardPage.js.map +1 -1
  14. package/dist/subscriberbot/pages/InboxPage.js +682 -126
  15. package/dist/subscriberbot/pages/InboxPage.js.map +1 -1
  16. package/dist/subscriberbot/pages/InsightsPage.js +220 -0
  17. package/dist/subscriberbot/pages/InsightsPage.js.map +1 -0
  18. package/dist/subscriberbot/pages/InvoicesPage.js +592 -81
  19. package/dist/subscriberbot/pages/InvoicesPage.js.map +1 -1
  20. package/dist/subscriberbot/pages/WorkflowsPage.js +344 -169
  21. package/dist/subscriberbot/pages/WorkflowsPage.js.map +1 -1
  22. package/dist/subscriberbot/pages/agents/AgentDetailPage.js +401 -116
  23. package/dist/subscriberbot/pages/agents/AgentDetailPage.js.map +1 -1
  24. package/dist/subscriberbot/pages/agents/AgentsListPage.js +185 -93
  25. package/dist/subscriberbot/pages/agents/AgentsListPage.js.map +1 -1
  26. package/dist/subscriberbot/pages/providers/ProviderDetailPage.js +735 -135
  27. package/dist/subscriberbot/pages/providers/ProviderDetailPage.js.map +1 -1
  28. package/dist/subscriberbot/pages/providers/ProviderNewPage.js +217 -153
  29. package/dist/subscriberbot/pages/providers/ProviderNewPage.js.map +1 -1
  30. package/dist/subscriberbot/pages/providers/ProvidersListPage.js +216 -111
  31. package/dist/subscriberbot/pages/providers/ProvidersListPage.js.map +1 -1
  32. package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js +902 -250
  33. package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js.map +1 -1
  34. package/dist/subscriberbot/pages/relationships/RelationshipNewPage.js +242 -192
  35. package/dist/subscriberbot/pages/relationships/RelationshipNewPage.js.map +1 -1
  36. package/dist/subscriberbot/pages/relationships/RelationshipsListPage.js +174 -131
  37. package/dist/subscriberbot/pages/relationships/RelationshipsListPage.js.map +1 -1
  38. package/package.json +1 -1
@@ -1,20 +1,22 @@
1
1
  import { useSubscriberBot as e } from "../context/SubscriberBotContext.js";
2
2
  import { cn as t } from "../utils/cn.js";
3
3
  import { PageLayout as n } from "../components/PageLayout.js";
4
- import { formatRelativeTime as r } from "../utils/formatters.js";
5
- import { StatusBadge as i } from "../components/StatusBadge.js";
6
- import { EmptyState as a } from "../components/EmptyState.js";
7
- import { ErrorView as o, LoadingView as s } from "../components/StateViews.js";
8
- import { useInboxMessages as c, useMarkMessageRead as l } from "../hooks/useSubscriberBotApi.js";
9
- import { useState as u } from "react";
10
- import { AlertTriangle as d, Inbox as f, Mail as p, MailOpen as m } from "lucide-react";
11
- import { Fragment as h, jsx as g, jsxs as _ } from "react/jsx-runtime";
12
- import { PagePurpose as v } from "@burdenoff/fe-libs/ui";
4
+ import { formatDateTime as r, formatEnumLabel as i, formatRelativeTime as a } from "../utils/formatters.js";
5
+ import { StatusBadge as o } from "../components/StatusBadge.js";
6
+ import { EmptyState as s } from "../components/EmptyState.js";
7
+ import { Modal as c } from "../components/Modal.js";
8
+ import { ErrorView as l, LoadingView as u, Section as d } from "../components/StateViews.js";
9
+ import { MiniProgress as f } from "../components/viz.js";
10
+ import { useChannels as p, useCreateChannel as m, useInboxMessages as h, useIngestMessage as g, useMarkMessageRead as ee, useVerifyChannel as _ } from "../hooks/useSubscriberBotApi.js";
11
+ import { useState as v } from "react";
12
+ import { AlertTriangle as y, BadgeCheck as b, Bell as x, ChevronRight as S, Hash as C, Inbox as w, Mail as T, MailOpen as E, MessageSquare as D, MessagesSquare as O, MousePointerClick as k, Phone as A, Plus as j, Radio as M, Send as N, ShieldAlert as P, Sparkles as F, Webhook as I } from "lucide-react";
13
+ import { Fragment as L, jsx as R, jsxs as z } from "react/jsx-runtime";
14
+ import { GlassCard as B, PagePurpose as V } from "@burdenoff/fe-libs/ui";
13
15
  //#region src/subscriberbot/pages/InboxPage.tsx
14
- var y = 25, b = [
16
+ var H = 25, U = "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)]", W = "mb-1 block text-xs font-medium text-text-secondary", G = [
15
17
  {
16
18
  value: "",
17
- label: "All categories"
19
+ label: "All"
18
20
  },
19
21
  {
20
22
  value: "BILLING",
@@ -30,7 +32,7 @@ var y = 25, b = [
30
32
  },
31
33
  {
32
34
  value: "SECURITY_ALERT",
33
- label: "Security alerts"
35
+ label: "Security"
34
36
  },
35
37
  {
36
38
  value: "PRODUCT_UPDATE",
@@ -44,143 +46,697 @@ var y = 25, b = [
44
46
  value: "OTHER",
45
47
  label: "Other"
46
48
  }
47
- ];
48
- function x() {
49
- let { navigateTo: x } = e(), [S, C] = u(0), [w, T] = u(""), [E, D] = u(!1), { data: O, isLoading: k, isError: A, refetch: j, isFetching: M } = c({
50
- category: w || void 0,
51
- unreadOnly: E || void 0,
49
+ ], K = [
50
+ "BILLING",
51
+ "RENEWAL",
52
+ "REQUIRED_ACTION",
53
+ "SECURITY_ALERT",
54
+ "PRODUCT_UPDATE",
55
+ "MARKETING",
56
+ "OTHER"
57
+ ], q = [
58
+ "EMAIL",
59
+ "SMS",
60
+ "SLACK",
61
+ "DISCORD",
62
+ "WHATSAPP",
63
+ "PUSH",
64
+ "WEBHOOK"
65
+ ], J = {
66
+ BILLING: "bg-[var(--color-accent-soft)] text-text-primary",
67
+ RENEWAL: "bg-status-success-bg-subtle text-status-success-text",
68
+ REQUIRED_ACTION: "bg-status-warning-bg-subtle text-status-warning-text",
69
+ SECURITY_ALERT: "bg-status-error-bg-subtle text-status-error-text",
70
+ PRODUCT_UPDATE: "bg-[var(--color-accent-soft)] text-text-primary",
71
+ MARKETING: "bg-bg-sunken text-text-secondary",
72
+ OTHER: "bg-bg-sunken text-text-secondary"
73
+ }, Y = {
74
+ EMAIL: T,
75
+ SMS: D,
76
+ SLACK: C,
77
+ DISCORD: O,
78
+ WHATSAPP: A,
79
+ PUSH: x,
80
+ WEBHOOK: I
81
+ };
82
+ function X({ category: e }) {
83
+ return /* @__PURE__ */ R("span", {
84
+ className: t("inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-medium", J[e]),
85
+ children: i(e)
86
+ });
87
+ }
88
+ function Z({ className: e }) {
89
+ return /* @__PURE__ */ z("span", {
90
+ title: "Action required",
91
+ className: t("inline-flex items-center gap-1 rounded-full bg-status-warning-bg-subtle px-2 py-0.5 text-[10px] font-medium text-status-warning-text", e),
92
+ children: [/* @__PURE__ */ R(y, { className: "h-3 w-3" }), " Action"]
93
+ });
94
+ }
95
+ function Q({ onClose: e }) {
96
+ let t = m(), [n, r] = v("EMAIL"), [i, a] = v(""), [o, s] = v(""), [l, u] = v(!0), [d, f] = v(null);
97
+ return /* @__PURE__ */ R(c, {
98
+ title: "Add channel",
99
+ onClose: e,
100
+ maxWidthClassName: "max-w-xl",
101
+ children: /* @__PURE__ */ z("form", {
102
+ onSubmit: (r) => {
103
+ if (r.preventDefault(), f(null), !i.trim()) {
104
+ f("An address is required.");
105
+ return;
106
+ }
107
+ t.mutate({
108
+ type: n,
109
+ address: i.trim(),
110
+ ingestEnabled: l,
111
+ ...o.trim() ? { label: o.trim() } : {}
112
+ }, {
113
+ onSuccess: () => e(),
114
+ onError: (e) => f(e instanceof Error ? e.message : "Could not add channel.")
115
+ });
116
+ },
117
+ className: "space-y-4",
118
+ children: [
119
+ d && /* @__PURE__ */ R("div", {
120
+ className: "rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text",
121
+ children: d
122
+ }),
123
+ /* @__PURE__ */ z("div", {
124
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
125
+ children: [/* @__PURE__ */ z("div", { children: [/* @__PURE__ */ R("label", {
126
+ htmlFor: "ch-type",
127
+ className: W,
128
+ children: "Type"
129
+ }), /* @__PURE__ */ R("select", {
130
+ id: "ch-type",
131
+ value: n,
132
+ onChange: (e) => r(e.target.value),
133
+ className: U,
134
+ children: q.map((e) => /* @__PURE__ */ R("option", {
135
+ value: e,
136
+ children: e.replace(/_/g, " ")
137
+ }, e))
138
+ })] }), /* @__PURE__ */ z("div", { children: [/* @__PURE__ */ R("label", {
139
+ htmlFor: "ch-label",
140
+ className: W,
141
+ children: "Label"
142
+ }), /* @__PURE__ */ R("input", {
143
+ id: "ch-label",
144
+ type: "text",
145
+ value: o,
146
+ onChange: (e) => s(e.target.value),
147
+ placeholder: "e.g. Billing inbox",
148
+ className: U
149
+ })] })]
150
+ }),
151
+ /* @__PURE__ */ z("div", { children: [/* @__PURE__ */ z("label", {
152
+ htmlFor: "ch-address",
153
+ className: W,
154
+ children: ["Address ", /* @__PURE__ */ R("span", {
155
+ className: "text-status-error-text",
156
+ children: "*"
157
+ })]
158
+ }), /* @__PURE__ */ R("input", {
159
+ id: "ch-address",
160
+ type: "text",
161
+ value: i,
162
+ onChange: (e) => a(e.target.value),
163
+ placeholder: "e.g. billing@acme.com, +1…, a webhook URL",
164
+ className: U,
165
+ required: !0
166
+ })] }),
167
+ /* @__PURE__ */ z("label", {
168
+ className: "flex items-center gap-2 text-sm text-text-primary",
169
+ children: [/* @__PURE__ */ R("input", {
170
+ type: "checkbox",
171
+ checked: l,
172
+ onChange: (e) => u(e.target.checked),
173
+ className: "size-4 rounded border-border-subtle"
174
+ }), "Ingest messages from this channel"]
175
+ }),
176
+ /* @__PURE__ */ z("div", {
177
+ className: "flex justify-end gap-3",
178
+ children: [/* @__PURE__ */ R("button", {
179
+ type: "button",
180
+ onClick: e,
181
+ className: "rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken",
182
+ children: "Cancel"
183
+ }), /* @__PURE__ */ R("button", {
184
+ type: "submit",
185
+ disabled: t.isPending,
186
+ 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",
187
+ children: t.isPending ? "Adding…" : "Add channel"
188
+ })]
189
+ })
190
+ ]
191
+ })
192
+ });
193
+ }
194
+ function te({ channel: e, onClose: t }) {
195
+ let n = g(), [r, i] = v(""), [a, o] = v(""), [s, l] = v("OTHER"), [u, d] = v(!1), [f, p] = v(null);
196
+ return /* @__PURE__ */ R(c, {
197
+ title: "Log test message",
198
+ description: `Simulate an ingested message from ${e.label || e.address}.`,
199
+ onClose: t,
200
+ children: /* @__PURE__ */ z("form", {
201
+ onSubmit: (i) => {
202
+ i.preventDefault(), p(null), n.mutate({
203
+ channelId: e.id,
204
+ receivedAt: (/* @__PURE__ */ new Date()).toISOString(),
205
+ category: s,
206
+ actionRequired: u,
207
+ ...r.trim() ? { subject: r.trim() } : {},
208
+ ...a.trim() ? { preview: a.trim() } : {}
209
+ }, {
210
+ onSuccess: () => t(),
211
+ onError: (e) => p(e instanceof Error ? e.message : "Could not log the message.")
212
+ });
213
+ },
214
+ className: "space-y-4",
215
+ children: [
216
+ f && /* @__PURE__ */ R("div", {
217
+ className: "rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text",
218
+ children: f
219
+ }),
220
+ /* @__PURE__ */ z("div", { children: [/* @__PURE__ */ R("label", {
221
+ htmlFor: "msg-subject",
222
+ className: W,
223
+ children: "Subject"
224
+ }), /* @__PURE__ */ R("input", {
225
+ id: "msg-subject",
226
+ type: "text",
227
+ value: r,
228
+ onChange: (e) => i(e.target.value),
229
+ placeholder: "e.g. Your plan renews in 3 days",
230
+ className: U
231
+ })] }),
232
+ /* @__PURE__ */ z("div", { children: [/* @__PURE__ */ R("label", {
233
+ htmlFor: "msg-preview",
234
+ className: W,
235
+ children: "Preview"
236
+ }), /* @__PURE__ */ R("textarea", {
237
+ id: "msg-preview",
238
+ value: a,
239
+ onChange: (e) => o(e.target.value),
240
+ rows: 2,
241
+ className: U
242
+ })] }),
243
+ /* @__PURE__ */ z("div", { children: [/* @__PURE__ */ R("label", {
244
+ htmlFor: "msg-category",
245
+ className: W,
246
+ children: "Category"
247
+ }), /* @__PURE__ */ R("select", {
248
+ id: "msg-category",
249
+ value: s,
250
+ onChange: (e) => l(e.target.value),
251
+ className: U,
252
+ children: K.map((e) => /* @__PURE__ */ R("option", {
253
+ value: e,
254
+ children: e.replace(/_/g, " ")
255
+ }, e))
256
+ })] }),
257
+ /* @__PURE__ */ z("label", {
258
+ className: "flex items-center gap-2 text-sm text-text-primary",
259
+ children: [/* @__PURE__ */ R("input", {
260
+ type: "checkbox",
261
+ checked: u,
262
+ onChange: (e) => d(e.target.checked),
263
+ className: "size-4 rounded border-border-subtle"
264
+ }), "Requires action"]
265
+ }),
266
+ /* @__PURE__ */ z("div", {
267
+ className: "flex justify-end gap-3",
268
+ children: [/* @__PURE__ */ R("button", {
269
+ type: "button",
270
+ onClick: t,
271
+ className: "rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken",
272
+ children: "Cancel"
273
+ }), /* @__PURE__ */ R("button", {
274
+ type: "submit",
275
+ disabled: n.isPending,
276
+ 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",
277
+ children: n.isPending ? "Logging…" : "Log message"
278
+ })]
279
+ })
280
+ ]
281
+ })
282
+ });
283
+ }
284
+ function ne({ channel: e, onVerify: t, verifying: n, onLog: r }) {
285
+ let a = Y[e.type] ?? M, s = e.status === "VERIFIED";
286
+ return /* @__PURE__ */ z(B, {
287
+ treatment: "metric",
288
+ className: "flex flex-col p-4",
289
+ children: [
290
+ /* @__PURE__ */ z("div", {
291
+ className: "flex items-start justify-between gap-2",
292
+ children: [/* @__PURE__ */ z("div", {
293
+ className: "flex min-w-0 items-center gap-2.5",
294
+ children: [/* @__PURE__ */ R("span", {
295
+ className: "flex size-9 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary",
296
+ children: /* @__PURE__ */ R(a, { className: "h-4 w-4" })
297
+ }), /* @__PURE__ */ z("div", {
298
+ className: "min-w-0",
299
+ children: [/* @__PURE__ */ R("p", {
300
+ className: "truncate text-sm font-semibold text-text-primary",
301
+ children: i(e.type)
302
+ }), e.label && /* @__PURE__ */ R("p", {
303
+ className: "truncate text-xs text-text-secondary",
304
+ children: e.label
305
+ })]
306
+ })]
307
+ }), /* @__PURE__ */ R(o, { status: e.status })]
308
+ }),
309
+ /* @__PURE__ */ z("div", {
310
+ className: "mt-3 border-t border-border-subtle pt-3",
311
+ children: [/* @__PURE__ */ R("p", {
312
+ className: "text-[11px] uppercase tracking-wide text-text-secondary",
313
+ children: e.ingestEnabled ? "Ingesting from" : "Ingest paused"
314
+ }), /* @__PURE__ */ R("p", {
315
+ className: "mt-0.5 truncate text-sm text-text-primary",
316
+ title: e.address,
317
+ children: e.address
318
+ })]
319
+ }),
320
+ /* @__PURE__ */ z("div", {
321
+ className: "mt-auto flex flex-wrap items-center gap-2 pt-3",
322
+ children: [
323
+ e.status === "PENDING" && /* @__PURE__ */ z("button", {
324
+ type: "button",
325
+ disabled: n,
326
+ onClick: () => t(e.id),
327
+ className: "inline-flex items-center gap-1 rounded-lg bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50",
328
+ children: [/* @__PURE__ */ R(b, { className: "h-3.5 w-3.5" }), " Verify"]
329
+ }),
330
+ s && e.ingestEnabled && /* @__PURE__ */ z("button", {
331
+ type: "button",
332
+ onClick: () => r(e),
333
+ className: "inline-flex items-center gap-1 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken",
334
+ children: [/* @__PURE__ */ R(N, { className: "h-3.5 w-3.5" }), " Log test message"]
335
+ }),
336
+ e.status === "DISABLED" && /* @__PURE__ */ R("span", {
337
+ className: "text-xs text-text-secondary",
338
+ children: "Channel disabled"
339
+ })
340
+ ]
341
+ })
342
+ ]
343
+ });
344
+ }
345
+ function re() {
346
+ let { data: e, isLoading: t, isError: n, refetch: r } = p(), i = _(), [a, o] = v(!1), [c, f] = v(null), m = e ?? [], h = m.filter((e) => e.status === "VERIFIED").length, g = m.filter((e) => e.status === "PENDING").length;
347
+ return /* @__PURE__ */ z(d, {
348
+ title: "Ingest channels",
349
+ description: "The sources the Universal Inbox pulls subscription communication from",
350
+ action: /* @__PURE__ */ z("button", {
351
+ type: "button",
352
+ onClick: () => o(!0),
353
+ className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text hover:opacity-90",
354
+ children: [/* @__PURE__ */ R(j, { className: "h-3.5 w-3.5" }), " Add channel"]
355
+ }),
356
+ children: [
357
+ t ? /* @__PURE__ */ R(u, { label: "Loading channels…" }) : n ? /* @__PURE__ */ R(l, {
358
+ message: "Could not load channels.",
359
+ onRetry: () => void r()
360
+ }) : m.length === 0 ? /* @__PURE__ */ R(s, {
361
+ illustration: "empty-data",
362
+ icon: /* @__PURE__ */ R(M, { className: "h-12 w-12" }),
363
+ title: "No channels yet",
364
+ description: "Add an email, SMS, Slack or webhook channel to start ingesting subscription communication."
365
+ }) : /* @__PURE__ */ z(L, { children: [(h > 0 || g > 0) && /* @__PURE__ */ z("div", {
366
+ className: "mb-3 flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-text-secondary",
367
+ children: [/* @__PURE__ */ z("span", {
368
+ className: "inline-flex items-center gap-1.5",
369
+ children: [
370
+ /* @__PURE__ */ R("span", {
371
+ className: "size-1.5 rounded-full bg-status-success-text",
372
+ "aria-hidden": !0
373
+ }),
374
+ h,
375
+ " verified"
376
+ ]
377
+ }), g > 0 && /* @__PURE__ */ z("span", {
378
+ className: "inline-flex items-center gap-1.5",
379
+ children: [
380
+ /* @__PURE__ */ R("span", {
381
+ className: "size-1.5 rounded-full bg-status-warning-text",
382
+ "aria-hidden": !0
383
+ }),
384
+ g,
385
+ " pending verification"
386
+ ]
387
+ })]
388
+ }), /* @__PURE__ */ R("div", {
389
+ className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3",
390
+ children: m.map((e) => /* @__PURE__ */ R(ne, {
391
+ channel: e,
392
+ verifying: i.isPending,
393
+ onVerify: (e) => i.mutate(e),
394
+ onLog: (e) => f(e)
395
+ }, e.id))
396
+ })] }),
397
+ a && /* @__PURE__ */ R(Q, { onClose: () => o(!1) }),
398
+ c && /* @__PURE__ */ R(te, {
399
+ channel: c,
400
+ onClose: () => f(null)
401
+ })
402
+ ]
403
+ });
404
+ }
405
+ function ie({ message: e, active: n, onSelect: r, onMarkRead: i, markPending: o, onViewRelationship: s }) {
406
+ let c = !e.isRead;
407
+ return /* @__PURE__ */ z("div", {
408
+ role: "button",
409
+ tabIndex: 0,
410
+ "aria-pressed": n,
411
+ onClick: r,
412
+ onKeyDown: (e) => {
413
+ (e.key === "Enter" || e.key === " ") && (e.preventDefault(), r());
414
+ },
415
+ className: t("relative flex cursor-pointer items-start gap-3 p-4 transition-colors", "focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] focus-visible:ring-inset", n ? "bg-bg-sunken" : c ? "bg-accent-wash" : "hover:bg-bg-sunken/60"),
416
+ children: [
417
+ c && /* @__PURE__ */ R("span", {
418
+ "aria-hidden": !0,
419
+ className: "absolute inset-y-0 left-0 w-0.5 bg-text-link"
420
+ }),
421
+ /* @__PURE__ */ R("span", {
422
+ className: "mt-0.5 shrink-0 text-text-secondary",
423
+ children: c ? /* @__PURE__ */ R(T, { className: "h-4 w-4 text-text-link" }) : /* @__PURE__ */ R(E, { className: "h-4 w-4" })
424
+ }),
425
+ /* @__PURE__ */ z("div", {
426
+ className: "min-w-0 flex-1",
427
+ children: [
428
+ /* @__PURE__ */ z("div", {
429
+ className: "flex items-center gap-2",
430
+ children: [
431
+ c && /* @__PURE__ */ R("span", {
432
+ "aria-label": "Unread",
433
+ className: "size-1.5 shrink-0 rounded-full bg-text-link"
434
+ }),
435
+ /* @__PURE__ */ R("p", {
436
+ className: t("min-w-0 flex-1 truncate text-sm", c ? "font-semibold text-text-primary" : "font-normal text-text-primary"),
437
+ children: e.subject || "(no subject)"
438
+ }),
439
+ /* @__PURE__ */ R("span", {
440
+ className: "shrink-0 text-xs tabular-nums text-text-secondary",
441
+ children: a(e.receivedAt)
442
+ })
443
+ ]
444
+ }),
445
+ e.preview && /* @__PURE__ */ R("p", {
446
+ className: "mt-0.5 line-clamp-2 text-xs text-text-secondary",
447
+ children: e.preview
448
+ }),
449
+ /* @__PURE__ */ z("div", {
450
+ className: "mt-1.5 flex flex-wrap items-center gap-2",
451
+ children: [
452
+ /* @__PURE__ */ R(X, { category: e.category }),
453
+ e.actionRequired && /* @__PURE__ */ R(Z, {}),
454
+ s && /* @__PURE__ */ R("button", {
455
+ type: "button",
456
+ onClick: (e) => {
457
+ e.stopPropagation(), s();
458
+ },
459
+ className: "text-xs font-medium text-text-link hover:underline",
460
+ children: "View relationship"
461
+ }),
462
+ c && /* @__PURE__ */ R("button", {
463
+ type: "button",
464
+ disabled: o,
465
+ onClick: (e) => {
466
+ e.stopPropagation(), i();
467
+ },
468
+ className: "ml-auto shrink-0 rounded-lg border border-border-subtle px-2.5 py-1 text-xs font-medium text-text-primary hover:bg-bg-sunken disabled:opacity-50",
469
+ children: "Mark read"
470
+ })
471
+ ]
472
+ })
473
+ ]
474
+ })
475
+ ]
476
+ });
477
+ }
478
+ function ae({ message: e, channel: t, onMarkRead: n, markPending: i, onViewRelationship: a }) {
479
+ if (!e) return /* @__PURE__ */ z(B, {
480
+ treatment: "metric",
481
+ className: "flex min-h-[20rem] flex-col items-center justify-center p-8 text-center",
482
+ children: [
483
+ /* @__PURE__ */ R(k, { className: "h-10 w-10 text-text-secondary" }),
484
+ /* @__PURE__ */ R("p", {
485
+ className: "mt-3 text-sm font-medium text-text-primary",
486
+ children: "Select a message"
487
+ }),
488
+ /* @__PURE__ */ R("p", {
489
+ className: "mt-1 max-w-[16rem] text-xs text-text-secondary",
490
+ children: "Pick any message from the list to read the full details here."
491
+ })
492
+ ]
493
+ });
494
+ let o = t ? Y[t.type] ?? M : M, s = e.categoryConfidence == null ? null : Math.round(Math.max(0, Math.min(1, e.categoryConfidence)) * 100);
495
+ return /* @__PURE__ */ z(B, {
496
+ treatment: "metric",
497
+ className: "flex flex-col p-5",
498
+ children: [
499
+ /* @__PURE__ */ z("div", {
500
+ className: "flex items-start justify-between gap-3",
501
+ children: [/* @__PURE__ */ R("h3", {
502
+ className: "text-base font-semibold text-text-primary",
503
+ children: e.subject || "(no subject)"
504
+ }), !e.isRead && /* @__PURE__ */ R("button", {
505
+ type: "button",
506
+ disabled: i,
507
+ onClick: n,
508
+ className: "shrink-0 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken disabled:opacity-50",
509
+ children: "Mark read"
510
+ })]
511
+ }),
512
+ t && /* @__PURE__ */ z("div", {
513
+ className: "mt-2 flex items-center gap-2 text-sm",
514
+ children: [/* @__PURE__ */ R(o, { className: "h-4 w-4 shrink-0 text-text-secondary" }), /* @__PURE__ */ R("span", {
515
+ className: "truncate text-text-secondary",
516
+ title: t.address,
517
+ children: t.address
518
+ })]
519
+ }),
520
+ /* @__PURE__ */ z("div", {
521
+ className: "mt-3 flex flex-wrap items-center gap-2",
522
+ children: [
523
+ /* @__PURE__ */ R(X, { category: e.category }),
524
+ e.actionRequired && /* @__PURE__ */ R(Z, {}),
525
+ /* @__PURE__ */ R("span", {
526
+ className: "text-xs text-text-secondary",
527
+ children: r(e.receivedAt)
528
+ })
529
+ ]
530
+ }),
531
+ s != null && /* @__PURE__ */ z("div", {
532
+ className: "mt-4 rounded-lg border border-border-subtle bg-bg-surface p-3",
533
+ children: [/* @__PURE__ */ z("div", {
534
+ className: "flex items-center justify-between text-xs",
535
+ children: [/* @__PURE__ */ z("span", {
536
+ className: "inline-flex items-center gap-1.5 font-medium text-text-primary",
537
+ children: [/* @__PURE__ */ R(F, { className: "h-3.5 w-3.5 text-text-link" }), " AI categorization"]
538
+ }), /* @__PURE__ */ z("span", {
539
+ className: "tabular-nums text-text-secondary",
540
+ children: [s, "% confidence"]
541
+ })]
542
+ }), /* @__PURE__ */ R(f, {
543
+ value: s,
544
+ max: 100,
545
+ tone: s >= 66 ? "positive" : s >= 33 ? "accent" : "warning",
546
+ className: "mt-2"
547
+ })]
548
+ }),
549
+ /* @__PURE__ */ R("div", {
550
+ className: "mt-4 border-t border-border-subtle pt-4",
551
+ children: e.preview ? /* @__PURE__ */ R("p", {
552
+ className: "whitespace-pre-wrap text-sm leading-relaxed text-text-primary",
553
+ children: e.preview
554
+ }) : /* @__PURE__ */ R("p", {
555
+ className: "text-sm italic text-text-secondary",
556
+ children: "No preview available for this message."
557
+ })
558
+ }),
559
+ e.actionRequired && /* @__PURE__ */ z("div", {
560
+ className: "mt-4 flex items-start gap-2 rounded-lg border border-status-warning-text/30 bg-status-warning-bg-subtle p-3",
561
+ children: [/* @__PURE__ */ R(P, { className: "mt-0.5 h-4 w-4 shrink-0 text-status-warning-text" }), /* @__PURE__ */ R("p", {
562
+ className: "text-xs text-status-warning-text",
563
+ children: "This message needs your attention — review it before its provider deadline."
564
+ })]
565
+ }),
566
+ a && /* @__PURE__ */ z("button", {
567
+ type: "button",
568
+ onClick: a,
569
+ className: "mt-4 inline-flex items-center gap-1 self-start text-sm font-medium text-text-link hover:underline",
570
+ children: ["View relationship ", /* @__PURE__ */ R(S, { className: "h-4 w-4" })]
571
+ })
572
+ ]
573
+ });
574
+ }
575
+ function $({ active: e, onClick: n, children: r, tone: i = "default" }) {
576
+ return /* @__PURE__ */ R("button", {
577
+ type: "button",
578
+ onClick: n,
579
+ "aria-pressed": e,
580
+ className: t("inline-flex items-center gap-1.5 rounded-full border px-3 py-1.5 text-xs font-medium transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]", e ? i === "warning" ? "border-transparent bg-status-warning-bg-subtle text-status-warning-text" : "border-transparent bg-action-primary-bg text-action-primary-text" : "border-border-subtle text-text-secondary hover:bg-bg-sunken hover:text-text-primary"),
581
+ children: r
582
+ });
583
+ }
584
+ function oe() {
585
+ let { navigateTo: r } = e(), [i, a] = v(0), [o, c] = v(""), [d, f] = v(!1), [m, g] = v(!1), [_, b] = v(null), { data: x, isLoading: S, isError: C, refetch: E, isFetching: D } = h({
586
+ category: o || void 0,
587
+ unreadOnly: d || void 0,
52
588
  pagination: {
53
- limit: y,
54
- offset: S * y
589
+ limit: H,
590
+ offset: i * H
55
591
  }
56
- }), N = l(), P = O?.items ?? [], F = O?.pageInfo.hasMore ?? !1;
57
- return /* @__PURE__ */ _(n, {
592
+ }), { data: O } = p(), k = ee(), A = x?.items ?? [], j = x?.pageInfo.hasMore ?? !1, M = m ? A.filter((e) => e.actionRequired) : A, N = A.filter((e) => !e.isRead).length, P = A.filter((e) => e.actionRequired).length, F = new Map((O ?? []).map((e) => [e.id, e])), I = M.find((e) => e.id === _) ?? null, L = I ? F.get(I.channelId) : void 0, B = () => {
593
+ a(0), b(null);
594
+ };
595
+ return /* @__PURE__ */ z(n, {
58
596
  title: "Universal Inbox",
59
597
  description: "Every subscription communication, merged and categorized",
60
598
  children: [
61
- /* @__PURE__ */ g(v, {
599
+ /* @__PURE__ */ R(V, {
62
600
  className: "mb-4",
63
601
  children: "Subscription communication is noisy and scattered. The Universal Inbox merges every channel a provider uses and uses AI to categorize each message so you never miss a renewal, required action or security notice."
64
602
  }),
65
- /* @__PURE__ */ _("div", {
66
- className: "flex flex-wrap items-center gap-3",
67
- children: [/* @__PURE__ */ g("select", {
68
- value: w,
69
- onChange: (e) => {
70
- T(e.target.value), C(0);
71
- },
72
- 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)]",
73
- children: b.map((e) => /* @__PURE__ */ g("option", {
74
- value: e.value,
603
+ /* @__PURE__ */ R("div", {
604
+ className: "-mx-1 flex gap-2 overflow-x-auto px-1 pb-1",
605
+ children: G.map((e) => /* @__PURE__ */ R("span", {
606
+ className: "shrink-0",
607
+ children: /* @__PURE__ */ R($, {
608
+ active: o === e.value,
609
+ onClick: () => {
610
+ c(e.value), B();
611
+ },
75
612
  children: e.label
76
- }, e.value))
77
- }), /* @__PURE__ */ _("label", {
78
- className: "flex items-center gap-2 text-sm text-text-primary",
79
- children: [/* @__PURE__ */ g("input", {
80
- type: "checkbox",
81
- checked: E,
82
- onChange: (e) => {
83
- D(e.target.checked), C(0);
613
+ })
614
+ }, e.value))
615
+ }),
616
+ /* @__PURE__ */ z("div", {
617
+ className: "mt-3 flex flex-wrap items-center gap-2",
618
+ children: [
619
+ /* @__PURE__ */ z($, {
620
+ active: m,
621
+ tone: "warning",
622
+ onClick: () => {
623
+ g((e) => !e), b(null);
84
624
  },
85
- className: "size-4 rounded border-border-subtle"
86
- }), "Unread only"]
87
- })]
625
+ children: [/* @__PURE__ */ R(y, { className: "h-3.5 w-3.5" }), " Action required"]
626
+ }),
627
+ /* @__PURE__ */ z($, {
628
+ active: d,
629
+ onClick: () => {
630
+ f((e) => !e), B();
631
+ },
632
+ children: [/* @__PURE__ */ R(T, { className: "h-3.5 w-3.5" }), " Unread only"]
633
+ }),
634
+ !S && !C && A.length > 0 && /* @__PURE__ */ z("p", {
635
+ className: "ml-auto text-xs text-text-secondary",
636
+ children: [
637
+ /* @__PURE__ */ R("span", {
638
+ className: "font-medium text-text-primary tabular-nums",
639
+ children: N
640
+ }),
641
+ " unread",
642
+ " · ",
643
+ /* @__PURE__ */ R("span", {
644
+ className: "font-medium text-text-primary tabular-nums",
645
+ children: P
646
+ }),
647
+ " need action",
648
+ D ? " · refreshing…" : ""
649
+ ]
650
+ })
651
+ ]
88
652
  }),
89
- k ? /* @__PURE__ */ g(s, { label: "Loading inbox…" }) : A ? /* @__PURE__ */ g(o, {
653
+ S ? /* @__PURE__ */ R(u, { label: "Loading inbox…" }) : C ? /* @__PURE__ */ R(l, {
90
654
  message: "Could not load the inbox.",
91
- onRetry: () => void j()
92
- }) : P.length === 0 ? /* @__PURE__ */ g("div", {
655
+ onRetry: () => void E()
656
+ }) : A.length === 0 ? /* @__PURE__ */ R("div", {
93
657
  className: "mt-6",
94
- children: /* @__PURE__ */ g(a, {
658
+ children: /* @__PURE__ */ R(s, {
95
659
  illustration: "empty-inbox",
96
- icon: /* @__PURE__ */ g(f, { className: "h-12 w-12" }),
660
+ icon: /* @__PURE__ */ R(w, { className: "h-12 w-12" }),
97
661
  title: "Inbox is empty",
98
- description: E || w ? "No messages match these filters." : "Merged subscription communications will appear here once channels start ingesting."
662
+ description: d || o ? "No messages match these filters." : "Merged subscription communications will appear here once channels start ingesting."
99
663
  })
100
- }) : /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g("div", {
101
- className: "mt-6 divide-y divide-border-subtle rounded-lg border border-border-subtle bg-bg-surface",
102
- children: P.map((e) => /* @__PURE__ */ _("div", {
103
- className: t("flex items-start gap-3 p-4", !e.isRead && "bg-accent-wash"),
104
- children: [
105
- /* @__PURE__ */ g("span", {
106
- className: "mt-0.5 shrink-0 text-text-secondary",
107
- children: e.isRead ? /* @__PURE__ */ g(m, { className: "h-4 w-4" }) : /* @__PURE__ */ g(p, { className: "h-4 w-4 text-text-link" })
108
- }),
109
- /* @__PURE__ */ _("div", {
110
- className: "min-w-0 flex-1",
664
+ }) : /* @__PURE__ */ z("div", {
665
+ className: "mt-5 grid grid-cols-1 gap-5 lg:grid-cols-[minmax(0,1fr)_380px]",
666
+ children: [/* @__PURE__ */ z("div", {
667
+ className: "min-w-0",
668
+ children: [M.length === 0 ? /* @__PURE__ */ z("div", {
669
+ className: "rounded-xl border border-border-subtle bg-bg-surface p-8 text-center",
670
+ children: [
671
+ /* @__PURE__ */ R(y, { className: "mx-auto h-8 w-8 text-text-secondary" }),
672
+ /* @__PURE__ */ R("p", {
673
+ className: "mt-2 text-sm font-medium text-text-primary",
674
+ children: "No action-required messages on this page"
675
+ }),
676
+ /* @__PURE__ */ R("p", {
677
+ className: "mt-1 text-xs text-text-secondary",
678
+ children: "Turn off the “Action required” lens or move to another page."
679
+ })
680
+ ]
681
+ }) : /* @__PURE__ */ R("div", {
682
+ className: "divide-y divide-border-subtle overflow-hidden rounded-xl border border-border-subtle bg-bg-surface",
683
+ children: M.map((e) => /* @__PURE__ */ R(ie, {
684
+ message: e,
685
+ active: e.id === _,
686
+ onSelect: () => b(e.id),
687
+ onMarkRead: () => k.mutate(e.id),
688
+ markPending: k.isPending,
689
+ onViewRelationship: e.relationshipId ? () => r(`/relationships/${e.relationshipId}`) : null
690
+ }, e.id))
691
+ }), /* @__PURE__ */ z("div", {
692
+ className: "mt-4 flex items-center justify-between",
693
+ children: [/* @__PURE__ */ z("p", {
694
+ className: "text-xs text-text-secondary",
111
695
  children: [
112
- /* @__PURE__ */ _("div", {
113
- className: "flex items-center gap-2",
114
- children: [/* @__PURE__ */ g("p", {
115
- className: t("truncate text-sm", e.isRead ? "font-normal text-text-primary" : "font-semibold text-text-primary"),
116
- children: e.subject || "(no subject)"
117
- }), e.actionRequired && /* @__PURE__ */ _("span", {
118
- title: "Action required",
119
- className: "inline-flex items-center gap-1 rounded-full bg-status-warning-bg-subtle px-2 py-0.5 text-[10px] font-medium text-status-warning-text",
120
- children: [/* @__PURE__ */ g(d, { className: "h-3 w-3" }), " Action"]
121
- })]
122
- }),
123
- e.preview && /* @__PURE__ */ g("p", {
124
- className: "mt-0.5 line-clamp-2 text-xs text-text-secondary",
125
- children: e.preview
126
- }),
127
- /* @__PURE__ */ _("div", {
128
- className: "mt-1.5 flex flex-wrap items-center gap-2",
129
- children: [
130
- /* @__PURE__ */ g(i, { status: e.category }),
131
- /* @__PURE__ */ g("span", {
132
- className: "text-xs text-text-secondary",
133
- children: r(e.receivedAt)
134
- }),
135
- e.relationshipId && /* @__PURE__ */ g("button", {
136
- type: "button",
137
- onClick: () => x(`/relationships/${e.relationshipId}`),
138
- className: "text-xs font-medium text-text-link hover:underline",
139
- children: "View relationship"
140
- })
141
- ]
142
- })
696
+ "Page ",
697
+ i + 1,
698
+ D ? " · refreshing…" : ""
143
699
  ]
144
- }),
145
- !e.isRead && /* @__PURE__ */ g("button", {
146
- type: "button",
147
- disabled: N.isPending,
148
- onClick: () => N.mutate(e.id),
149
- className: "shrink-0 rounded-lg border border-border-subtle px-2.5 py-1 text-xs font-medium text-text-primary hover:bg-bg-sunken disabled:opacity-50",
150
- children: "Mark read"
151
- })
152
- ]
153
- }, e.id))
154
- }), /* @__PURE__ */ _("div", {
155
- className: "mt-4 flex items-center justify-between",
156
- children: [/* @__PURE__ */ _("p", {
157
- className: "text-xs text-text-secondary",
158
- children: [
159
- "Page ",
160
- S + 1,
161
- M ? " · refreshing…" : ""
162
- ]
163
- }), /* @__PURE__ */ _("div", {
164
- className: "flex gap-2",
165
- children: [/* @__PURE__ */ g("button", {
166
- type: "button",
167
- disabled: S === 0,
168
- onClick: () => C((e) => Math.max(0, e - 1)),
169
- className: t("rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium", S === 0 ? "cursor-not-allowed text-text-secondary opacity-50" : "text-text-primary hover:bg-bg-sunken"),
170
- children: "Previous"
171
- }), /* @__PURE__ */ g("button", {
172
- type: "button",
173
- disabled: !F,
174
- onClick: () => C((e) => e + 1),
175
- className: t("rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium", F ? "text-text-primary hover:bg-bg-sunken" : "cursor-not-allowed text-text-secondary opacity-50"),
176
- children: "Next"
700
+ }), /* @__PURE__ */ z("div", {
701
+ className: "flex gap-2",
702
+ children: [/* @__PURE__ */ R("button", {
703
+ type: "button",
704
+ disabled: i === 0,
705
+ onClick: () => {
706
+ a((e) => Math.max(0, e - 1)), b(null);
707
+ },
708
+ className: t("rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium", i === 0 ? "cursor-not-allowed text-text-secondary opacity-50" : "text-text-primary hover:bg-bg-sunken"),
709
+ children: "Previous"
710
+ }), /* @__PURE__ */ R("button", {
711
+ type: "button",
712
+ disabled: !j,
713
+ onClick: () => {
714
+ a((e) => e + 1), b(null);
715
+ },
716
+ className: t("rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium", j ? "text-text-primary hover:bg-bg-sunken" : "cursor-not-allowed text-text-secondary opacity-50"),
717
+ children: "Next"
718
+ })]
719
+ })]
177
720
  })]
721
+ }), /* @__PURE__ */ R("aside", {
722
+ className: "hidden lg:block",
723
+ children: /* @__PURE__ */ R("div", {
724
+ className: "lg:sticky lg:top-4",
725
+ children: /* @__PURE__ */ R(ae, {
726
+ message: I,
727
+ channel: L,
728
+ onMarkRead: () => I && k.mutate(I.id),
729
+ markPending: k.isPending,
730
+ onViewRelationship: I?.relationshipId ? () => r(`/relationships/${I.relationshipId}`) : null
731
+ })
732
+ })
178
733
  })]
179
- })] })
734
+ }),
735
+ /* @__PURE__ */ R(re, {})
180
736
  ]
181
737
  });
182
738
  }
183
739
  //#endregion
184
- export { x as InboxPage };
740
+ export { oe as InboxPage };
185
741
 
186
742
  //# sourceMappingURL=InboxPage.js.map