@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
@@ -5,14 +5,16 @@ import { MetricsCard as r } from "../components/MetricsCard.js";
5
5
  import { formatCurrency as i, formatDate as a } from "../utils/formatters.js";
6
6
  import { StatusBadge as o } from "../components/StatusBadge.js";
7
7
  import { EmptyState as s } from "../components/EmptyState.js";
8
- import { ErrorView as c, LoadingView as l } from "../components/StateViews.js";
9
- import { useInvoices as u, useMarkInvoicePaid as d } from "../hooks/useSubscriberBotApi.js";
10
- import { useMemo as f, useState as p } from "react";
11
- import { FileText as m, Receipt as h } from "lucide-react";
12
- import { Fragment as g, jsx as _, jsxs as v } from "react/jsx-runtime";
13
- import { PagePurpose as y } from "@burdenoff/fe-libs/ui";
8
+ import { Modal as c } from "../components/Modal.js";
9
+ import { ErrorView as l, LoadingView as u, Section as d } from "../components/StateViews.js";
10
+ import { CHART_COLORS as f, Donut as p, Legend as m } from "../components/viz.js";
11
+ import { useCreateInvoice as h, useCreatePaymentInstrument as g, useInvoices as _, useMarkInvoicePaid as v, usePaymentInstruments as y, useRelationships as b, useRemovePaymentInstrument as x } from "../hooks/useSubscriberBotApi.js";
12
+ import { useMemo as S, useState as C } from "react";
13
+ import { AlertTriangle as w, CheckCircle2 as T, CreditCard as E, FileText as D, Plus as O, Receipt as k, Star as A, Trash2 as j } from "lucide-react";
14
+ import { Fragment as M, jsx as N, jsxs as P } from "react/jsx-runtime";
15
+ import { GlassCard as F, PagePurpose as I } from "@burdenoff/fe-libs/ui";
14
16
  //#region src/subscriberbot/pages/InvoicesPage.tsx
