@burdenoff/microfe-subscriberbot 2026.706.1 → 2026.706.2
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/components/Modal.js +51 -0
- package/dist/subscriberbot/components/Modal.js.map +1 -0
- package/dist/subscriberbot/hooks/useSubscriberBotApi.js +358 -163
- package/dist/subscriberbot/hooks/useSubscriberBotApi.js.map +1 -1
- package/dist/subscriberbot/pages/InboxPage.js +339 -66
- package/dist/subscriberbot/pages/InboxPage.js.map +1 -1
- package/dist/subscriberbot/pages/InvoicesPage.js +496 -73
- package/dist/subscriberbot/pages/InvoicesPage.js.map +1 -1
- package/dist/subscriberbot/pages/WorkflowsPage.js +136 -150
- package/dist/subscriberbot/pages/WorkflowsPage.js.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentDetailPage.js +197 -58
- package/dist/subscriberbot/pages/agents/AgentDetailPage.js.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentsListPage.js +2 -0
- package/dist/subscriberbot/pages/agents/AgentsListPage.js.map +1 -1
- package/dist/subscriberbot/pages/providers/ProviderDetailPage.js +538 -71
- package/dist/subscriberbot/pages/providers/ProviderDetailPage.js.map +1 -1
- package/dist/subscriberbot/pages/providers/ProvidersListPage.js +2 -0
- package/dist/subscriberbot/pages/providers/ProvidersListPage.js.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js +664 -126
- package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipsListPage.js +18 -16
- package/dist/subscriberbot/pages/relationships/RelationshipsListPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,17 +1,18 @@
|
|
|
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 {
|
|
5
|
-
import { StatusBadge as
|
|
6
|
-
import { EmptyState as
|
|
7
|
-
import { ErrorView as
|
|
8
|
-
import { useInboxMessages as
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
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, Section as l } from "../components/StateViews.js";
|
|
8
|
+
import { useChannels as u, useCreateChannel as d, useInboxMessages as f, useIngestMessage as p, useMarkMessageRead as m, useVerifyChannel as h } from "../hooks/useSubscriberBotApi.js";
|
|
9
|
+
import { Modal as g } from "../components/Modal.js";
|
|
10
|
+
import { useState as _ } from "react";
|
|
11
|
+
import { AlertTriangle as v, BadgeCheck as y, Inbox as b, Mail as x, MailOpen as S, Plus as C, Radio as w, Send as T } from "lucide-react";
|
|
12
|
+
import { Fragment as E, jsx as D, jsxs as O } from "react/jsx-runtime";
|
|
13
|
+
import { PagePurpose as k } from "@burdenoff/fe-libs/ui";
|
|
13
14
|
//#region src/subscriberbot/pages/InboxPage.tsx
|
|
14
|
-
var
|
|
15
|
+
var A = 25, j = "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)]", M = "mb-1 block text-xs font-medium text-text-secondary", N = [
|
|
15
16
|
{
|
|
16
17
|
value: "",
|
|
17
18
|
label: "All categories"
|
|
@@ -44,97 +45,368 @@ var y = 25, b = [
|
|
|
44
45
|
value: "OTHER",
|
|
45
46
|
label: "Other"
|
|
46
47
|
}
|
|
48
|
+
], P = [
|
|
49
|
+
"BILLING",
|
|
50
|
+
"RENEWAL",
|
|
51
|
+
"REQUIRED_ACTION",
|
|
52
|
+
"SECURITY_ALERT",
|
|
53
|
+
"PRODUCT_UPDATE",
|
|
54
|
+
"MARKETING",
|
|
55
|
+
"OTHER"
|
|
56
|
+
], F = [
|
|
57
|
+
"EMAIL",
|
|
58
|
+
"SMS",
|
|
59
|
+
"SLACK",
|
|
60
|
+
"DISCORD",
|
|
61
|
+
"WHATSAPP",
|
|
62
|
+
"PUSH",
|
|
63
|
+
"WEBHOOK"
|
|
47
64
|
];
|
|
48
|
-
function
|
|
49
|
-
let
|
|
50
|
-
|
|
51
|
-
|
|
65
|
+
function I({ onClose: e }) {
|
|
66
|
+
let t = d(), [n, r] = _("EMAIL"), [i, a] = _(""), [o, s] = _(""), [c, l] = _(!0), [u, f] = _(null);
|
|
67
|
+
return /* @__PURE__ */ D(g, {
|
|
68
|
+
title: "Add channel",
|
|
69
|
+
onClose: e,
|
|
70
|
+
maxWidthClassName: "max-w-xl",
|
|
71
|
+
children: /* @__PURE__ */ O("form", {
|
|
72
|
+
onSubmit: (r) => {
|
|
73
|
+
if (r.preventDefault(), f(null), !i.trim()) {
|
|
74
|
+
f("An address is required.");
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
t.mutate({
|
|
78
|
+
type: n,
|
|
79
|
+
address: i.trim(),
|
|
80
|
+
ingestEnabled: c,
|
|
81
|
+
...o.trim() ? { label: o.trim() } : {}
|
|
82
|
+
}, {
|
|
83
|
+
onSuccess: () => e(),
|
|
84
|
+
onError: (e) => f(e instanceof Error ? e.message : "Could not add channel.")
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
className: "space-y-4",
|
|
88
|
+
children: [
|
|
89
|
+
u && /* @__PURE__ */ D("div", {
|
|
90
|
+
className: "rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text",
|
|
91
|
+
children: u
|
|
92
|
+
}),
|
|
93
|
+
/* @__PURE__ */ O("div", {
|
|
94
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
|
|
95
|
+
children: [/* @__PURE__ */ O("div", { children: [/* @__PURE__ */ D("label", {
|
|
96
|
+
htmlFor: "ch-type",
|
|
97
|
+
className: M,
|
|
98
|
+
children: "Type"
|
|
99
|
+
}), /* @__PURE__ */ D("select", {
|
|
100
|
+
id: "ch-type",
|
|
101
|
+
value: n,
|
|
102
|
+
onChange: (e) => r(e.target.value),
|
|
103
|
+
className: j,
|
|
104
|
+
children: F.map((e) => /* @__PURE__ */ D("option", {
|
|
105
|
+
value: e,
|
|
106
|
+
children: e.replace(/_/g, " ")
|
|
107
|
+
}, e))
|
|
108
|
+
})] }), /* @__PURE__ */ O("div", { children: [/* @__PURE__ */ D("label", {
|
|
109
|
+
htmlFor: "ch-label",
|
|
110
|
+
className: M,
|
|
111
|
+
children: "Label"
|
|
112
|
+
}), /* @__PURE__ */ D("input", {
|
|
113
|
+
id: "ch-label",
|
|
114
|
+
type: "text",
|
|
115
|
+
value: o,
|
|
116
|
+
onChange: (e) => s(e.target.value),
|
|
117
|
+
placeholder: "e.g. Billing inbox",
|
|
118
|
+
className: j
|
|
119
|
+
})] })]
|
|
120
|
+
}),
|
|
121
|
+
/* @__PURE__ */ O("div", { children: [/* @__PURE__ */ O("label", {
|
|
122
|
+
htmlFor: "ch-address",
|
|
123
|
+
className: M,
|
|
124
|
+
children: ["Address ", /* @__PURE__ */ D("span", {
|
|
125
|
+
className: "text-status-error-text",
|
|
126
|
+
children: "*"
|
|
127
|
+
})]
|
|
128
|
+
}), /* @__PURE__ */ D("input", {
|
|
129
|
+
id: "ch-address",
|
|
130
|
+
type: "text",
|
|
131
|
+
value: i,
|
|
132
|
+
onChange: (e) => a(e.target.value),
|
|
133
|
+
placeholder: "e.g. billing@acme.com, +1…, a webhook URL",
|
|
134
|
+
className: j,
|
|
135
|
+
required: !0
|
|
136
|
+
})] }),
|
|
137
|
+
/* @__PURE__ */ O("label", {
|
|
138
|
+
className: "flex items-center gap-2 text-sm text-text-primary",
|
|
139
|
+
children: [/* @__PURE__ */ D("input", {
|
|
140
|
+
type: "checkbox",
|
|
141
|
+
checked: c,
|
|
142
|
+
onChange: (e) => l(e.target.checked),
|
|
143
|
+
className: "size-4 rounded border-border-subtle"
|
|
144
|
+
}), "Ingest messages from this channel"]
|
|
145
|
+
}),
|
|
146
|
+
/* @__PURE__ */ O("div", {
|
|
147
|
+
className: "flex justify-end gap-3",
|
|
148
|
+
children: [/* @__PURE__ */ D("button", {
|
|
149
|
+
type: "button",
|
|
150
|
+
onClick: e,
|
|
151
|
+
className: "rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken",
|
|
152
|
+
children: "Cancel"
|
|
153
|
+
}), /* @__PURE__ */ D("button", {
|
|
154
|
+
type: "submit",
|
|
155
|
+
disabled: t.isPending,
|
|
156
|
+
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",
|
|
157
|
+
children: t.isPending ? "Adding…" : "Add channel"
|
|
158
|
+
})]
|
|
159
|
+
})
|
|
160
|
+
]
|
|
161
|
+
})
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
function L({ channel: e, onClose: t }) {
|
|
165
|
+
let n = p(), [r, i] = _(""), [a, o] = _(""), [s, c] = _("OTHER"), [l, u] = _(!1), [d, f] = _(null);
|
|
166
|
+
return /* @__PURE__ */ D(g, {
|
|
167
|
+
title: "Log test message",
|
|
168
|
+
description: `Simulate an ingested message from ${e.label || e.address}.`,
|
|
169
|
+
onClose: t,
|
|
170
|
+
children: /* @__PURE__ */ O("form", {
|
|
171
|
+
onSubmit: (i) => {
|
|
172
|
+
i.preventDefault(), f(null), n.mutate({
|
|
173
|
+
channelId: e.id,
|
|
174
|
+
receivedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
175
|
+
category: s,
|
|
176
|
+
actionRequired: l,
|
|
177
|
+
...r.trim() ? { subject: r.trim() } : {},
|
|
178
|
+
...a.trim() ? { preview: a.trim() } : {}
|
|
179
|
+
}, {
|
|
180
|
+
onSuccess: () => t(),
|
|
181
|
+
onError: (e) => f(e instanceof Error ? e.message : "Could not log the message.")
|
|
182
|
+
});
|
|
183
|
+
},
|
|
184
|
+
className: "space-y-4",
|
|
185
|
+
children: [
|
|
186
|
+
d && /* @__PURE__ */ D("div", {
|
|
187
|
+
className: "rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text",
|
|
188
|
+
children: d
|
|
189
|
+
}),
|
|
190
|
+
/* @__PURE__ */ O("div", { children: [/* @__PURE__ */ D("label", {
|
|
191
|
+
htmlFor: "msg-subject",
|
|
192
|
+
className: M,
|
|
193
|
+
children: "Subject"
|
|
194
|
+
}), /* @__PURE__ */ D("input", {
|
|
195
|
+
id: "msg-subject",
|
|
196
|
+
type: "text",
|
|
197
|
+
value: r,
|
|
198
|
+
onChange: (e) => i(e.target.value),
|
|
199
|
+
placeholder: "e.g. Your plan renews in 3 days",
|
|
200
|
+
className: j
|
|
201
|
+
})] }),
|
|
202
|
+
/* @__PURE__ */ O("div", { children: [/* @__PURE__ */ D("label", {
|
|
203
|
+
htmlFor: "msg-preview",
|
|
204
|
+
className: M,
|
|
205
|
+
children: "Preview"
|
|
206
|
+
}), /* @__PURE__ */ D("textarea", {
|
|
207
|
+
id: "msg-preview",
|
|
208
|
+
value: a,
|
|
209
|
+
onChange: (e) => o(e.target.value),
|
|
210
|
+
rows: 2,
|
|
211
|
+
className: j
|
|
212
|
+
})] }),
|
|
213
|
+
/* @__PURE__ */ O("div", { children: [/* @__PURE__ */ D("label", {
|
|
214
|
+
htmlFor: "msg-category",
|
|
215
|
+
className: M,
|
|
216
|
+
children: "Category"
|
|
217
|
+
}), /* @__PURE__ */ D("select", {
|
|
218
|
+
id: "msg-category",
|
|
219
|
+
value: s,
|
|
220
|
+
onChange: (e) => c(e.target.value),
|
|
221
|
+
className: j,
|
|
222
|
+
children: P.map((e) => /* @__PURE__ */ D("option", {
|
|
223
|
+
value: e,
|
|
224
|
+
children: e.replace(/_/g, " ")
|
|
225
|
+
}, e))
|
|
226
|
+
})] }),
|
|
227
|
+
/* @__PURE__ */ O("label", {
|
|
228
|
+
className: "flex items-center gap-2 text-sm text-text-primary",
|
|
229
|
+
children: [/* @__PURE__ */ D("input", {
|
|
230
|
+
type: "checkbox",
|
|
231
|
+
checked: l,
|
|
232
|
+
onChange: (e) => u(e.target.checked),
|
|
233
|
+
className: "size-4 rounded border-border-subtle"
|
|
234
|
+
}), "Requires action"]
|
|
235
|
+
}),
|
|
236
|
+
/* @__PURE__ */ O("div", {
|
|
237
|
+
className: "flex justify-end gap-3",
|
|
238
|
+
children: [/* @__PURE__ */ D("button", {
|
|
239
|
+
type: "button",
|
|
240
|
+
onClick: t,
|
|
241
|
+
className: "rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken",
|
|
242
|
+
children: "Cancel"
|
|
243
|
+
}), /* @__PURE__ */ D("button", {
|
|
244
|
+
type: "submit",
|
|
245
|
+
disabled: n.isPending,
|
|
246
|
+
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",
|
|
247
|
+
children: n.isPending ? "Logging…" : "Log message"
|
|
248
|
+
})]
|
|
249
|
+
})
|
|
250
|
+
]
|
|
251
|
+
})
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
function R() {
|
|
255
|
+
let { data: e, isLoading: t, isError: n, refetch: i } = u(), d = h(), [f, p] = _(!1), [m, g] = _(null), v = e ?? [];
|
|
256
|
+
return /* @__PURE__ */ O(l, {
|
|
257
|
+
title: "Ingest channels",
|
|
258
|
+
description: "The sources the Universal Inbox pulls subscription communication from",
|
|
259
|
+
action: /* @__PURE__ */ O("button", {
|
|
260
|
+
type: "button",
|
|
261
|
+
onClick: () => p(!0),
|
|
262
|
+
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",
|
|
263
|
+
children: [/* @__PURE__ */ D(C, { className: "h-3.5 w-3.5" }), " Add channel"]
|
|
264
|
+
}),
|
|
265
|
+
children: [
|
|
266
|
+
t ? /* @__PURE__ */ D(c, { label: "Loading channels…" }) : n ? /* @__PURE__ */ D(s, {
|
|
267
|
+
message: "Could not load channels.",
|
|
268
|
+
onRetry: () => void i()
|
|
269
|
+
}) : v.length === 0 ? /* @__PURE__ */ D(o, {
|
|
270
|
+
illustration: "empty-data",
|
|
271
|
+
icon: /* @__PURE__ */ D(w, { className: "h-12 w-12" }),
|
|
272
|
+
title: "No channels yet",
|
|
273
|
+
description: "Add an email, SMS, Slack or webhook channel to start ingesting subscription communication."
|
|
274
|
+
}) : /* @__PURE__ */ D("div", {
|
|
275
|
+
className: "divide-y divide-border-subtle rounded-lg border border-border-subtle bg-bg-surface",
|
|
276
|
+
children: v.map((e) => /* @__PURE__ */ O("div", {
|
|
277
|
+
className: "flex items-center justify-between gap-3 p-3",
|
|
278
|
+
children: [/* @__PURE__ */ O("div", {
|
|
279
|
+
className: "flex min-w-0 items-center gap-2",
|
|
280
|
+
children: [/* @__PURE__ */ D(w, { className: "h-4 w-4 shrink-0 text-text-secondary" }), /* @__PURE__ */ O("div", {
|
|
281
|
+
className: "min-w-0",
|
|
282
|
+
children: [/* @__PURE__ */ D("p", {
|
|
283
|
+
className: "truncate text-sm font-medium text-text-primary",
|
|
284
|
+
children: e.label || e.address
|
|
285
|
+
}), /* @__PURE__ */ O("p", {
|
|
286
|
+
className: "text-xs text-text-secondary",
|
|
287
|
+
children: [r(e.type), e.label ? ` · ${e.address}` : ""]
|
|
288
|
+
})]
|
|
289
|
+
})]
|
|
290
|
+
}), /* @__PURE__ */ O("div", {
|
|
291
|
+
className: "flex shrink-0 items-center gap-2",
|
|
292
|
+
children: [
|
|
293
|
+
/* @__PURE__ */ D(a, { status: e.status }),
|
|
294
|
+
e.status === "PENDING" && /* @__PURE__ */ O("button", {
|
|
295
|
+
type: "button",
|
|
296
|
+
disabled: d.isPending,
|
|
297
|
+
onClick: () => d.mutate(e.id),
|
|
298
|
+
className: "inline-flex items-center gap-1 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",
|
|
299
|
+
children: [/* @__PURE__ */ D(y, { className: "h-3.5 w-3.5" }), " Verify"]
|
|
300
|
+
}),
|
|
301
|
+
e.status === "VERIFIED" && e.ingestEnabled && /* @__PURE__ */ O("button", {
|
|
302
|
+
type: "button",
|
|
303
|
+
onClick: () => g(e),
|
|
304
|
+
className: "inline-flex items-center gap-1 rounded-lg border border-border-subtle px-2.5 py-1 text-xs font-medium text-text-primary hover:bg-bg-sunken",
|
|
305
|
+
children: [/* @__PURE__ */ D(T, { className: "h-3.5 w-3.5" }), " Log test message"]
|
|
306
|
+
})
|
|
307
|
+
]
|
|
308
|
+
})]
|
|
309
|
+
}, e.id))
|
|
310
|
+
}),
|
|
311
|
+
f && /* @__PURE__ */ D(I, { onClose: () => p(!1) }),
|
|
312
|
+
m && /* @__PURE__ */ D(L, {
|
|
313
|
+
channel: m,
|
|
314
|
+
onClose: () => g(null)
|
|
315
|
+
})
|
|
316
|
+
]
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
function z() {
|
|
320
|
+
let { navigateTo: r } = e(), [l, u] = _(0), [d, p] = _(""), [h, g] = _(!1), { data: y, isLoading: C, isError: w, refetch: T, isFetching: j } = f({
|
|
321
|
+
category: d || void 0,
|
|
322
|
+
unreadOnly: h || void 0,
|
|
52
323
|
pagination: {
|
|
53
|
-
limit:
|
|
54
|
-
offset:
|
|
324
|
+
limit: A,
|
|
325
|
+
offset: l * A
|
|
55
326
|
}
|
|
56
|
-
}),
|
|
57
|
-
return /* @__PURE__ */
|
|
327
|
+
}), M = m(), P = y?.items ?? [], F = y?.pageInfo.hasMore ?? !1;
|
|
328
|
+
return /* @__PURE__ */ O(n, {
|
|
58
329
|
title: "Universal Inbox",
|
|
59
330
|
description: "Every subscription communication, merged and categorized",
|
|
60
331
|
children: [
|
|
61
|
-
/* @__PURE__ */
|
|
332
|
+
/* @__PURE__ */ D(k, {
|
|
62
333
|
className: "mb-4",
|
|
63
334
|
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
335
|
}),
|
|
65
|
-
/* @__PURE__ */
|
|
336
|
+
/* @__PURE__ */ O("div", {
|
|
66
337
|
className: "flex flex-wrap items-center gap-3",
|
|
67
|
-
children: [/* @__PURE__ */
|
|
68
|
-
|
|
338
|
+
children: [/* @__PURE__ */ D("select", {
|
|
339
|
+
"aria-label": "Filter by category",
|
|
340
|
+
value: d,
|
|
69
341
|
onChange: (e) => {
|
|
70
|
-
|
|
342
|
+
p(e.target.value), u(0);
|
|
71
343
|
},
|
|
72
344
|
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:
|
|
345
|
+
children: N.map((e) => /* @__PURE__ */ D("option", {
|
|
74
346
|
value: e.value,
|
|
75
347
|
children: e.label
|
|
76
348
|
}, e.value))
|
|
77
|
-
}), /* @__PURE__ */
|
|
349
|
+
}), /* @__PURE__ */ O("label", {
|
|
78
350
|
className: "flex items-center gap-2 text-sm text-text-primary",
|
|
79
|
-
children: [/* @__PURE__ */
|
|
351
|
+
children: [/* @__PURE__ */ D("input", {
|
|
80
352
|
type: "checkbox",
|
|
81
|
-
checked:
|
|
353
|
+
checked: h,
|
|
82
354
|
onChange: (e) => {
|
|
83
|
-
|
|
355
|
+
g(e.target.checked), u(0);
|
|
84
356
|
},
|
|
85
357
|
className: "size-4 rounded border-border-subtle"
|
|
86
358
|
}), "Unread only"]
|
|
87
359
|
})]
|
|
88
360
|
}),
|
|
89
|
-
|
|
361
|
+
C ? /* @__PURE__ */ D(c, { label: "Loading inbox…" }) : w ? /* @__PURE__ */ D(s, {
|
|
90
362
|
message: "Could not load the inbox.",
|
|
91
|
-
onRetry: () => void
|
|
92
|
-
}) : P.length === 0 ? /* @__PURE__ */
|
|
363
|
+
onRetry: () => void T()
|
|
364
|
+
}) : P.length === 0 ? /* @__PURE__ */ D("div", {
|
|
93
365
|
className: "mt-6",
|
|
94
|
-
children: /* @__PURE__ */
|
|
366
|
+
children: /* @__PURE__ */ D(o, {
|
|
95
367
|
illustration: "empty-inbox",
|
|
96
|
-
icon: /* @__PURE__ */
|
|
368
|
+
icon: /* @__PURE__ */ D(b, { className: "h-12 w-12" }),
|
|
97
369
|
title: "Inbox is empty",
|
|
98
|
-
description:
|
|
370
|
+
description: h || d ? "No messages match these filters." : "Merged subscription communications will appear here once channels start ingesting."
|
|
99
371
|
})
|
|
100
|
-
}) : /* @__PURE__ */
|
|
372
|
+
}) : /* @__PURE__ */ O(E, { children: [/* @__PURE__ */ D("div", {
|
|
101
373
|
className: "mt-6 divide-y divide-border-subtle rounded-lg border border-border-subtle bg-bg-surface",
|
|
102
|
-
children: P.map((e) => /* @__PURE__ */
|
|
374
|
+
children: P.map((e) => /* @__PURE__ */ O("div", {
|
|
103
375
|
className: t("flex items-start gap-3 p-4", !e.isRead && "bg-accent-wash"),
|
|
104
376
|
children: [
|
|
105
|
-
/* @__PURE__ */
|
|
377
|
+
/* @__PURE__ */ D("span", {
|
|
106
378
|
className: "mt-0.5 shrink-0 text-text-secondary",
|
|
107
|
-
children: e.isRead ? /* @__PURE__ */
|
|
379
|
+
children: e.isRead ? /* @__PURE__ */ D(S, { className: "h-4 w-4" }) : /* @__PURE__ */ D(x, { className: "h-4 w-4 text-text-link" })
|
|
108
380
|
}),
|
|
109
|
-
/* @__PURE__ */
|
|
381
|
+
/* @__PURE__ */ O("div", {
|
|
110
382
|
className: "min-w-0 flex-1",
|
|
111
383
|
children: [
|
|
112
|
-
/* @__PURE__ */
|
|
384
|
+
/* @__PURE__ */ O("div", {
|
|
113
385
|
className: "flex items-center gap-2",
|
|
114
|
-
children: [/* @__PURE__ */
|
|
386
|
+
children: [/* @__PURE__ */ D("p", {
|
|
115
387
|
className: t("truncate text-sm", e.isRead ? "font-normal text-text-primary" : "font-semibold text-text-primary"),
|
|
116
388
|
children: e.subject || "(no subject)"
|
|
117
|
-
}), e.actionRequired && /* @__PURE__ */
|
|
389
|
+
}), e.actionRequired && /* @__PURE__ */ O("span", {
|
|
118
390
|
title: "Action required",
|
|
119
391
|
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__ */
|
|
392
|
+
children: [/* @__PURE__ */ D(v, { className: "h-3 w-3" }), " Action"]
|
|
121
393
|
})]
|
|
122
394
|
}),
|
|
123
|
-
e.preview && /* @__PURE__ */
|
|
395
|
+
e.preview && /* @__PURE__ */ D("p", {
|
|
124
396
|
className: "mt-0.5 line-clamp-2 text-xs text-text-secondary",
|
|
125
397
|
children: e.preview
|
|
126
398
|
}),
|
|
127
|
-
/* @__PURE__ */
|
|
399
|
+
/* @__PURE__ */ O("div", {
|
|
128
400
|
className: "mt-1.5 flex flex-wrap items-center gap-2",
|
|
129
401
|
children: [
|
|
130
|
-
/* @__PURE__ */
|
|
131
|
-
/* @__PURE__ */
|
|
402
|
+
/* @__PURE__ */ D(a, { status: e.category }),
|
|
403
|
+
/* @__PURE__ */ D("span", {
|
|
132
404
|
className: "text-xs text-text-secondary",
|
|
133
|
-
children:
|
|
405
|
+
children: i(e.receivedAt)
|
|
134
406
|
}),
|
|
135
|
-
e.relationshipId && /* @__PURE__ */
|
|
407
|
+
e.relationshipId && /* @__PURE__ */ D("button", {
|
|
136
408
|
type: "button",
|
|
137
|
-
onClick: () =>
|
|
409
|
+
onClick: () => r(`/relationships/${e.relationshipId}`),
|
|
138
410
|
className: "text-xs font-medium text-text-link hover:underline",
|
|
139
411
|
children: "View relationship"
|
|
140
412
|
})
|
|
@@ -142,45 +414,46 @@ function x() {
|
|
|
142
414
|
})
|
|
143
415
|
]
|
|
144
416
|
}),
|
|
145
|
-
!e.isRead && /* @__PURE__ */
|
|
417
|
+
!e.isRead && /* @__PURE__ */ D("button", {
|
|
146
418
|
type: "button",
|
|
147
|
-
disabled:
|
|
148
|
-
onClick: () =>
|
|
419
|
+
disabled: M.isPending,
|
|
420
|
+
onClick: () => M.mutate(e.id),
|
|
149
421
|
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
422
|
children: "Mark read"
|
|
151
423
|
})
|
|
152
424
|
]
|
|
153
425
|
}, e.id))
|
|
154
|
-
}), /* @__PURE__ */
|
|
426
|
+
}), /* @__PURE__ */ O("div", {
|
|
155
427
|
className: "mt-4 flex items-center justify-between",
|
|
156
|
-
children: [/* @__PURE__ */
|
|
428
|
+
children: [/* @__PURE__ */ O("p", {
|
|
157
429
|
className: "text-xs text-text-secondary",
|
|
158
430
|
children: [
|
|
159
431
|
"Page ",
|
|
160
|
-
|
|
161
|
-
|
|
432
|
+
l + 1,
|
|
433
|
+
j ? " · refreshing…" : ""
|
|
162
434
|
]
|
|
163
|
-
}), /* @__PURE__ */
|
|
435
|
+
}), /* @__PURE__ */ O("div", {
|
|
164
436
|
className: "flex gap-2",
|
|
165
|
-
children: [/* @__PURE__ */
|
|
437
|
+
children: [/* @__PURE__ */ D("button", {
|
|
166
438
|
type: "button",
|
|
167
|
-
disabled:
|
|
168
|
-
onClick: () =>
|
|
169
|
-
className: t("rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium",
|
|
439
|
+
disabled: l === 0,
|
|
440
|
+
onClick: () => u((e) => Math.max(0, e - 1)),
|
|
441
|
+
className: t("rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium", l === 0 ? "cursor-not-allowed text-text-secondary opacity-50" : "text-text-primary hover:bg-bg-sunken"),
|
|
170
442
|
children: "Previous"
|
|
171
|
-
}), /* @__PURE__ */
|
|
443
|
+
}), /* @__PURE__ */ D("button", {
|
|
172
444
|
type: "button",
|
|
173
445
|
disabled: !F,
|
|
174
|
-
onClick: () =>
|
|
446
|
+
onClick: () => u((e) => e + 1),
|
|
175
447
|
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
448
|
children: "Next"
|
|
177
449
|
})]
|
|
178
450
|
})]
|
|
179
|
-
})] })
|
|
451
|
+
})] }),
|
|
452
|
+
/* @__PURE__ */ D(R, {})
|
|
180
453
|
]
|
|
181
454
|
});
|
|
182
455
|
}
|
|
183
456
|
//#endregion
|
|
184
|
-
export {
|
|
457
|
+
export { z as InboxPage };
|
|
185
458
|
|
|
186
459
|
//# sourceMappingURL=InboxPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InboxPage.js","names":[],"sources":["../../../src/subscriberbot/pages/InboxPage.tsx"],"sourcesContent":["/**\n * Universal Inbox\n * @module subscriberbot/pages\n *\n * Every merged subscription communication in one timeline, AI-categorized.\n * Filter by category / unread, mark messages read. Reads\n * `subListInboxMessages` (a SubInboxMessageConnection); writes\n * `subMarkMessageRead`.\n */\n\nimport { useState } from 'react';\nimport { Mail, MailOpen, AlertTriangle, Inbox as InboxIcon } 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 { useInboxMessages, useMarkMessageRead } from '../hooks/useSubscriberBotApi';\nimport { cn } from '../utils/cn';\nimport { formatRelativeTime } from '../utils/formatters';\nimport type { SubMessageCategory } from '../types';\n\nconst PAGE_SIZE = 25;\n\nconst CATEGORY_OPTIONS: Array<{ value: '' | SubMessageCategory; label: string }> = [\n { value: '', label: 'All categories' },\n { value: 'BILLING', label: 'Billing' },\n { value: 'RENEWAL', label: 'Renewals' },\n { value: 'REQUIRED_ACTION', label: 'Required actions' },\n { value: 'SECURITY_ALERT', label: 'Security alerts' },\n { value: 'PRODUCT_UPDATE', label: 'Product updates' },\n { value: 'MARKETING', label: 'Marketing' },\n { value: 'OTHER', label: 'Other' },\n];\n\nexport function InboxPage() {\n const { navigateTo } = useSubscriberBot();\n const [page, setPage] = useState(0);\n const [category, setCategory] = useState<'' | SubMessageCategory>('');\n const [unreadOnly, setUnreadOnly] = useState(false);\n\n const { data, isLoading, isError, refetch, isFetching } = useInboxMessages({\n category: category || undefined,\n unreadOnly: unreadOnly || undefined,\n pagination: { limit: PAGE_SIZE, offset: page * PAGE_SIZE },\n });\n const markRead = useMarkMessageRead();\n\n const items = data?.items ?? [];\n const hasMore = data?.pageInfo.hasMore ?? false;\n\n return (\n <PageLayout\n title=\"Universal Inbox\"\n description=\"Every subscription communication, merged and categorized\"\n >\n <PagePurpose className=\"mb-4\">\n Subscription communication is noisy and scattered. The Universal Inbox merges every channel\n a provider uses and uses AI to categorize each message so you never miss a renewal, required\n action or security notice.\n </PagePurpose>\n\n <div className=\"flex flex-wrap items-center gap-3\">\n <select\n value={category}\n onChange={(e) => {\n setCategory(e.target.value as '' | SubMessageCategory);\n setPage(0);\n }}\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 {CATEGORY_OPTIONS.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={unreadOnly}\n onChange={(e) => {\n setUnreadOnly(e.target.checked);\n setPage(0);\n }}\n className=\"size-4 rounded border-border-subtle\"\n />\n Unread only\n </label>\n </div>\n\n {isLoading ? (\n <LoadingView label=\"Loading inbox…\" />\n ) : isError ? (\n <ErrorView message=\"Could not load the inbox.\" onRetry={() => void refetch()} />\n ) : items.length === 0 ? (\n <div className=\"mt-6\">\n <EmptyState\n illustration=\"empty-inbox\"\n icon={<InboxIcon className=\"h-12 w-12\" />}\n title=\"Inbox is empty\"\n description={\n unreadOnly || category\n ? 'No messages match these filters.'\n : 'Merged subscription communications will appear here once channels start ingesting.'\n }\n />\n </div>\n ) : (\n <>\n <div className=\"mt-6 divide-y divide-border-subtle rounded-lg border border-border-subtle bg-bg-surface\">\n {items.map((m) => (\n <div\n key={m.id}\n className={cn('flex items-start gap-3 p-4', !m.isRead && 'bg-accent-wash')}\n >\n <span className=\"mt-0.5 shrink-0 text-text-secondary\">\n {m.isRead ? (\n <MailOpen className=\"h-4 w-4\" />\n ) : (\n <Mail className=\"h-4 w-4 text-text-link\" />\n )}\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <p\n className={cn(\n 'truncate text-sm',\n m.isRead\n ? 'font-normal text-text-primary'\n : 'font-semibold text-text-primary'\n )}\n >\n {m.subject || '(no subject)'}\n </p>\n {m.actionRequired && (\n <span\n title=\"Action required\"\n 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\"\n >\n <AlertTriangle className=\"h-3 w-3\" /> Action\n </span>\n )}\n </div>\n {m.preview && (\n <p className=\"mt-0.5 line-clamp-2 text-xs text-text-secondary\">{m.preview}</p>\n )}\n <div className=\"mt-1.5 flex flex-wrap items-center gap-2\">\n <StatusBadge status={m.category} />\n <span className=\"text-xs text-text-secondary\">\n {formatRelativeTime(m.receivedAt)}\n </span>\n {m.relationshipId && (\n <button\n type=\"button\"\n onClick={() => navigateTo(`/relationships/${m.relationshipId}`)}\n className=\"text-xs font-medium text-text-link hover:underline\"\n >\n View relationship\n </button>\n )}\n </div>\n </div>\n {!m.isRead && (\n <button\n type=\"button\"\n disabled={markRead.isPending}\n onClick={() => markRead.mutate(m.id)}\n 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\"\n >\n Mark read\n </button>\n )}\n </div>\n ))}\n </div>\n\n <div className=\"mt-4 flex items-center justify-between\">\n <p className=\"text-xs text-text-secondary\">\n Page {page + 1}\n {isFetching ? ' · refreshing…' : ''}\n </p>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n disabled={page === 0}\n onClick={() => setPage((p) => Math.max(0, p - 1))}\n className={cn(\n 'rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium',\n page === 0\n ? 'cursor-not-allowed text-text-secondary opacity-50'\n : 'text-text-primary hover:bg-bg-sunken'\n )}\n >\n Previous\n </button>\n <button\n type=\"button\"\n disabled={!hasMore}\n onClick={() => setPage((p) => p + 1)}\n className={cn(\n 'rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium',\n !hasMore\n ? 'cursor-not-allowed text-text-secondary opacity-50'\n : 'text-text-primary hover:bg-bg-sunken'\n )}\n >\n Next\n </button>\n </div>\n </div>\n </>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"InboxPage.js","names":[],"sources":["../../../src/subscriberbot/pages/InboxPage.tsx"],"sourcesContent":["/**\n * Universal Inbox\n * @module subscriberbot/pages\n *\n * Every merged subscription communication in one timeline, AI-categorized,\n * plus management of the ingest channels that feed it. Filter by category /\n * unread, mark messages read, add & verify channels, and log a test message.\n * Reads `subListInboxMessages` / `subListChannels`; writes `subMarkMessageRead`,\n * `subCreateChannel`, `subVerifyChannel`, `subIngestMessage`.\n */\n\nimport { useState } from 'react';\nimport {\n Mail,\n MailOpen,\n AlertTriangle,\n Inbox as InboxIcon,\n Plus,\n Radio,\n BadgeCheck,\n Send,\n} 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 { Modal } from '../components/Modal';\nimport { LoadingView, ErrorView, Section } from '../components/StateViews';\nimport { useSubscriberBot } from '../context/SubscriberBotContext';\nimport {\n useInboxMessages,\n useMarkMessageRead,\n useChannels,\n useCreateChannel,\n useVerifyChannel,\n useIngestMessage,\n} from '../hooks/useSubscriberBotApi';\nimport { cn } from '../utils/cn';\nimport { formatEnumLabel, formatRelativeTime } from '../utils/formatters';\nimport type { SubChannelType, SubCommunicationChannel, SubMessageCategory } from '../types';\n\nconst PAGE_SIZE = 25;\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\nconst CATEGORY_OPTIONS: Array<{ value: '' | SubMessageCategory; label: string }> = [\n { value: '', label: 'All categories' },\n { value: 'BILLING', label: 'Billing' },\n { value: 'RENEWAL', label: 'Renewals' },\n { value: 'REQUIRED_ACTION', label: 'Required actions' },\n { value: 'SECURITY_ALERT', label: 'Security alerts' },\n { value: 'PRODUCT_UPDATE', label: 'Product updates' },\n { value: 'MARKETING', label: 'Marketing' },\n { value: 'OTHER', label: 'Other' },\n];\n\nconst MESSAGE_CATEGORIES: SubMessageCategory[] = [\n 'BILLING',\n 'RENEWAL',\n 'REQUIRED_ACTION',\n 'SECURITY_ALERT',\n 'PRODUCT_UPDATE',\n 'MARKETING',\n 'OTHER',\n];\n\nconst CHANNEL_TYPES: SubChannelType[] = [\n 'EMAIL',\n 'SMS',\n 'SLACK',\n 'DISCORD',\n 'WHATSAPP',\n 'PUSH',\n 'WEBHOOK',\n];\n\n// ---------------------------------------------------------------------------\n// Add channel modal → subCreateChannel\n// ---------------------------------------------------------------------------\n\nfunction AddChannelModal({ onClose }: { onClose: () => void }) {\n const create = useCreateChannel();\n const [type, setType] = useState<SubChannelType>('EMAIL');\n const [address, setAddress] = useState('');\n const [label, setLabel] = useState('');\n const [ingestEnabled, setIngestEnabled] = 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 (!address.trim()) {\n setFormError('An address is required.');\n return;\n }\n create.mutate(\n {\n type,\n address: address.trim(),\n ingestEnabled,\n ...(label.trim() ? { label: label.trim() } : {}),\n },\n {\n onSuccess: () => onClose(),\n onError: (err) =>\n setFormError(err instanceof Error ? err.message : 'Could not add channel.'),\n }\n );\n };\n\n return (\n <Modal title=\"Add channel\" 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 className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"ch-type\" className={labelCls}>\n Type\n </label>\n <select\n id=\"ch-type\"\n value={type}\n onChange={(e) => setType(e.target.value as SubChannelType)}\n className={inputCls}\n >\n {CHANNEL_TYPES.map((t) => (\n <option key={t} value={t}>\n {t.replace(/_/g, ' ')}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor=\"ch-label\" className={labelCls}>\n Label\n </label>\n <input\n id=\"ch-label\"\n type=\"text\"\n value={label}\n onChange={(e) => setLabel(e.target.value)}\n placeholder=\"e.g. Billing inbox\"\n className={inputCls}\n />\n </div>\n </div>\n <div>\n <label htmlFor=\"ch-address\" className={labelCls}>\n Address <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n id=\"ch-address\"\n type=\"text\"\n value={address}\n onChange={(e) => setAddress(e.target.value)}\n placeholder=\"e.g. billing@acme.com, +1…, a webhook URL\"\n className={inputCls}\n required\n />\n </div>\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={ingestEnabled}\n onChange={(e) => setIngestEnabled(e.target.checked)}\n className=\"size-4 rounded border-border-subtle\"\n />\n Ingest messages from this channel\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 channel'}\n </button>\n </div>\n </form>\n </Modal>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Log test message modal → subIngestMessage\n// ---------------------------------------------------------------------------\n\nfunction LogMessageModal({\n channel,\n onClose,\n}: {\n channel: SubCommunicationChannel;\n onClose: () => void;\n}) {\n const ingest = useIngestMessage();\n const [subject, setSubject] = useState('');\n const [preview, setPreview] = useState('');\n const [category, setCategory] = useState<SubMessageCategory>('OTHER');\n const [actionRequired, setActionRequired] = useState(false);\n const [formError, setFormError] = useState<string | null>(null);\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n setFormError(null);\n ingest.mutate(\n {\n channelId: channel.id,\n receivedAt: new Date().toISOString(),\n category,\n actionRequired,\n ...(subject.trim() ? { subject: subject.trim() } : {}),\n ...(preview.trim() ? { preview: preview.trim() } : {}),\n },\n {\n onSuccess: () => onClose(),\n onError: (err) =>\n setFormError(err instanceof Error ? err.message : 'Could not log the message.'),\n }\n );\n };\n\n return (\n <Modal\n title=\"Log test message\"\n description={`Simulate an ingested message from ${channel.label || channel.address}.`}\n onClose={onClose}\n >\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=\"msg-subject\" className={labelCls}>\n Subject\n </label>\n <input\n id=\"msg-subject\"\n type=\"text\"\n value={subject}\n onChange={(e) => setSubject(e.target.value)}\n placeholder=\"e.g. Your plan renews in 3 days\"\n className={inputCls}\n />\n </div>\n <div>\n <label htmlFor=\"msg-preview\" className={labelCls}>\n Preview\n </label>\n <textarea\n id=\"msg-preview\"\n value={preview}\n onChange={(e) => setPreview(e.target.value)}\n rows={2}\n className={inputCls}\n />\n </div>\n <div>\n <label htmlFor=\"msg-category\" className={labelCls}>\n Category\n </label>\n <select\n id=\"msg-category\"\n value={category}\n onChange={(e) => setCategory(e.target.value as SubMessageCategory)}\n className={inputCls}\n >\n {MESSAGE_CATEGORIES.map((c) => (\n <option key={c} value={c}>\n {c.replace(/_/g, ' ')}\n </option>\n ))}\n </select>\n </div>\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={actionRequired}\n onChange={(e) => setActionRequired(e.target.checked)}\n className=\"size-4 rounded border-border-subtle\"\n />\n Requires action\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={ingest.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 {ingest.isPending ? 'Logging…' : 'Log message'}\n </button>\n </div>\n </form>\n </Modal>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Channels management panel\n// ---------------------------------------------------------------------------\n\nfunction ChannelsSection() {\n const { data, isLoading, isError, refetch } = useChannels();\n const verify = useVerifyChannel();\n const [showAdd, setShowAdd] = useState(false);\n const [logChannel, setLogChannel] = useState<SubCommunicationChannel | null>(null);\n\n const channels = data ?? [];\n\n return (\n <Section\n title=\"Ingest channels\"\n description=\"The sources the Universal Inbox pulls subscription communication from\"\n action={\n <button\n type=\"button\"\n onClick={() => setShowAdd(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 channel\n </button>\n }\n >\n {isLoading ? (\n <LoadingView label=\"Loading channels…\" />\n ) : isError ? (\n <ErrorView message=\"Could not load channels.\" onRetry={() => void refetch()} />\n ) : channels.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Radio className=\"h-12 w-12\" />}\n title=\"No channels yet\"\n description=\"Add an email, SMS, Slack or webhook channel to start ingesting subscription communication.\"\n />\n ) : (\n <div className=\"divide-y divide-border-subtle rounded-lg border border-border-subtle bg-bg-surface\">\n {channels.map((ch) => (\n <div key={ch.id} className=\"flex items-center justify-between gap-3 p-3\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <Radio className=\"h-4 w-4 shrink-0 text-text-secondary\" />\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {ch.label || ch.address}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {formatEnumLabel(ch.type)}\n {ch.label ? ` · ${ch.address}` : ''}\n </p>\n </div>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n <StatusBadge status={ch.status} />\n {ch.status === 'PENDING' && (\n <button\n type=\"button\"\n disabled={verify.isPending}\n onClick={() => verify.mutate(ch.id)}\n className=\"inline-flex items-center gap-1 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\"\n >\n <BadgeCheck className=\"h-3.5 w-3.5\" /> Verify\n </button>\n )}\n {ch.status === 'VERIFIED' && ch.ingestEnabled && (\n <button\n type=\"button\"\n onClick={() => setLogChannel(ch)}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-subtle px-2.5 py-1 text-xs font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <Send className=\"h-3.5 w-3.5\" /> Log test message\n </button>\n )}\n </div>\n </div>\n ))}\n </div>\n )}\n\n {showAdd && <AddChannelModal onClose={() => setShowAdd(false)} />}\n {logChannel && <LogMessageModal channel={logChannel} onClose={() => setLogChannel(null)} />}\n </Section>\n );\n}\n\nexport function InboxPage() {\n const { navigateTo } = useSubscriberBot();\n const [page, setPage] = useState(0);\n const [category, setCategory] = useState<'' | SubMessageCategory>('');\n const [unreadOnly, setUnreadOnly] = useState(false);\n\n const { data, isLoading, isError, refetch, isFetching } = useInboxMessages({\n category: category || undefined,\n unreadOnly: unreadOnly || undefined,\n pagination: { limit: PAGE_SIZE, offset: page * PAGE_SIZE },\n });\n const markRead = useMarkMessageRead();\n\n const items = data?.items ?? [];\n const hasMore = data?.pageInfo.hasMore ?? false;\n\n return (\n <PageLayout\n title=\"Universal Inbox\"\n description=\"Every subscription communication, merged and categorized\"\n >\n <PagePurpose className=\"mb-4\">\n Subscription communication is noisy and scattered. The Universal Inbox merges every channel\n a provider uses and uses AI to categorize each message so you never miss a renewal, required\n action or security notice.\n </PagePurpose>\n\n <div className=\"flex flex-wrap items-center gap-3\">\n <select\n aria-label=\"Filter by category\"\n value={category}\n onChange={(e) => {\n setCategory(e.target.value as '' | SubMessageCategory);\n setPage(0);\n }}\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 {CATEGORY_OPTIONS.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={unreadOnly}\n onChange={(e) => {\n setUnreadOnly(e.target.checked);\n setPage(0);\n }}\n className=\"size-4 rounded border-border-subtle\"\n />\n Unread only\n </label>\n </div>\n\n {isLoading ? (\n <LoadingView label=\"Loading inbox…\" />\n ) : isError ? (\n <ErrorView message=\"Could not load the inbox.\" onRetry={() => void refetch()} />\n ) : items.length === 0 ? (\n <div className=\"mt-6\">\n <EmptyState\n illustration=\"empty-inbox\"\n icon={<InboxIcon className=\"h-12 w-12\" />}\n title=\"Inbox is empty\"\n description={\n unreadOnly || category\n ? 'No messages match these filters.'\n : 'Merged subscription communications will appear here once channels start ingesting.'\n }\n />\n </div>\n ) : (\n <>\n <div className=\"mt-6 divide-y divide-border-subtle rounded-lg border border-border-subtle bg-bg-surface\">\n {items.map((m) => (\n <div\n key={m.id}\n className={cn('flex items-start gap-3 p-4', !m.isRead && 'bg-accent-wash')}\n >\n <span className=\"mt-0.5 shrink-0 text-text-secondary\">\n {m.isRead ? (\n <MailOpen className=\"h-4 w-4\" />\n ) : (\n <Mail className=\"h-4 w-4 text-text-link\" />\n )}\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <p\n className={cn(\n 'truncate text-sm',\n m.isRead\n ? 'font-normal text-text-primary'\n : 'font-semibold text-text-primary'\n )}\n >\n {m.subject || '(no subject)'}\n </p>\n {m.actionRequired && (\n <span\n title=\"Action required\"\n 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\"\n >\n <AlertTriangle className=\"h-3 w-3\" /> Action\n </span>\n )}\n </div>\n {m.preview && (\n <p className=\"mt-0.5 line-clamp-2 text-xs text-text-secondary\">{m.preview}</p>\n )}\n <div className=\"mt-1.5 flex flex-wrap items-center gap-2\">\n <StatusBadge status={m.category} />\n <span className=\"text-xs text-text-secondary\">\n {formatRelativeTime(m.receivedAt)}\n </span>\n {m.relationshipId && (\n <button\n type=\"button\"\n onClick={() => navigateTo(`/relationships/${m.relationshipId}`)}\n className=\"text-xs font-medium text-text-link hover:underline\"\n >\n View relationship\n </button>\n )}\n </div>\n </div>\n {!m.isRead && (\n <button\n type=\"button\"\n disabled={markRead.isPending}\n onClick={() => markRead.mutate(m.id)}\n 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\"\n >\n Mark read\n </button>\n )}\n </div>\n ))}\n </div>\n\n <div className=\"mt-4 flex items-center justify-between\">\n <p className=\"text-xs text-text-secondary\">\n Page {page + 1}\n {isFetching ? ' · refreshing…' : ''}\n </p>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n disabled={page === 0}\n onClick={() => setPage((p) => Math.max(0, p - 1))}\n className={cn(\n 'rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium',\n page === 0\n ? 'cursor-not-allowed text-text-secondary opacity-50'\n : 'text-text-primary hover:bg-bg-sunken'\n )}\n >\n Previous\n </button>\n <button\n type=\"button\"\n disabled={!hasMore}\n onClick={() => setPage((p) => p + 1)}\n className={cn(\n 'rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium',\n !hasMore\n ? 'cursor-not-allowed text-text-secondary opacity-50'\n : 'text-text-primary hover:bg-bg-sunken'\n )}\n >\n Next\n </button>\n </div>\n </div>\n </>\n )}\n\n <ChannelsSection />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|