15
- var b = 25, x = [
17
+ var L = 25, R = "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)]", z = "mb-1 block text-xs font-medium text-text-secondary", B = [
16
18
  {
17
19
  value: "",
18
20
  label: "All statuses"
@@ -45,130 +47,637 @@ var b = 25, x = [
45
47
  value: "REFUNDED",
46
48
  label: "Refunded"
47
49
  }
48
- ], S = new Set([
50
+ ], V = [
51
+ "ISSUED",
52
+ "DRAFT",
53
+ "OVERDUE",
54
+ "PAID",
55
+ "FAILED",
56
+ "CANCELLED",
57
+ "REFUNDED"
58
+ ], H = [
59
+ "CARD",
60
+ "BANK_ACCOUNT",
61
+ "PAYPAL",
62
+ "UPI",
63
+ "WALLET",
64
+ "OTHER"
65
+ ], U = new Set([
49
66
  "ISSUED",
50
67
  "OVERDUE",
51
68
  "FAILED"
52
- ]);
53
- function C() {
54
- let { navigateTo: C } = e(), [w, T] = p(0), [E, D] = p(""), { data: O, isLoading: k, isError: A, refetch: j, isFetching: M } = u({
55
- status: E || void 0,
69
+ ]), W = {
70
+ PAID: "var(--color-status-success-text)",
71
+ OVERDUE: "var(--color-status-error-text)",
72
+ FAILED: "var(--color-status-error-text)",
73
+ ISSUED: "var(--color-chart-1)",
74
+ DRAFT: "var(--color-text-secondary)",
75
+ CANCELLED: "var(--color-chart-4)",
76
+ REFUNDED: "var(--color-status-warning-text)"
77
+ };
78
+ function G({ onClose: e }) {
79
+ let t = h(), n = b({ pagination: {
80
+ limit: 100,
81
+ offset: 0
82
+ } }).data?.items ?? [], [r, i] = C(""), [a, o] = C(""), [s, l] = C("ISSUED"), [u, d] = C("USD"), [f, p] = C(""), [m, g] = C(""), [_, v] = C(""), [y, x] = C(""), [S, w] = C(""), [T, E] = C(null);
83
+ return /* @__PURE__ */ N(c, {
84
+ title: "Create invoice",
85
+ onClose: e,
86
+ maxWidthClassName: "max-w-xl",
87
+ children: /* @__PURE__ */ P("form", {
88
+ onSubmit: (n) => {
89
+ if (n.preventDefault(), E(null), !r) {
90
+ E("Select the relationship this invoice belongs to.");
91
+ return;
92
+ }
93
+ t.mutate({
94
+ relationshipId: r,
95
+ status: s,
96
+ currency: u.trim() || "USD",
97
+ ...a.trim() ? { number: a.trim() } : {},
98
+ ...f.trim() ? { subtotal: Number(f) } : {},
99
+ ...m.trim() ? { taxAmount: Number(m) } : {},
100
+ ..._.trim() ? { total: Number(_) } : {},
101
+ ...y ? { issueDate: new Date(y).toISOString() } : {},
102
+ ...S ? { dueDate: new Date(S).toISOString() } : {}
103
+ }, {
104
+ onSuccess: () => e(),
105
+ onError: (e) => E(e instanceof Error ? e.message : "Could not create invoice.")
106
+ });
107
+ },
108
+ className: "space-y-4",
109
+ children: [
110
+ T && /* @__PURE__ */ N("div", {
111
+ className: "rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text",
112
+ children: T
113
+ }),
114
+ /* @__PURE__ */ P("div", { children: [/* @__PURE__ */ P("label", {
115
+ htmlFor: "inv-relationship",
116
+ className: z,
117
+ children: ["Relationship ", /* @__PURE__ */ N("span", {
118
+ className: "text-status-error-text",
119
+ children: "*"
120
+ })]
121
+ }), /* @__PURE__ */ P("select", {
122
+ id: "inv-relationship",
123
+ value: r,
124
+ onChange: (e) => i(e.target.value),
125
+ className: R,
126
+ required: !0,
127
+ children: [/* @__PURE__ */ N("option", {
128
+ value: "",
129
+ children: "— Select a relationship —"
130
+ }), n.map((e) => /* @__PURE__ */ N("option", {
131
+ value: e.id,
132
+ children: e.label
133
+ }, e.id))]
134
+ })] }),
135
+ /* @__PURE__ */ P("div", {
136
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-3",
137
+ children: [
138
+ /* @__PURE__ */ P("div", { children: [/* @__PURE__ */ N("label", {
139
+ htmlFor: "inv-number",
140
+ className: z,
141
+ children: "Number"
142
+ }), /* @__PURE__ */ N("input", {
143
+ id: "inv-number",
144
+ type: "text",
145
+ value: a,
146
+ onChange: (e) => o(e.target.value),
147
+ placeholder: "INV-0001",
148
+ className: R
149
+ })] }),
150
+ /* @__PURE__ */ P("div", { children: [/* @__PURE__ */ N("label", {
151
+ htmlFor: "inv-status",
152
+ className: z,
153
+ children: "Status"
154
+ }), /* @__PURE__ */ N("select", {
155
+ id: "inv-status",
156
+ value: s,
157
+ onChange: (e) => l(e.target.value),
158
+ className: R,
159
+ children: V.map((e) => /* @__PURE__ */ N("option", {
160
+ value: e,
161
+ children: e.replace(/_/g, " ")
162
+ }, e))
163
+ })] }),
164
+ /* @__PURE__ */ P("div", { children: [/* @__PURE__ */ N("label", {
165
+ htmlFor: "inv-currency",
166
+ className: z,
167
+ children: "Currency"
168
+ }), /* @__PURE__ */ N("input", {
169
+ id: "inv-currency",
170
+ type: "text",
171
+ value: u,
172
+ onChange: (e) => d(e.target.value.toUpperCase()),
173
+ maxLength: 3,
174
+ className: R
175
+ })] })
176
+ ]
177
+ }),
178
+ /* @__PURE__ */ P("div", {
179
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-3",
180
+ children: [
181
+ /* @__PURE__ */ P("div", { children: [/* @__PURE__ */ N("label", {
182
+ htmlFor: "inv-subtotal",
183
+ className: z,
184
+ children: "Subtotal"
185
+ }), /* @__PURE__ */ N("input", {
186
+ id: "inv-subtotal",
187
+ type: "number",
188
+ min: "0",
189
+ step: "0.01",
190
+ value: f,
191
+ onChange: (e) => p(e.target.value),
192
+ className: R
193
+ })] }),
194
+ /* @__PURE__ */ P("div", { children: [/* @__PURE__ */ N("label", {
195
+ htmlFor: "inv-tax",
196
+ className: z,
197
+ children: "Tax"
198
+ }), /* @__PURE__ */ N("input", {
199
+ id: "inv-tax",
200
+ type: "number",
201
+ min: "0",
202
+ step: "0.01",
203
+ value: m,
204
+ onChange: (e) => g(e.target.value),
205
+ className: R
206
+ })] }),
207
+ /* @__PURE__ */ P("div", { children: [/* @__PURE__ */ N("label", {
208
+ htmlFor: "inv-total",
209
+ className: z,
210
+ children: "Total"
211
+ }), /* @__PURE__ */ N("input", {
212
+ id: "inv-total",
213
+ type: "number",
214
+ min: "0",
215
+ step: "0.01",
216
+ value: _,
217
+ onChange: (e) => v(e.target.value),
218
+ className: R
219
+ })] })
220
+ ]
221
+ }),
222
+ /* @__PURE__ */ P("div", {
223
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
224
+ children: [/* @__PURE__ */ P("div", { children: [/* @__PURE__ */ N("label", {
225
+ htmlFor: "inv-issue",
226
+ className: z,
227
+ children: "Issue date"
228
+ }), /* @__PURE__ */ N("input", {
229
+ id: "inv-issue",
230
+ type: "date",
231
+ value: y,
232
+ onChange: (e) => x(e.target.value),
233
+ className: R
234
+ })] }), /* @__PURE__ */ P("div", { children: [/* @__PURE__ */ N("label", {
235
+ htmlFor: "inv-due",
236
+ className: z,
237
+ children: "Due date"
238
+ }), /* @__PURE__ */ N("input", {
239
+ id: "inv-due",
240
+ type: "date",
241
+ value: S,
242
+ onChange: (e) => w(e.target.value),
243
+ className: R
244
+ })] })]
245
+ }),
246
+ /* @__PURE__ */ P("div", {
247
+ className: "flex justify-end gap-3",
248
+ children: [/* @__PURE__ */ N("button", {
249
+ type: "button",
250
+ onClick: e,
251
+ className: "rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken",
252
+ children: "Cancel"
253
+ }), /* @__PURE__ */ N("button", {
254
+ type: "submit",
255
+ disabled: t.isPending,
256
+ 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",
257
+ children: t.isPending ? "Creating…" : "Create invoice"
258
+ })]
259
+ })
260
+ ]
261
+ })
262
+ });
263
+ }
264
+ function K({ onClose: e }) {
265
+ let t = g(), [n, r] = C(""), [i, a] = C("CARD"), [o, s] = C(""), [l, u] = C(""), [d, f] = C(""), [p, m] = C(""), [h, _] = C(!1), [v, y] = C(null);
266
+ return /* @__PURE__ */ N(c, {
267
+ title: "Add payment method",
268
+ description: "A passive annotation of which external card/account pays for a subscription — no full card numbers or CVV.",
269
+ onClose: e,
270
+ maxWidthClassName: "max-w-xl",
271
+ children: /* @__PURE__ */ P("form", {
272
+ onSubmit: (r) => {
273
+ if (r.preventDefault(), y(null), !n.trim()) {
274
+ y("A label is required.");
275
+ return;
276
+ }
277
+ t.mutate({
278
+ label: n.trim(),
279
+ type: i,
280
+ isDefault: h,
281
+ ...o.trim() ? { brand: o.trim() } : {},
282
+ ...l.trim() ? { last4: l.trim() } : {},
283
+ ...d.trim() ? { expiryMonth: Number(d) } : {},
284
+ ...p.trim() ? { expiryYear: Number(p) } : {}
285
+ }, {
286
+ onSuccess: () => e(),
287
+ onError: (e) => y(e instanceof Error ? e.message : "Could not add payment method.")
288
+ });
289
+ },
290
+ className: "space-y-4",
291
+ children: [
292
+ v && /* @__PURE__ */ N("div", {
293
+ className: "rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text",
294
+ children: v
295
+ }),
296
+ /* @__PURE__ */ P("div", {
297
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
298
+ children: [/* @__PURE__ */ P("div", { children: [/* @__PURE__ */ P("label", {
299
+ htmlFor: "pm-label",
300
+ className: z,
301
+ children: ["Label ", /* @__PURE__ */ N("span", {
302
+ className: "text-status-error-text",
303
+ children: "*"
304
+ })]
305
+ }), /* @__PURE__ */ N("input", {
306
+ id: "pm-label",
307
+ type: "text",
308
+ value: n,
309
+ onChange: (e) => r(e.target.value),
310
+ placeholder: "e.g. Personal Visa",
311
+ className: R,
312
+ required: !0
313
+ })] }), /* @__PURE__ */ P("div", { children: [/* @__PURE__ */ N("label", {
314
+ htmlFor: "pm-type",
315
+ className: z,
316
+ children: "Type"
317
+ }), /* @__PURE__ */ N("select", {
318
+ id: "pm-type",
319
+ value: i,
320
+ onChange: (e) => a(e.target.value),
321
+ className: R,
322
+ children: H.map((e) => /* @__PURE__ */ N("option", {
323
+ value: e,
324
+ children: e.replace(/_/g, " ")
325
+ }, e))
326
+ })] })]
327
+ }),
328
+ /* @__PURE__ */ P("div", {
329
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-3",
330
+ children: [
331
+ /* @__PURE__ */ P("div", { children: [/* @__PURE__ */ N("label", {
332
+ htmlFor: "pm-brand",
333
+ className: z,
334
+ children: "Brand"
335
+ }), /* @__PURE__ */ N("input", {
336
+ id: "pm-brand",
337
+ type: "text",
338
+ value: o,
339
+ onChange: (e) => s(e.target.value),
340
+ placeholder: "Visa, Amex…",
341
+ className: R
342
+ })] }),
343
+ /* @__PURE__ */ P("div", { children: [/* @__PURE__ */ N("label", {
344
+ htmlFor: "pm-last4",
345
+ className: z,
346
+ children: "Last 4"
347
+ }), /* @__PURE__ */ N("input", {
348
+ id: "pm-last4",
349
+ type: "text",
350
+ inputMode: "numeric",
351
+ maxLength: 4,
352
+ value: l,
353
+ onChange: (e) => u(e.target.value.replace(/\D/g, "")),
354
+ placeholder: "4242",
355
+ className: R
356
+ })] }),
357
+ /* @__PURE__ */ P("div", {
358
+ className: "grid grid-cols-2 gap-2",
359
+ children: [/* @__PURE__ */ P("div", { children: [/* @__PURE__ */ N("label", {
360
+ htmlFor: "pm-exp-month",
361
+ className: z,
362
+ children: "Exp. mo."
363
+ }), /* @__PURE__ */ N("input", {
364
+ id: "pm-exp-month",
365
+ type: "number",
366
+ min: "1",
367
+ max: "12",
368
+ value: d,
369
+ onChange: (e) => f(e.target.value),
370
+ placeholder: "12",
371
+ className: R
372
+ })] }), /* @__PURE__ */ P("div", { children: [/* @__PURE__ */ N("label", {
373
+ htmlFor: "pm-exp-year",
374
+ className: z,
375
+ children: "Exp. yr."
376
+ }), /* @__PURE__ */ N("input", {
377
+ id: "pm-exp-year",
378
+ type: "number",
379
+ min: "2024",
380
+ max: "2099",
381
+ value: p,
382
+ onChange: (e) => m(e.target.value),
383
+ placeholder: "2028",
384
+ className: R
385
+ })] })]
386
+ })
387
+ ]
388
+ }),
389
+ /* @__PURE__ */ P("label", {
390
+ className: "flex items-center gap-2 text-sm text-text-primary",
391
+ children: [/* @__PURE__ */ N("input", {
392
+ type: "checkbox",
393
+ checked: h,
394
+ onChange: (e) => _(e.target.checked),
395
+ className: "size-4 rounded border-border-subtle"
396
+ }), "Set as default"]
397
+ }),
398
+ /* @__PURE__ */ P("div", {
399
+ className: "flex justify-end gap-3",
400
+ children: [/* @__PURE__ */ N("button", {
401
+ type: "button",
402
+ onClick: e,
403
+ className: "rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken",
404
+ children: "Cancel"
405
+ }), /* @__PURE__ */ N("button", {
406
+ type: "submit",
407
+ disabled: t.isPending,
408
+ 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",
409
+ children: t.isPending ? "Adding…" : "Add payment method"
410
+ })]
411
+ })
412
+ ]
413
+ })
414
+ });
415
+ }
416
+ function q() {
417
+ let { data: e, isLoading: t, isError: n, refetch: r } = y(), i = x(), [a, c] = C(!1), f = e ?? [];
418
+ return /* @__PURE__ */ P(d, {
419
+ title: "Payment methods",
420
+ description: "Which external cards and accounts pay for your subscriptions",
421
+ action: /* @__PURE__ */ P("button", {
422
+ type: "button",
423
+ onClick: () => c(!0),
424
+ 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",
425
+ children: [/* @__PURE__ */ N(O, { className: "h-3.5 w-3.5" }), " Add payment method"]
426
+ }),
427
+ children: [t ? /* @__PURE__ */ N(u, { label: "Loading payment methods…" }) : n ? /* @__PURE__ */ N(l, {
428
+ message: "Could not load payment methods.",
429
+ onRetry: () => void r()
430
+ }) : f.length === 0 ? /* @__PURE__ */ N(s, {
431
+ illustration: "empty-data",
432
+ icon: /* @__PURE__ */ N(E, { className: "h-12 w-12" }),
433
+ title: "No payment methods",
434
+ description: "Annotate which card or account pays for each subscription. No full card numbers are stored."
435
+ }) : /* @__PURE__ */ N("div", {
436
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-3",
437
+ children: f.map((e) => {
438
+ let t = [e.expiryMonth && e.expiryYear ? `Exp ${String(e.expiryMonth).padStart(2, "0")}/${String(e.expiryYear).slice(-2)}` : null].filter(Boolean);
439
+ return /* @__PURE__ */ P(F, {
440
+ treatment: "metric",
441
+ className: "flex flex-col gap-4 p-4",
442
+ children: [/* @__PURE__ */ P("div", {
443
+ className: "flex items-start justify-between gap-2",
444
+ children: [/* @__PURE__ */ P("div", {
445
+ className: "flex items-center gap-3",
446
+ children: [/* @__PURE__ */ N("span", {
447
+ className: "grid h-9 w-12 place-items-center rounded-md bg-[var(--color-accent-soft)] text-text-primary",
448
+ children: /* @__PURE__ */ N(E, {
449
+ className: "h-4 w-4",
450
+ "aria-hidden": !0
451
+ })
452
+ }), /* @__PURE__ */ P("div", {
453
+ className: "min-w-0",
454
+ children: [/* @__PURE__ */ P("p", {
455
+ className: "flex items-center gap-1.5 truncate text-sm font-semibold text-text-primary",
456
+ children: [e.brand || e.label, e.isDefault && /* @__PURE__ */ P("span", {
457
+ className: "inline-flex items-center gap-1 rounded-full bg-status-success-bg-subtle px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-status-success-text",
458
+ children: [/* @__PURE__ */ N(A, {
459
+ className: "h-2.5 w-2.5 fill-current",
460
+ "aria-hidden": !0
461
+ }), "Default"]
462
+ })]
463
+ }), /* @__PURE__ */ N("p", {
464
+ className: "font-mono text-sm tracking-[0.15em] text-text-secondary",
465
+ children: e.last4 ? `•••• ${e.last4}` : e.type.replace(/_/g, " ")
466
+ })]
467
+ })]
468
+ }), /* @__PURE__ */ N(o, { status: e.status })]
469
+ }), /* @__PURE__ */ P("div", {
470
+ className: "mt-auto flex items-center justify-between border-t border-border-subtle pt-3",
471
+ children: [/* @__PURE__ */ N("p", {
472
+ className: "truncate text-xs text-text-secondary",
473
+ children: [e.label, ...t].filter(Boolean).join(" · ")
474
+ }), /* @__PURE__ */ P("button", {
475
+ type: "button",
476
+ "aria-label": `Remove ${e.label}`,
477
+ disabled: i.isPending,
478
+ onClick: () => i.mutate(e.id),
479
+ className: "inline-flex shrink-0 items-center gap-1 rounded-lg border border-border-subtle px-2.5 py-1 text-xs font-medium text-text-secondary hover:border-status-error-text/40 hover:bg-status-error-bg-subtle hover:text-status-error-text disabled:opacity-50",
480
+ children: [/* @__PURE__ */ N(j, {
481
+ className: "h-3.5 w-3.5",
482
+ "aria-hidden": !0
483
+ }), "Remove"]
484
+ })]
485
+ })]
486
+ }, e.id);
487
+ })
488
+ }), a && /* @__PURE__ */ N(K, { onClose: () => c(!1) })]
489
+ });
490
+ }
491
+ function J() {
492
+ let { navigateTo: c } = e(), [d, h] = C(0), [g, y] = C(""), [b, x] = C(!1), { data: E, isLoading: A, isError: j, refetch: R, isFetching: z } = _({
493
+ status: g || void 0,
56
494
  pagination: {
57
- limit: b,
58
- offset: w * b
495
+ limit: L,
496
+ offset: d * L
59
497
  }
60
- }), N = d(), P = f(() => O?.items ?? [], [O]), F = O?.pageInfo.hasMore ?? !1, I = f(() => {
61
- let e = P.filter((e) => S.has(e.status)), t = P[0]?.currency ?? "USD";
498
+ }), V = v(), H = S(() => E?.items ?? [], [E]), K = E?.pageInfo.hasMore ?? !1, J = S(() => {
499
+ let e = H.filter((e) => U.has(e.status)), t = H[0]?.currency ?? "USD", n = /* @__PURE__ */ new Map();
500
+ for (let e of H) n.set(e.status, (n.get(e.status) ?? 0) + 1);
501
+ let r = Array.from(n.entries()).sort((e, t) => t[1] - e[1]).map(([e, t], n) => ({
502
+ label: e.charAt(0) + e.slice(1).toLowerCase(),
503
+ value: t,
504
+ color: W[e] ?? f[n % f.length]
505
+ }));
62
506
  return {
63
507
  outstandingCount: e.length,
64
508
  outstandingTotal: e.reduce((e, t) => e + t.total, 0),
509
+ paidCount: H.filter((e) => e.status === "PAID").length,
510
+ overdueCount: H.filter((e) => e.status === "OVERDUE").length,
511
+ distribution: r,
65
512
  currency: t
66
513
  };
67
- }, [P]);
68
- return /* @__PURE__ */ v(n, {
514
+ }, [H]);
515
+ return /* @__PURE__ */ P(n, {
69
516
  title: "Invoices",
70
517
  description: "Your billing ledger across every relationship",
518
+ actions: /* @__PURE__ */ P("button", {
519
+ type: "button",
520
+ onClick: () => x(!0),
521
+ 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",
522
+ children: [/* @__PURE__ */ N(O, { className: "h-4 w-4" }), " Create invoice"]
523
+ }),
71
524
  children: [
72
- /* @__PURE__ */ _(y, {
525
+ /* @__PURE__ */ N(I, {
73
526
  className: "mb-4",
74
527
  children: "Every invoice from your recurring relationships in one ledger. Filter by status and settle issued or overdue invoices directly."
75
528
  }),
76
- !k && !A && P.length > 0 && /* @__PURE__ */ v("div", {
77
- className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
78
- children: [/* @__PURE__ */ _(r, {
79
- featured: !0,
80
- icon: /* @__PURE__ */ _(h, {}),
81
- value: i(I.outstandingTotal, I.currency, { maximumFractionDigits: 0 }),
82
- label: "Outstanding (this page)"
83
- }), /* @__PURE__ */ _(r, {
84
- icon: /* @__PURE__ */ _(m, {}),
85
- value: I.outstandingCount,
86
- label: "Open invoices (this page)"
529
+ !A && !j && H.length > 0 && /* @__PURE__ */ P("div", {
530
+ className: "grid grid-cols-1 gap-4 lg:grid-cols-3",
531
+ children: [/* @__PURE__ */ P("div", {
532
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:col-span-2",
533
+ children: [
534
+ /* @__PURE__ */ N(r, {
535
+ featured: !0,
536
+ icon: /* @__PURE__ */ N(k, {}),
537
+ value: i(J.outstandingTotal, J.currency, { maximumFractionDigits: 0 }),
538
+ label: "Outstanding · this page"
539
+ }),
540
+ /* @__PURE__ */ N(r, {
541
+ icon: /* @__PURE__ */ N(D, {}),
542
+ value: J.outstandingCount,
543
+ label: "Open invoices"
544
+ }),
545
+ /* @__PURE__ */ N(r, {
546
+ icon: /* @__PURE__ */ N(w, {}),
547
+ value: J.overdueCount,
548
+ label: "Overdue",
549
+ ...J.overdueCount > 0 ? { trend: "down" } : {}
550
+ }),
551
+ /* @__PURE__ */ N(r, {
552
+ icon: /* @__PURE__ */ N(T, {}),
553
+ value: J.paidCount,
554
+ label: "Paid",
555
+ ...J.paidCount > 0 ? { trend: "up" } : {}
556
+ })
557
+ ]
558
+ }), /* @__PURE__ */ P(F, {
559
+ treatment: "insight",
560
+ className: "p-6",
561
+ children: [
562
+ /* @__PURE__ */ N("p", {
563
+ className: "text-sm font-semibold text-text-primary",
564
+ children: "Status breakdown"
565
+ }),
566
+ /* @__PURE__ */ N("p", {
567
+ className: "text-xs text-text-secondary",
568
+ children: "Across the invoices on this page"
569
+ }),
570
+ /* @__PURE__ */ P("div", {
571
+ className: "mt-4 flex flex-col items-center gap-5 sm:flex-row sm:items-center lg:flex-col xl:flex-row",
572
+ children: [/* @__PURE__ */ N(p, {
573
+ data: J.distribution,
574
+ size: 132,
575
+ thickness: 16,
576
+ centerLabel: String(H.length),
577
+ centerSub: "invoices",
578
+ className: "shrink-0"
579
+ }), /* @__PURE__ */ N(m, {
580
+ data: J.distribution,
581
+ className: "w-full min-w-0 flex-1",
582
+ valueFormat: (e) => String(e)
583
+ })]
584
+ })
585
+ ]
87
586
  })]
88
587
  }),
89
- /* @__PURE__ */ _("div", {
588
+ /* @__PURE__ */ N("div", {
90
589
  className: "mt-6 flex flex-wrap items-center gap-3",
91
- children: /* @__PURE__ */ _("select", {
92
- value: E,
590
+ children: /* @__PURE__ */ N("select", {
591
+ "aria-label": "Filter by status",
592
+ value: g,
93
593
  onChange: (e) => {
94
- D(e.target.value), T(0);
594
+ y(e.target.value), h(0);
95
595
  },
96
596
  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)]",
97
- children: x.map((e) => /* @__PURE__ */ _("option", {
597
+ children: B.map((e) => /* @__PURE__ */ N("option", {
98
598
  value: e.value,
99
599
  children: e.label
100
600
  }, e.value))
101
601
  })
102
602
  }),
103
- k ? /* @__PURE__ */ _(l, { label: "Loading invoices…" }) : A ? /* @__PURE__ */ _(c, {
603
+ A ? /* @__PURE__ */ N(u, { label: "Loading invoices…" }) : j ? /* @__PURE__ */ N(l, {
104
604
  message: "Could not load invoices.",
105
- onRetry: () => void j()
106
- }) : P.length === 0 ? /* @__PURE__ */ _("div", {
605
+ onRetry: () => void R()
606
+ }) : H.length === 0 ? /* @__PURE__ */ N("div", {
107
607
  className: "mt-6",
108
- children: /* @__PURE__ */ _(s, {
608
+ children: /* @__PURE__ */ N(s, {
109
609
  illustration: "empty-data",
110
- icon: /* @__PURE__ */ _(h, { className: "h-12 w-12" }),
610
+ icon: /* @__PURE__ */ N(k, { className: "h-12 w-12" }),
111
611
  title: "No invoices",
112
- description: E ? "No invoices match this status filter." : "Invoices from your relationships will appear here."
612
+ description: g ? "No invoices match this status filter." : "Invoices from your relationships will appear here — or create one manually.",
613
+ action: /* @__PURE__ */ N("button", {
614
+ type: "button",
615
+ onClick: () => x(!0),
616
+ className: "rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90",
617
+ children: "Create invoice"
618
+ })
113
619
  })
114
- }) : /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ v("div", {
620
+ }) : /* @__PURE__ */ P(M, { children: [/* @__PURE__ */ P("div", {
115
621
  className: "mt-6 overflow-hidden rounded-lg border border-border-subtle bg-bg-surface",
116
- children: [/* @__PURE__ */ v("div", {
622
+ children: [/* @__PURE__ */ P("div", {
117
623
  className: "hidden grid-cols-[1.5fr_1fr_1fr_1fr_auto] gap-4 border-b border-border-subtle bg-bg-sunken px-4 py-2.5 text-xs font-medium text-text-secondary sm:grid",
118
624
  children: [
119
- /* @__PURE__ */ _("span", { children: "Invoice" }),
120
- /* @__PURE__ */ _("span", { children: "Status" }),
121
- /* @__PURE__ */ _("span", {
625
+ /* @__PURE__ */ N("span", { children: "Invoice" }),
626
+ /* @__PURE__ */ N("span", { children: "Status" }),
627
+ /* @__PURE__ */ N("span", {
122
628
  className: "text-right",
123
629
  children: "Total"
124
630
  }),
125
- /* @__PURE__ */ _("span", {
631
+ /* @__PURE__ */ N("span", {
126
632
  className: "text-right",
127
633
  children: "Due"
128
634
  }),
129
- /* @__PURE__ */ _("span", {
635
+ /* @__PURE__ */ N("span", {
130
636
  className: "text-right",
131
637
  children: "Action"
132
638
  })
133
639
  ]
134
- }), /* @__PURE__ */ _("div", {
640
+ }), /* @__PURE__ */ N("div", {
135
641
  className: "divide-y divide-border-subtle",
136
- children: P.map((e) => /* @__PURE__ */ v("div", {
137
- className: "grid grid-cols-1 gap-2 px-4 py-3 sm:grid-cols-[1.5fr_1fr_1fr_1fr_auto] sm:items-center sm:gap-4",
642
+ children: H.map((e) => /* @__PURE__ */ P("div", {
643
+ className: "grid grid-cols-1 gap-2 px-4 py-3 transition-colors hover:bg-bg-sunken/50 sm:grid-cols-[1.5fr_1fr_1fr_1fr_auto] sm:items-center sm:gap-4",
138
644
  children: [
139
- /* @__PURE__ */ v("button", {
645
+ /* @__PURE__ */ P("button", {
140
646
  type: "button",
141
- onClick: () => C(`/relationships/${e.relationshipId}`),
647
+ onClick: () => c(`/relationships/${e.relationshipId}`),
142
648
  className: "min-w-0 text-left",
143
- children: [/* @__PURE__ */ _("p", {
649
+ children: [/* @__PURE__ */ N("p", {
144
650
  className: "truncate text-sm font-medium text-text-primary",
145
651
  children: e.number || `Invoice ${e.id.slice(0, 8)}`
146
- }), /* @__PURE__ */ _("p", {
652
+ }), /* @__PURE__ */ N("p", {
147
653
  className: "text-xs text-text-secondary",
148
654
  children: e.issueDate ? `Issued ${a(e.issueDate)}` : "—"
149
655
  })]
150
656
  }),
151
- /* @__PURE__ */ _("span", { children: /* @__PURE__ */ _(o, { status: e.status }) }),
152
- /* @__PURE__ */ _("span", {
657
+ /* @__PURE__ */ N("span", { children: /* @__PURE__ */ N(o, { status: e.status }) }),
658
+ /* @__PURE__ */ N("span", {
153
659
  className: "text-sm font-semibold tabular-nums text-text-primary sm:text-right",
154
660
  children: i(e.total, e.currency)
155
661
  }),
156
- /* @__PURE__ */ _("span", {
662
+ /* @__PURE__ */ N("span", {
157
663
  className: "text-xs text-text-secondary sm:text-right",
158
664
  children: e.dueDate ? a(e.dueDate) : "—"
159
665
  }),
160
- /* @__PURE__ */ _("span", {
666
+ /* @__PURE__ */ N("span", {
161
667
  className: "sm:text-right",
162
- children: S.has(e.status) ? /* @__PURE__ */ _("button", {
668
+ children: U.has(e.status) ? /* @__PURE__ */ P("button", {
163
669
  type: "button",
164
- disabled: N.isPending,
165
- onClick: () => N.mutate(e.id),
166
- className: "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",
167
- children: "Mark paid"
168
- }) : e.paidAt ? /* @__PURE__ */ v("span", {
670
+ disabled: V.isPending,
671
+ onClick: () => V.mutate(e.id),
672
+ className: "inline-flex items-center gap-1.5 rounded-lg border border-status-success-text/30 bg-status-success-bg-subtle px-3 py-1.5 text-xs font-medium text-status-success-text hover:opacity-90 disabled:opacity-50",
673
+ children: [/* @__PURE__ */ N(T, {
674
+ className: "h-3.5 w-3.5",
675
+ "aria-hidden": !0
676
+ }), "Mark paid"]
677
+ }) : e.paidAt ? /* @__PURE__ */ P("span", {
169
678
  className: "text-xs text-text-secondary",
170
679
  children: ["Paid ", a(e.paidAt)]
171
- }) : /* @__PURE__ */ _("span", {
680
+ }) : /* @__PURE__ */ N("span", {
172
681
  className: "text-xs text-text-secondary",
173
682
  children: "—"
174
683
  })
@@ -176,36 +685,38 @@ function C() {
176
685
  ]
177
686
  }, e.id))
178
687
  })]
179
- }), /* @__PURE__ */ v("div", {
688
+ }), /* @__PURE__ */ P("div", {
180
689
  className: "mt-4 flex items-center justify-between",
181
- children: [/* @__PURE__ */ v("p", {
690
+ children: [/* @__PURE__ */ P("p", {
182
691
  className: "text-xs text-text-secondary",
183
692
  children: [
184
693
  "Page ",
185
- w + 1,
186
- M ? " · refreshing…" : ""
694
+ d + 1,
695
+ z ? " · refreshing…" : ""
187
696
  ]
188
- }), /* @__PURE__ */ v("div", {
697
+ }), /* @__PURE__ */ P("div", {
189
698
  className: "flex gap-2",
190
- children: [/* @__PURE__ */ _("button", {
699
+ children: [/* @__PURE__ */ N("button", {
191
700
  type: "button",
192
- disabled: w === 0,
193
- onClick: () => T((e) => Math.max(0, e - 1)),
194
- className: t("rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium", w === 0 ? "cursor-not-allowed text-text-secondary opacity-50" : "text-text-primary hover:bg-bg-sunken"),
701
+ disabled: d === 0,
702
+ onClick: () => h((e) => Math.max(0, e - 1)),
703
+ className: t("rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium", d === 0 ? "cursor-not-allowed text-text-secondary opacity-50" : "text-text-primary hover:bg-bg-sunken"),
195
704
  children: "Previous"
196
- }), /* @__PURE__ */ _("button", {
705
+ }), /* @__PURE__ */ N("button", {
197
706
  type: "button",
198
- disabled: !F,
199
- onClick: () => T((e) => e + 1),
200
- 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"),
707
+ disabled: !K,
708
+ onClick: () => h((e) => e + 1),
709
+ className: t("rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium", K ? "text-text-primary hover:bg-bg-sunken" : "cursor-not-allowed text-text-secondary opacity-50"),
201
710
  children: "Next"
202
711
  })]
203
712
  })]
204
- })] })
713
+ })] }),
714
+ /* @__PURE__ */ N(q, {}),
715
+ b && /* @__PURE__ */ N(G, { onClose: () => x(!1) })
205
716
  ]
206
717
  });
207
718
  }
208
719
  //#endregion
209
- export { C as InvoicesPage };
720
+ export { J as InvoicesPage };
210
721
 
211
722
  //# sourceMappingURL=InvoicesPage.js.map