@burdenoff/microfe-subscriberbot 2026.1023.2 → 2026.1030.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/subscriberbot/SubscriberBotRoutes.d.ts.map +1 -1
  2. package/dist/subscriberbot/SubscriberBotRoutes.js +2 -2
  3. package/dist/subscriberbot/SubscriberBotRoutes.js.map +1 -1
  4. package/dist/subscriberbot/admin/AdminDashboardPage.js +5 -5
  5. package/dist/subscriberbot/admin/AdminDashboardPage.js.map +1 -1
  6. package/dist/subscriberbot/components/PostureOptions.d.ts +10 -0
  7. package/dist/subscriberbot/components/PostureOptions.d.ts.map +1 -0
  8. package/dist/subscriberbot/components/PostureOptions.js +20 -0
  9. package/dist/subscriberbot/components/PostureOptions.js.map +1 -0
  10. package/dist/subscriberbot/components/index.d.ts +2 -0
  11. package/dist/subscriberbot/components/index.d.ts.map +1 -1
  12. package/dist/subscriberbot/components/index.js +1 -0
  13. package/dist/subscriberbot/hooks/index.d.ts +3 -0
  14. package/dist/subscriberbot/hooks/index.d.ts.map +1 -1
  15. package/dist/subscriberbot/hooks/index.js +3 -1
  16. package/dist/subscriberbot/hooks/useAutonomousAgentMode.d.ts +15 -0
  17. package/dist/subscriberbot/hooks/useAutonomousAgentMode.d.ts.map +1 -0
  18. package/dist/subscriberbot/hooks/useAutonomousAgentMode.js +13 -0
  19. package/dist/subscriberbot/hooks/useAutonomousAgentMode.js.map +1 -0
  20. package/dist/subscriberbot/hooks/useRouteSelection.d.ts +15 -0
  21. package/dist/subscriberbot/hooks/useRouteSelection.d.ts.map +1 -0
  22. package/dist/subscriberbot/hooks/useRouteSelection.js +18 -0
  23. package/dist/subscriberbot/hooks/useRouteSelection.js.map +1 -0
  24. package/dist/subscriberbot/pages/AssetVaultPage.js +16 -16
  25. package/dist/subscriberbot/pages/DashboardPage.js +23 -23
  26. package/dist/subscriberbot/pages/DashboardPage.js.map +1 -1
  27. package/dist/subscriberbot/pages/InboxPage.d.ts.map +1 -1
  28. package/dist/subscriberbot/pages/InboxPage.js +249 -248
  29. package/dist/subscriberbot/pages/InboxPage.js.map +1 -1
  30. package/dist/subscriberbot/pages/InsightsPage.js +14 -14
  31. package/dist/subscriberbot/pages/InvoicesPage.js +66 -66
  32. package/dist/subscriberbot/pages/WorkflowsPage.d.ts.map +1 -1
  33. package/dist/subscriberbot/pages/WorkflowsPage.js +183 -182
  34. package/dist/subscriberbot/pages/WorkflowsPage.js.map +1 -1
  35. package/dist/subscriberbot/pages/agents/AgentDetailPage.d.ts.map +1 -1
  36. package/dist/subscriberbot/pages/agents/AgentDetailPage.js +184 -182
  37. package/dist/subscriberbot/pages/agents/AgentDetailPage.js.map +1 -1
  38. package/dist/subscriberbot/pages/agents/AgentNewPage.d.ts.map +1 -1
  39. package/dist/subscriberbot/pages/agents/AgentNewPage.js +77 -79
  40. package/dist/subscriberbot/pages/agents/AgentNewPage.js.map +1 -1
  41. package/dist/subscriberbot/pages/agents/AgentsListPage.d.ts.map +1 -1
  42. package/dist/subscriberbot/pages/agents/AgentsListPage.js +134 -133
  43. package/dist/subscriberbot/pages/agents/AgentsListPage.js.map +1 -1
  44. package/dist/subscriberbot/pages/providers/ProviderDetailPage.js +64 -64
  45. package/dist/subscriberbot/pages/providers/ProviderNewPage.js +3 -3
  46. package/dist/subscriberbot/pages/providers/ProvidersListPage.js +18 -18
  47. package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js +76 -76
  48. package/dist/subscriberbot/pages/relationships/RelationshipNewPage.js +6 -6
  49. package/dist/subscriberbot/pages/relationships/RelationshipsListPage.js +7 -7
  50. package/dist/subscriberbot/utils/pageContext.d.ts +43 -11
  51. package/dist/subscriberbot/utils/pageContext.d.ts.map +1 -1
  52. package/dist/subscriberbot/utils/pageContext.js +42 -37
  53. package/dist/subscriberbot/utils/pageContext.js.map +1 -1
  54. package/package.json +4 -2
@@ -4,13 +4,13 @@ import { MetricsCard as n } from "../components/MetricsCard.js";
4
4
  import { formatCurrency as r, formatEnumLabel as i, formatRelativeTime as a } from "../utils/formatters.js";
5
5
  import { StatusBadge as o } from "../components/StatusBadge.js";
6
6
  import { EmptyState as s } from "../components/EmptyState.js";
7
- import { ErrorView as c, LoadingView as l, Section as u } from "../components/StateViews.js";
7
+ import { useTr as c } from "../hooks/useTr.js";
8
+ import { ErrorView as ee, LoadingView as l, Section as u } from "../components/StateViews.js";
8
9
  import { Donut as d, Legend as f, Timeline as p } from "../components/viz.js";
9
10
  import "../components/index.js";
10
11
  import { useInboxMessages as m, useInvoices as h, useRelationships as g, useRenewalEvents as _ } from "../hooks/useSubscriberBotApi.js";
11
- import { useTr as v } from "../hooks/useTr.js";
12
- import { useMemo as y } from "react";
13
- import { AlertTriangle as b, ArrowRight as x, ArrowUpRight as S, Bot as C, CalendarClock as w, Inbox as T, Repeat2 as E, ShieldCheck as ee, Sparkles as D, Wallet as O } from "lucide-react";
12
+ import { useMemo as v } from "react";
13
+ import { AlertTriangle as y, ArrowRight as b, ArrowUpRight as x, Bot as S, CalendarClock as C, Inbox as w, Repeat2 as T, ShieldCheck as E, Sparkles as D, Wallet as O } from "lucide-react";
14
14
  import { jsx as k, jsxs as A } from "react/jsx-runtime";
15
15
  import { GlassCard as j, PagePurpose as M } from "@burdenoff/fe-libs/ui";
16
16
  //#region src/subscriberbot/pages/DashboardPage.tsx
@@ -25,7 +25,7 @@ var N = {
25
25
  USAGE_BASED: 1
26
26
  }, P = 1e3 * 60 * 60 * 24;
27
27
  function F() {
28
- let { navigateTo: F, currentUser: I } = e(), L = v(), R = g({ pagination: {
28
+ let { navigateTo: F, currentUser: I } = e(), L = c(), R = g({ pagination: {
29
29
  limit: 100,
30
30
  offset: 0
31
31
  } }), z = h({ pagination: {
@@ -34,10 +34,10 @@ function F() {
34
34
  } }), B = m({ pagination: {
35
35
  limit: 50,
36
36
  offset: 0
37
- } }), V = _({ upcomingOnly: !0 }), H = y(() => R.data?.items ?? [], [R.data]), U = y(() => z.data?.items ?? [], [z.data]), W = y(() => B.data?.items ?? [], [B.data]), G = y(() => V.data ?? [], [V.data]), K = y(() => {
37
+ } }), V = _({ upcomingOnly: !0 }), H = v(() => R.data?.items ?? [], [R.data]), U = v(() => z.data?.items ?? [], [z.data]), W = v(() => B.data?.items ?? [], [B.data]), G = v(() => V.data ?? [], [V.data]), K = v(() => {
38
38
  let e = /* @__PURE__ */ new Map();
39
39
  return H.forEach((t) => e.set(t.id, t)), e;
40
- }, [H]), q = y(() => {
40
+ }, [H]), q = v(() => {
41
41
  let e = H.filter((e) => e.status === "ACTIVE" || e.status === "TRIAL"), t = e.reduce((e, t) => e + t.amount * (N[t.interval] ?? 1), 0), n = e[0]?.currency ?? H[0]?.currency ?? "USD", r = W.filter((e) => e.actionRequired).length, i = U.filter((e) => e.status === "ISSUED" || e.status === "OVERDUE" || e.status === "FAILED"), a = i.reduce((e, t) => e + t.total, 0), o = H.filter((e) => e.status === "PAST_DUE" || e.status === "LAPSED").length;
42
42
  return {
43
43
  activeCount: e.length,
@@ -53,13 +53,13 @@ function F() {
53
53
  H,
54
54
  W,
55
55
  U
56
- ]), J = y(() => {
56
+ ]), J = v(() => {
57
57
  let e = /* @__PURE__ */ new Map();
58
58
  return H.forEach((t) => e.set(t.status, (e.get(t.status) ?? 0) + 1)), Array.from(e.entries()).sort((e, t) => t[1] - e[1]).map(([e, t]) => ({
59
59
  label: i(e),
60
60
  value: t
61
61
  }));
62
- }, [H]), Y = y(() => {
62
+ }, [H]), Y = v(() => {
63
63
  let e = Date.now(), t = G.map((t) => {
64
64
  let n = K.get(t.relationshipId), r = new Date(t.occursAt).getTime();
65
65
  return {
@@ -92,12 +92,12 @@ function F() {
92
92
  G,
93
93
  H,
94
94
  K
95
- ]), X = y(() => Y.slice(0, 6).map((e) => ({
95
+ ]), X = v(() => Y.slice(0, 6).map((e) => ({
96
96
  title: e.title,
97
97
  sub: a(e.when),
98
98
  tone: e.soon ? "warning" : "default",
99
99
  meta: e.amount == null ? void 0 : r(e.amount, e.currency ?? q.primaryCurrency, { maximumFractionDigits: 0 })
100
- })), [Y, q.primaryCurrency]), Z = y(() => W.filter((e) => e.actionRequired), [W]), Q = R.isLoading || z.isLoading || B.isLoading || V.isLoading, te = R.isError && z.isError && B.isError && V.isError;
100
+ })), [Y, q.primaryCurrency]), Z = v(() => W.filter((e) => e.actionRequired), [W]), Q = R.isLoading || z.isLoading || B.isLoading || V.isLoading, te = R.isError && z.isError && B.isError && V.isError;
101
101
  if (Q) return /* @__PURE__ */ k(t, {
102
102
  title: L("dashboard.title", "Overview"),
103
103
  description: L("dashboard.description", "Your recurring digital relationships at a glance"),
@@ -106,7 +106,7 @@ function F() {
106
106
  if (te) return /* @__PURE__ */ k(t, {
107
107
  title: L("dashboard.title", "Overview"),
108
108
  description: L("dashboard.description", "Your recurring digital relationships at a glance"),
109
- children: /* @__PURE__ */ k(c, {
109
+ children: /* @__PURE__ */ k(ee, {
110
110
  message: L("dashboard.error", "We couldn't reach the Subscriber Bot service. Please try again."),
111
111
  onRetry: () => {
112
112
  R.refetch(), z.refetch(), B.refetch(), V.refetch();
@@ -119,7 +119,7 @@ function F() {
119
119
  descriptionKey: "dashboard.quickActions.addRelationship.description",
120
120
  fallbackLabel: "Add a relationship",
121
121
  fallbackDescription: "Track a new subscription",
122
- icon: /* @__PURE__ */ k(E, { className: "h-5 w-5" }),
122
+ icon: /* @__PURE__ */ k(T, { className: "h-5 w-5" }),
123
123
  path: "/relationships/new"
124
124
  },
125
125
  {
@@ -135,7 +135,7 @@ function F() {
135
135
  descriptionKey: "dashboard.quickActions.openInbox.description",
136
136
  fallbackLabel: "Open inbox",
137
137
  fallbackDescription: "Merged communications",
138
- icon: /* @__PURE__ */ k(T, { className: "h-5 w-5" }),
138
+ icon: /* @__PURE__ */ k(w, { className: "h-5 w-5" }),
139
139
  path: "/inbox"
140
140
  },
141
141
  {
@@ -143,7 +143,7 @@ function F() {
143
143
  descriptionKey: "dashboard.quickActions.configureAgents.description",
144
144
  fallbackLabel: "Configure agents",
145
145
  fallbackDescription: "Automate your portfolio",
146
- icon: /* @__PURE__ */ k(C, { className: "h-5 w-5" }),
146
+ icon: /* @__PURE__ */ k(S, { className: "h-5 w-5" }),
147
147
  path: "/agents"
148
148
  }
149
149
  ];
@@ -165,17 +165,17 @@ function F() {
165
165
  label: L("dashboard.metrics.recurringSpend", "Recurring spend / month")
166
166
  }),
167
167
  /* @__PURE__ */ k(n, {
168
- icon: /* @__PURE__ */ k(E, {}),
168
+ icon: /* @__PURE__ */ k(T, {}),
169
169
  value: q.activeCount,
170
170
  label: L("dashboard.metrics.activeRelationships", "Active relationships")
171
171
  }),
172
172
  /* @__PURE__ */ k(n, {
173
- icon: /* @__PURE__ */ k(w, {}),
173
+ icon: /* @__PURE__ */ k(C, {}),
174
174
  value: Y.length,
175
175
  label: L("dashboard.metrics.upcomingRenewals", "Upcoming renewals")
176
176
  }),
177
177
  /* @__PURE__ */ k(n, {
178
- icon: /* @__PURE__ */ k(b, {}),
178
+ icon: /* @__PURE__ */ k(y, {}),
179
179
  value: q.actionRequired,
180
180
  label: L("dashboard.metrics.needsAction", "Need your action"),
181
181
  trend: q.actionRequired > 0 ? "down" : void 0
@@ -197,7 +197,7 @@ function F() {
197
197
  children: [
198
198
  L("common.viewAll", "View all"),
199
199
  " ",
200
- /* @__PURE__ */ k(S, { className: "h-3.5 w-3.5" })
200
+ /* @__PURE__ */ k(x, { className: "h-3.5 w-3.5" })
201
201
  ]
202
202
  }),
203
203
  children: /* @__PURE__ */ k(j, {
@@ -261,7 +261,7 @@ function F() {
261
261
  className: "flex items-center gap-3 px-2 py-4",
262
262
  children: [/* @__PURE__ */ k("span", {
263
263
  className: "flex size-10 shrink-0 items-center justify-center rounded-lg bg-status-success-bg-subtle text-status-success-text",
264
- children: /* @__PURE__ */ k(ee, { className: "h-5 w-5" })
264
+ children: /* @__PURE__ */ k(E, { className: "h-5 w-5" })
265
265
  }), /* @__PURE__ */ A("div", {
266
266
  className: "min-w-0",
267
267
  children: [/* @__PURE__ */ k("p", {
@@ -303,14 +303,14 @@ function F() {
303
303
  className: "mt-6 flex w-full items-center justify-between gap-3 rounded-xl border border-status-warning-text/30 bg-status-warning-bg-subtle px-5 py-4 text-left transition-shadow hover:shadow-md",
304
304
  children: [/* @__PURE__ */ A("div", {
305
305
  className: "flex items-center gap-3",
306
- children: [/* @__PURE__ */ k(b, { className: "h-5 w-5 shrink-0 text-status-warning-text" }), /* @__PURE__ */ k("span", {
306
+ children: [/* @__PURE__ */ k(y, { className: "h-5 w-5 shrink-0 text-status-warning-text" }), /* @__PURE__ */ k("span", {
307
307
  className: "text-sm font-medium text-status-warning-text",
308
308
  children: L("dashboard.openInvoices.label", "{{count}} open invoice · {{amount}} outstanding", {
309
309
  count: q.openInvoiceCount,
310
310
  amount: r(q.openInvoiceTotal, q.primaryCurrency)
311
311
  })
312
312
  })]
313
- }), /* @__PURE__ */ k(x, { className: "h-4 w-4 shrink-0 text-status-warning-text" })]
313
+ }), /* @__PURE__ */ k(b, { className: "h-4 w-4 shrink-0 text-status-warning-text" })]
314
314
  }),
315
315
  /* @__PURE__ */ k(u, {
316
316
  title: L("dashboard.quickActions.title", "Quick actions"),
@@ -336,7 +336,7 @@ function F() {
336
336
  children: L(e.descriptionKey, e.fallbackDescription)
337
337
  })]
338
338
  }),
339
- /* @__PURE__ */ k(x, { className: "h-4 w-4 shrink-0 text-text-secondary transition-transform group-hover:translate-x-0.5" })
339
+ /* @__PURE__ */ k(b, { className: "h-4 w-4 shrink-0 text-text-secondary transition-transform group-hover:translate-x-0.5" })
340
340
  ]
341
341
  }, e.path))
342
342
  })
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../src/subscriberbot/pages/DashboardPage.tsx"],"sourcesContent":["/**\n * Dashboard / Overview\n * @module subscriberbot/pages\n *\n * The Subscriber Bot landing page: a premium fintech console over every\n * recurring digital relationship — total recurring spend, portfolio-by-status,\n * upcoming renewals, the action-required inbox, and outstanding invoices. Every\n * number and chart is COMPUTED from real data returned by\n * `wspace-subscriberbot-svc` — nothing is mocked.\n */\n\nimport { useMemo } from 'react';\nimport {\n Repeat2,\n Wallet,\n CalendarClock,\n Inbox,\n AlertTriangle,\n ArrowRight,\n ArrowUpRight,\n Sparkles,\n Bot,\n ShieldCheck,\n} from 'lucide-react';\nimport { PagePurpose, GlassCard } from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n MetricsCard,\n StatusBadge,\n EmptyState,\n LoadingView,\n ErrorView,\n Section,\n Donut,\n Legend,\n Timeline,\n type Segment,\n type TimelineItem,\n} from '../components';\nimport { useSubscriberBot } from '../context/SubscriberBotContext';\nimport {\n useRelationships,\n useInvoices,\n useInboxMessages,\n useRenewalEvents,\n} from '../hooks/useSubscriberBotApi';\nimport { useTr } from '../hooks/useTr';\nimport { formatCurrency, formatRelativeTime, formatEnumLabel } from '../utils/formatters';\nimport type { SubBillingInterval, SubRelationship, SubRenewalEvent } from '../types';\n\n// Normalize any billing interval to an approximate monthly multiplier so we can\n// express total recurring spend as one comparable \"per month\" number.\nconst MONTHLY_FACTOR: Record<SubBillingInterval, number> = {\n DAILY: 30,\n WEEKLY: 4.345,\n MONTHLY: 1,\n QUARTERLY: 1 / 3,\n YEARLY: 1 / 12,\n LIFETIME: 0,\n FREE: 0,\n USAGE_BASED: 1,\n};\n\n// A single normalized \"what charges next\" row, sourced from real renewal events\n// when present, otherwise derived from each relationship's own `renewsAt`.\ninterface UpcomingRenewal {\n id: string;\n relationshipId: string;\n title: string;\n when: string;\n amount: number | null;\n currency: string | null;\n soon: boolean;\n}\n\nconst MS_PER_DAY = 1000 * 60 * 60 * 24;\n\nexport function DashboardPage() {\n const { navigateTo, currentUser } = useSubscriberBot();\n const tr = useTr();\n\n const relationshipsQ = useRelationships({ pagination: { limit: 100, offset: 0 } });\n const invoicesQ = useInvoices({ pagination: { limit: 50, offset: 0 } });\n const inboxQ = useInboxMessages({ pagination: { limit: 50, offset: 0 } });\n const renewalsQ = useRenewalEvents({ upcomingOnly: true });\n\n const relationships = useMemo(() => relationshipsQ.data?.items ?? [], [relationshipsQ.data]);\n const invoices = useMemo(() => invoicesQ.data?.items ?? [], [invoicesQ.data]);\n const messages = useMemo(() => inboxQ.data?.items ?? [], [inboxQ.data]);\n const renewals = useMemo(() => renewalsQ.data ?? [], [renewalsQ.data]);\n\n // Fast label lookup so renewal rows can name the relationship they belong to.\n const relationshipById = useMemo(() => {\n const map = new Map<string, SubRelationship>();\n relationships.forEach((r) => map.set(r.id, r));\n return map;\n }, [relationships]);\n\n const stats = useMemo(() => {\n const active = relationships.filter((r) => r.status === 'ACTIVE' || r.status === 'TRIAL');\n const monthlySpend = active.reduce(\n (sum, r) => sum + r.amount * (MONTHLY_FACTOR[r.interval] ?? 1),\n 0\n );\n const primaryCurrency = active[0]?.currency ?? relationships[0]?.currency ?? 'USD';\n // BOFF-8660 — KPI must match the Universal Inbox \"need action\" counter\n // and the on-page Needs-attention list, both of which count by\n // `actionRequired` alone (read state is irrelevant). The previous\n // `&& !m.isRead` clause made the tile show 0 the moment the user read\n // the only action-required message, contradicting the surfaces below.\n const actionRequired = messages.filter((m) => m.actionRequired).length;\n const openInvoices = invoices.filter(\n (i) => i.status === 'ISSUED' || i.status === 'OVERDUE' || i.status === 'FAILED'\n );\n const openInvoiceTotal = openInvoices.reduce((sum, i) => sum + i.total, 0);\n const atRisk = relationships.filter(\n (r) => r.status === 'PAST_DUE' || r.status === 'LAPSED'\n ).length;\n\n return {\n activeCount: active.length,\n totalCount: relationships.length,\n monthlySpend,\n primaryCurrency,\n actionRequired,\n openInvoiceCount: openInvoices.length,\n openInvoiceTotal,\n atRisk,\n };\n }, [relationships, messages, invoices]);\n\n // Portfolio-by-status → donut segments (grouped from real relationships).\n const statusSegments = useMemo<Segment[]>(() => {\n const counts = new Map<string, number>();\n relationships.forEach((r) => counts.set(r.status, (counts.get(r.status) ?? 0) + 1));\n return Array.from(counts.entries())\n .sort((a, b) => b[1] - a[1])\n .map(([status, value]) => ({ label: formatEnumLabel(status), value }));\n }, [relationships]);\n\n // Normalize upcoming renewals: prefer real renewal events, fall back to\n // relationship `renewsAt` when the event stream is empty. Sorted soonest-first.\n const upcoming = useMemo<UpcomingRenewal[]>(() => {\n const now = Date.now();\n const fromEvents: UpcomingRenewal[] = renewals.map((e: SubRenewalEvent) => {\n const rel = relationshipById.get(e.relationshipId);\n const t = new Date(e.occursAt).getTime();\n return {\n id: e.id,\n relationshipId: e.relationshipId,\n title: rel?.label ?? formatEnumLabel(e.eventType),\n when: e.occursAt,\n amount: e.amount ?? rel?.amount ?? null,\n currency: e.currency ?? rel?.currency ?? null,\n soon: !Number.isNaN(t) && t - now <= 7 * MS_PER_DAY,\n };\n });\n\n if (fromEvents.length > 0) {\n return fromEvents\n .filter((u) => !Number.isNaN(new Date(u.when).getTime()))\n .sort((a, b) => new Date(a.when).getTime() - new Date(b.when).getTime());\n }\n\n // Derive from relationships that carry a future renewal date.\n return relationships\n .filter((r) => {\n if (!r.renewsAt) return false;\n const t = new Date(r.renewsAt).getTime();\n return !Number.isNaN(t) && t >= now;\n })\n .map((r) => {\n const t = new Date(r.renewsAt as string).getTime();\n return {\n id: r.id,\n relationshipId: r.id,\n title: r.label,\n when: r.renewsAt as string,\n amount: r.amount,\n currency: r.currency,\n soon: t - now <= 7 * MS_PER_DAY,\n };\n })\n .sort((a, b) => new Date(a.when).getTime() - new Date(b.when).getTime());\n }, [renewals, relationships, relationshipById]);\n\n const timelineItems = useMemo<TimelineItem[]>(\n () =>\n upcoming.slice(0, 6).map((u) => ({\n title: u.title,\n sub: formatRelativeTime(u.when),\n tone: u.soon ? 'warning' : 'default',\n meta:\n u.amount != null\n ? formatCurrency(u.amount, u.currency ?? stats.primaryCurrency, {\n maximumFractionDigits: 0,\n })\n : undefined,\n })),\n [upcoming, stats.primaryCurrency]\n );\n\n const actionMessages = useMemo(() => messages.filter((m) => m.actionRequired), [messages]);\n\n const isLoading =\n relationshipsQ.isLoading || invoicesQ.isLoading || inboxQ.isLoading || renewalsQ.isLoading;\n const isError =\n relationshipsQ.isError && invoicesQ.isError && inboxQ.isError && renewalsQ.isError;\n\n if (isLoading) {\n return (\n <PageLayout\n title={tr('dashboard.title', 'Overview')}\n description={tr(\n 'dashboard.description',\n 'Your recurring digital relationships at a glance'\n )}\n >\n <LoadingView label={tr('dashboard.loading', 'Loading your subscription portfolio…')} />\n </PageLayout>\n );\n }\n\n if (isError) {\n return (\n <PageLayout\n title={tr('dashboard.title', 'Overview')}\n description={tr(\n 'dashboard.description',\n 'Your recurring digital relationships at a glance'\n )}\n >\n <ErrorView\n message={tr(\n 'dashboard.error',\n \"We couldn't reach the Subscriber Bot service. Please try again.\"\n )}\n onRetry={() => {\n void relationshipsQ.refetch();\n void invoicesQ.refetch();\n void inboxQ.refetch();\n void renewalsQ.refetch();\n }}\n />\n </PageLayout>\n );\n }\n\n const greetingName = currentUser?.firstName || currentUser?.name?.split(' ')[0];\n\n const quickActions = [\n {\n labelKey: 'dashboard.quickActions.addRelationship.label',\n descriptionKey: 'dashboard.quickActions.addRelationship.description',\n fallbackLabel: 'Add a relationship',\n fallbackDescription: 'Track a new subscription',\n icon: <Repeat2 className=\"h-5 w-5\" />,\n path: '/relationships/new',\n },\n {\n labelKey: 'dashboard.quickActions.discoverProviders.label',\n descriptionKey: 'dashboard.quickActions.discoverProviders.description',\n fallbackLabel: 'Discover providers',\n fallbackDescription: 'Browse the registry',\n icon: <Sparkles className=\"h-5 w-5\" />,\n path: '/providers',\n },\n {\n labelKey: 'dashboard.quickActions.openInbox.label',\n descriptionKey: 'dashboard.quickActions.openInbox.description',\n fallbackLabel: 'Open inbox',\n fallbackDescription: 'Merged communications',\n icon: <Inbox className=\"h-5 w-5\" />,\n path: '/inbox',\n },\n {\n labelKey: 'dashboard.quickActions.configureAgents.label',\n descriptionKey: 'dashboard.quickActions.configureAgents.description',\n fallbackLabel: 'Configure agents',\n fallbackDescription: 'Automate your portfolio',\n icon: <Bot className=\"h-5 w-5\" />,\n path: '/agents',\n },\n ];\n\n return (\n <PageLayout\n title={\n greetingName\n ? tr('dashboard.welcomeBack', 'Welcome back, {{name}}', { name: greetingName })\n : tr('dashboard.title', 'Overview')\n }\n description={tr(\n 'dashboard.description',\n 'Everything you pay for on repeat — in one intelligent console.'\n )}\n >\n <PagePurpose className=\"mb-6\">\n {tr(\n 'dashboard.purpose',\n 'Subscriber Bot is your system of record for every recurring digital relationship. See total recurring spend, what renews next, what needs action, and what to optimize — all from real data across your providers.'\n )}\n </PagePurpose>\n\n {/* KPI tiles */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <MetricsCard\n featured\n icon={<Wallet />}\n value={formatCurrency(stats.monthlySpend, stats.primaryCurrency, {\n maximumFractionDigits: 0,\n })}\n label={tr('dashboard.metrics.recurringSpend', 'Recurring spend / month')}\n />\n <MetricsCard\n icon={<Repeat2 />}\n value={stats.activeCount}\n label={tr('dashboard.metrics.activeRelationships', 'Active relationships')}\n />\n <MetricsCard\n icon={<CalendarClock />}\n value={upcoming.length}\n label={tr('dashboard.metrics.upcomingRenewals', 'Upcoming renewals')}\n />\n <MetricsCard\n icon={<AlertTriangle />}\n value={stats.actionRequired}\n label={tr('dashboard.metrics.needsAction', 'Need your action')}\n trend={stats.actionRequired > 0 ? 'down' : undefined}\n />\n </div>\n\n {/* Main console: renewals (left) + portfolio donut & needs-attention (right) */}\n <div className=\"mt-8 grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Upcoming renewals timeline — spans two columns on desktop */}\n <div className=\"lg:col-span-2\">\n <Section\n title={tr('dashboard.renewals.title', 'Upcoming renewals')}\n description={tr(\n 'dashboard.renewals.description',\n \"What's charging next across your portfolio\"\n )}\n className=\"mt-0\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/relationships')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-text-link hover:underline\"\n >\n {tr('common.viewAll', 'View all')} <ArrowUpRight className=\"h-3.5 w-3.5\" />\n </button>\n }\n >\n <GlassCard treatment=\"plain\" className=\"p-5\">\n {timelineItems.length === 0 ? (\n <EmptyState\n illustration=\"empty-calendar\"\n title={tr('dashboard.renewals.emptyTitle', 'No upcoming renewals')}\n description={tr(\n 'dashboard.renewals.emptyDescription',\n 'Renewals you track will appear here ahead of time.'\n )}\n />\n ) : (\n <Timeline items={timelineItems} />\n )}\n </GlassCard>\n </Section>\n </div>\n\n {/* Right rail: portfolio-by-status donut + needs-attention panel */}\n <div className=\"flex flex-col gap-6\">\n <Section\n title={tr('dashboard.portfolio.title', 'Portfolio by status')}\n description={tr('dashboard.portfolio.description', 'Where your relationships stand')}\n className=\"mt-0\"\n >\n <GlassCard treatment=\"glass\" className=\"p-5\">\n {statusSegments.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title={tr('dashboard.portfolio.emptyTitle', 'No relationships yet')}\n description={tr(\n 'dashboard.portfolio.emptyDescription',\n 'Add your first subscription to see the breakdown.'\n )}\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/relationships/new')}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n {tr('dashboard.portfolio.addRelationship', 'Add relationship')}\n </button>\n }\n />\n ) : (\n <div className=\"flex flex-col items-center gap-5 sm:flex-row sm:items-center lg:flex-col xl:flex-row\">\n <Donut\n data={statusSegments}\n size={148}\n thickness={20}\n centerLabel={String(stats.totalCount)}\n centerSub={tr(\n stats.totalCount === 1\n ? 'dashboard.portfolio.relationshipSingular'\n : 'dashboard.portfolio.relationshipPlural',\n stats.totalCount === 1 ? 'relationship' : 'relationships'\n )}\n className=\"shrink-0\"\n />\n <Legend data={statusSegments} className=\"w-full min-w-0 flex-1\" />\n </div>\n )}\n </GlassCard>\n </Section>\n\n <Section\n title={tr('dashboard.needsAttention.title', 'Needs attention')}\n description={tr(\n 'dashboard.needsAttention.description',\n 'Required actions from the Universal Inbox'\n )}\n className=\"mt-0\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/inbox')}\n className=\"text-sm font-medium text-text-link hover:underline\"\n >\n {tr('dashboard.needsAttention.openInbox', 'Open inbox')}\n </button>\n }\n >\n <GlassCard treatment=\"plain\" className=\"p-3\">\n {actionMessages.length === 0 ? (\n <div className=\"flex items-center gap-3 px-2 py-4\">\n <span className=\"flex size-10 shrink-0 items-center justify-center rounded-lg bg-status-success-bg-subtle text-status-success-text\">\n <ShieldCheck className=\"h-5 w-5\" />\n </span>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('dashboard.needsAttention.allCaughtUp', \"You're all caught up\")}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {tr(\n 'dashboard.needsAttention.allCaughtUpDescription',\n 'Required actions across your channels will surface here.'\n )}\n </p>\n </div>\n </div>\n ) : (\n <ul className=\"space-y-1\">\n {actionMessages.slice(0, 5).map((message) => (\n <li key={message.id}>\n <button\n type=\"button\"\n onClick={() => navigateTo('/inbox')}\n className=\"flex w-full items-center gap-3 rounded-lg border-l-2 border-status-warning-text/60 bg-status-warning-bg-subtle/50 p-3 text-left transition-colors hover:bg-status-warning-bg-subtle\"\n >\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {message.subject ||\n tr('dashboard.needsAttention.noSubject', '(no subject)')}\n </p>\n {message.preview && (\n <p className=\"truncate text-xs text-text-secondary\">\n {message.preview}\n </p>\n )}\n </div>\n <StatusBadge status={message.category} className=\"shrink-0\" />\n </button>\n </li>\n ))}\n </ul>\n )}\n </GlassCard>\n </Section>\n </div>\n </div>\n\n {/* Open-invoice call-out (only when there is real outstanding balance) */}\n {stats.openInvoiceCount > 0 && (\n <button\n type=\"button\"\n onClick={() => navigateTo('/invoices')}\n className=\"mt-6 flex w-full items-center justify-between gap-3 rounded-xl border border-status-warning-text/30 bg-status-warning-bg-subtle px-5 py-4 text-left transition-shadow hover:shadow-md\"\n >\n <div className=\"flex items-center gap-3\">\n <AlertTriangle className=\"h-5 w-5 shrink-0 text-status-warning-text\" />\n <span className=\"text-sm font-medium text-status-warning-text\">\n {tr(\n 'dashboard.openInvoices.label',\n '{{count}} open invoice · {{amount}} outstanding',\n {\n count: stats.openInvoiceCount,\n amount: formatCurrency(stats.openInvoiceTotal, stats.primaryCurrency),\n }\n )}\n </span>\n </div>\n <ArrowRight className=\"h-4 w-4 shrink-0 text-status-warning-text\" />\n </button>\n )}\n\n {/* Quick actions */}\n <Section\n title={tr('dashboard.quickActions.title', 'Quick actions')}\n description={tr('dashboard.quickActions.description', 'Jump straight into your workflow')}\n >\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4\">\n {quickActions.map((qa) => (\n <button\n key={qa.path}\n type=\"button\"\n onClick={() => navigateTo(qa.path)}\n className=\"group flex items-center gap-3 rounded-xl border border-border-subtle bg-bg-surface p-4 text-left transition-all hover:-translate-y-0.5 hover:border-border-seam hover:shadow-md\"\n >\n <span className=\"flex size-10 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n {qa.icon}\n </span>\n <span className=\"min-w-0 flex-1\">\n <span className=\"block text-sm font-medium text-text-primary\">\n {tr(qa.labelKey, qa.fallbackLabel)}\n </span>\n <span className=\"block text-xs text-text-secondary\">\n {tr(qa.descriptionKey, qa.fallbackDescription)}\n </span>\n </span>\n <ArrowRight className=\"h-4 w-4 shrink-0 text-text-secondary transition-transform group-hover:translate-x-0.5\" />\n </button>\n ))}\n </div>\n </Section>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../src/subscriberbot/pages/DashboardPage.tsx"],"sourcesContent":["/**\n * Dashboard / Overview\n * @module subscriberbot/pages\n *\n * The Subscriber Bot landing page: a premium fintech console over every\n * recurring digital relationship — total recurring spend, portfolio-by-status,\n * upcoming renewals, the action-required inbox, and outstanding invoices. Every\n * number and chart is COMPUTED from real data returned by\n * `wspace-subscriberbot-svc` — nothing is mocked.\n */\n\nimport { useMemo } from 'react';\nimport {\n Repeat2,\n Wallet,\n CalendarClock,\n Inbox,\n AlertTriangle,\n ArrowRight,\n ArrowUpRight,\n Sparkles,\n Bot,\n ShieldCheck,\n} from 'lucide-react';\nimport { PagePurpose, GlassCard } from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n MetricsCard,\n StatusBadge,\n EmptyState,\n LoadingView,\n ErrorView,\n Section,\n Donut,\n Legend,\n Timeline,\n type Segment,\n type TimelineItem,\n} from '../components';\nimport { useSubscriberBot } from '../context/SubscriberBotContext';\nimport {\n useRelationships,\n useInvoices,\n useInboxMessages,\n useRenewalEvents,\n} from '../hooks/useSubscriberBotApi';\nimport { useTr } from '../hooks/useTr';\nimport { formatCurrency, formatRelativeTime, formatEnumLabel } from '../utils/formatters';\nimport type { SubBillingInterval, SubRelationship, SubRenewalEvent } from '../types';\n\n// Normalize any billing interval to an approximate monthly multiplier so we can\n// express total recurring spend as one comparable \"per month\" number.\nconst MONTHLY_FACTOR: Record<SubBillingInterval, number> = {\n DAILY: 30,\n WEEKLY: 4.345,\n MONTHLY: 1,\n QUARTERLY: 1 / 3,\n YEARLY: 1 / 12,\n LIFETIME: 0,\n FREE: 0,\n USAGE_BASED: 1,\n};\n\n// A single normalized \"what charges next\" row, sourced from real renewal events\n// when present, otherwise derived from each relationship's own `renewsAt`.\ninterface UpcomingRenewal {\n id: string;\n relationshipId: string;\n title: string;\n when: string;\n amount: number | null;\n currency: string | null;\n soon: boolean;\n}\n\nconst MS_PER_DAY = 1000 * 60 * 60 * 24;\n\nexport function DashboardPage() {\n const { navigateTo, currentUser } = useSubscriberBot();\n const tr = useTr();\n\n const relationshipsQ = useRelationships({ pagination: { limit: 100, offset: 0 } });\n const invoicesQ = useInvoices({ pagination: { limit: 50, offset: 0 } });\n const inboxQ = useInboxMessages({ pagination: { limit: 50, offset: 0 } });\n const renewalsQ = useRenewalEvents({ upcomingOnly: true });\n\n const relationships = useMemo(() => relationshipsQ.data?.items ?? [], [relationshipsQ.data]);\n const invoices = useMemo(() => invoicesQ.data?.items ?? [], [invoicesQ.data]);\n const messages = useMemo(() => inboxQ.data?.items ?? [], [inboxQ.data]);\n const renewals = useMemo(() => renewalsQ.data ?? [], [renewalsQ.data]);\n\n // Fast label lookup so renewal rows can name the relationship they belong to.\n const relationshipById = useMemo(() => {\n const map = new Map<string, SubRelationship>();\n relationships.forEach((r) => map.set(r.id, r));\n return map;\n }, [relationships]);\n\n const stats = useMemo(() => {\n const active = relationships.filter((r) => r.status === 'ACTIVE' || r.status === 'TRIAL');\n const monthlySpend = active.reduce(\n (sum, r) => sum + r.amount * (MONTHLY_FACTOR[r.interval] ?? 1),\n 0\n );\n const primaryCurrency = active[0]?.currency ?? relationships[0]?.currency ?? 'USD';\n // BOFF-8660 — KPI must match the Universal Inbox \"need action\" counter\n // and the on-page Needs-attention list, both of which count by\n // `actionRequired` alone (read state is irrelevant). The previous\n // `&& !m.isRead` clause made the tile show 0 the moment the user read\n // the only action-required message, contradicting the surfaces below.\n const actionRequired = messages.filter((m) => m.actionRequired).length;\n const openInvoices = invoices.filter(\n (i) => i.status === 'ISSUED' || i.status === 'OVERDUE' || i.status === 'FAILED'\n );\n const openInvoiceTotal = openInvoices.reduce((sum, i) => sum + i.total, 0);\n const atRisk = relationships.filter(\n (r) => r.status === 'PAST_DUE' || r.status === 'LAPSED'\n ).length;\n\n return {\n activeCount: active.length,\n totalCount: relationships.length,\n monthlySpend,\n primaryCurrency,\n actionRequired,\n openInvoiceCount: openInvoices.length,\n openInvoiceTotal,\n atRisk,\n };\n }, [relationships, messages, invoices]);\n\n // Portfolio-by-status → donut segments (grouped from real relationships).\n const statusSegments = useMemo<Segment[]>(() => {\n const counts = new Map<string, number>();\n relationships.forEach((r) => counts.set(r.status, (counts.get(r.status) ?? 0) + 1));\n return Array.from(counts.entries())\n .sort((a, b) => b[1] - a[1])\n .map(([status, value]) => ({ label: formatEnumLabel(status), value }));\n }, [relationships]);\n\n // Normalize upcoming renewals: prefer real renewal events, fall back to\n // relationship `renewsAt` when the event stream is empty. Sorted soonest-first.\n const upcoming = useMemo<UpcomingRenewal[]>(() => {\n const now = Date.now();\n const fromEvents: UpcomingRenewal[] = renewals.map((e: SubRenewalEvent) => {\n const rel = relationshipById.get(e.relationshipId);\n const t = new Date(e.occursAt).getTime();\n return {\n id: e.id,\n relationshipId: e.relationshipId,\n title: rel?.label ?? formatEnumLabel(e.eventType),\n when: e.occursAt,\n amount: e.amount ?? rel?.amount ?? null,\n currency: e.currency ?? rel?.currency ?? null,\n soon: !Number.isNaN(t) && t - now <= 7 * MS_PER_DAY,\n };\n });\n\n if (fromEvents.length > 0) {\n return fromEvents\n .filter((u) => !Number.isNaN(new Date(u.when).getTime()))\n .sort((a, b) => new Date(a.when).getTime() - new Date(b.when).getTime());\n }\n\n // Derive from relationships that carry a future renewal date.\n return relationships\n .filter((r) => {\n if (!r.renewsAt) return false;\n const t = new Date(r.renewsAt).getTime();\n return !Number.isNaN(t) && t >= now;\n })\n .map((r) => {\n const t = new Date(r.renewsAt as string).getTime();\n return {\n id: r.id,\n relationshipId: r.id,\n title: r.label,\n when: r.renewsAt as string,\n amount: r.amount,\n currency: r.currency,\n soon: t - now <= 7 * MS_PER_DAY,\n };\n })\n .sort((a, b) => new Date(a.when).getTime() - new Date(b.when).getTime());\n }, [renewals, relationships, relationshipById]);\n\n const timelineItems = useMemo<TimelineItem[]>(\n () =>\n upcoming.slice(0, 6).map((u) => ({\n title: u.title,\n sub: formatRelativeTime(u.when),\n tone: u.soon ? 'warning' : 'default',\n meta:\n u.amount != null\n ? formatCurrency(u.amount, u.currency ?? stats.primaryCurrency, {\n maximumFractionDigits: 0,\n })\n : undefined,\n })),\n [upcoming, stats.primaryCurrency]\n );\n\n const actionMessages = useMemo(() => messages.filter((m) => m.actionRequired), [messages]);\n\n const isLoading =\n relationshipsQ.isLoading || invoicesQ.isLoading || inboxQ.isLoading || renewalsQ.isLoading;\n const isError =\n relationshipsQ.isError && invoicesQ.isError && inboxQ.isError && renewalsQ.isError;\n\n if (isLoading) {\n return (\n <PageLayout\n title={tr('dashboard.title', 'Overview')}\n description={tr(\n 'dashboard.description',\n 'Your recurring digital relationships at a glance'\n )}\n >\n <LoadingView label={tr('dashboard.loading', 'Loading your subscription portfolio…')} />\n </PageLayout>\n );\n }\n\n if (isError) {\n return (\n <PageLayout\n title={tr('dashboard.title', 'Overview')}\n description={tr(\n 'dashboard.description',\n 'Your recurring digital relationships at a glance'\n )}\n >\n <ErrorView\n message={tr(\n 'dashboard.error',\n \"We couldn't reach the Subscriber Bot service. Please try again.\"\n )}\n onRetry={() => {\n void relationshipsQ.refetch();\n void invoicesQ.refetch();\n void inboxQ.refetch();\n void renewalsQ.refetch();\n }}\n />\n </PageLayout>\n );\n }\n\n const greetingName = currentUser?.firstName || currentUser?.name?.split(' ')[0];\n\n const quickActions = [\n {\n labelKey: 'dashboard.quickActions.addRelationship.label',\n descriptionKey: 'dashboard.quickActions.addRelationship.description',\n fallbackLabel: 'Add a relationship',\n fallbackDescription: 'Track a new subscription',\n icon: <Repeat2 className=\"h-5 w-5\" />,\n path: '/relationships/new',\n },\n {\n labelKey: 'dashboard.quickActions.discoverProviders.label',\n descriptionKey: 'dashboard.quickActions.discoverProviders.description',\n fallbackLabel: 'Discover providers',\n fallbackDescription: 'Browse the registry',\n icon: <Sparkles className=\"h-5 w-5\" />,\n path: '/providers',\n },\n {\n labelKey: 'dashboard.quickActions.openInbox.label',\n descriptionKey: 'dashboard.quickActions.openInbox.description',\n fallbackLabel: 'Open inbox',\n fallbackDescription: 'Merged communications',\n icon: <Inbox className=\"h-5 w-5\" />,\n path: '/inbox',\n },\n {\n labelKey: 'dashboard.quickActions.configureAgents.label',\n descriptionKey: 'dashboard.quickActions.configureAgents.description',\n fallbackLabel: 'Configure agents',\n fallbackDescription: 'Automate your portfolio',\n icon: <Bot className=\"h-5 w-5\" />,\n path: '/agents',\n },\n ];\n\n return (\n <PageLayout\n title={\n greetingName\n ? tr('dashboard.welcomeBack', 'Welcome back, {{name}}', { name: greetingName })\n : tr('dashboard.title', 'Overview')\n }\n description={tr(\n 'dashboard.description',\n 'Everything you pay for on repeat — in one intelligent console.'\n )}\n >\n <PagePurpose className=\"mb-6\">\n {tr(\n 'dashboard.purpose',\n 'Subscriber Bot is your system of record for every recurring digital relationship. See total recurring spend, what renews next, what needs action, and what to optimize — all from real data across your providers.'\n )}\n </PagePurpose>\n\n {/* KPI tiles */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <MetricsCard\n featured\n icon={<Wallet />}\n value={formatCurrency(stats.monthlySpend, stats.primaryCurrency, {\n maximumFractionDigits: 0,\n })}\n label={tr('dashboard.metrics.recurringSpend', 'Recurring spend / month')}\n />\n <MetricsCard\n icon={<Repeat2 />}\n value={stats.activeCount}\n label={tr('dashboard.metrics.activeRelationships', 'Active relationships')}\n />\n <MetricsCard\n icon={<CalendarClock />}\n value={upcoming.length}\n label={tr('dashboard.metrics.upcomingRenewals', 'Upcoming renewals')}\n />\n <MetricsCard\n icon={<AlertTriangle />}\n value={stats.actionRequired}\n label={tr('dashboard.metrics.needsAction', 'Need your action')}\n trend={stats.actionRequired > 0 ? 'down' : undefined}\n />\n </div>\n\n {/* Main console: renewals (left) + portfolio donut & needs-attention (right) */}\n <div className=\"mt-8 grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Upcoming renewals timeline — spans two columns on desktop */}\n <div className=\"lg:col-span-2\">\n <Section\n title={tr('dashboard.renewals.title', 'Upcoming renewals')}\n description={tr(\n 'dashboard.renewals.description',\n \"What's charging next across your portfolio\"\n )}\n className=\"mt-0\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/relationships')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-text-link hover:underline\"\n >\n {tr('common.viewAll', 'View all')} <ArrowUpRight className=\"h-3.5 w-3.5\" />\n </button>\n }\n >\n <GlassCard treatment=\"plain\" className=\"p-5\">\n {timelineItems.length === 0 ? (\n <EmptyState\n illustration=\"empty-calendar\"\n title={tr('dashboard.renewals.emptyTitle', 'No upcoming renewals')}\n description={tr(\n 'dashboard.renewals.emptyDescription',\n 'Renewals you track will appear here ahead of time.'\n )}\n />\n ) : (\n <Timeline items={timelineItems} />\n )}\n </GlassCard>\n </Section>\n </div>\n\n {/* Right rail: portfolio-by-status donut + needs-attention panel */}\n <div className=\"flex flex-col gap-6\">\n <Section\n title={tr('dashboard.portfolio.title', 'Portfolio by status')}\n description={tr('dashboard.portfolio.description', 'Where your relationships stand')}\n className=\"mt-0\"\n >\n <GlassCard treatment=\"glass\" className=\"p-5\">\n {statusSegments.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title={tr('dashboard.portfolio.emptyTitle', 'No relationships yet')}\n description={tr(\n 'dashboard.portfolio.emptyDescription',\n 'Add your first subscription to see the breakdown.'\n )}\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/relationships/new')}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n {tr('dashboard.portfolio.addRelationship', 'Add relationship')}\n </button>\n }\n />\n ) : (\n <div className=\"flex flex-col items-center gap-5 sm:flex-row sm:items-center lg:flex-col xl:flex-row\">\n <Donut\n data={statusSegments}\n size={148}\n thickness={20}\n centerLabel={String(stats.totalCount)}\n centerSub={tr(\n stats.totalCount === 1\n ? 'dashboard.portfolio.relationshipSingular'\n : 'dashboard.portfolio.relationshipPlural',\n stats.totalCount === 1 ? 'relationship' : 'relationships'\n )}\n className=\"shrink-0\"\n />\n <Legend data={statusSegments} className=\"w-full min-w-0 flex-1\" />\n </div>\n )}\n </GlassCard>\n </Section>\n\n <Section\n title={tr('dashboard.needsAttention.title', 'Needs attention')}\n description={tr(\n 'dashboard.needsAttention.description',\n 'Required actions from the Universal Inbox'\n )}\n className=\"mt-0\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/inbox')}\n className=\"text-sm font-medium text-text-link hover:underline\"\n >\n {tr('dashboard.needsAttention.openInbox', 'Open inbox')}\n </button>\n }\n >\n <GlassCard treatment=\"plain\" className=\"p-3\">\n {actionMessages.length === 0 ? (\n <div className=\"flex items-center gap-3 px-2 py-4\">\n <span className=\"flex size-10 shrink-0 items-center justify-center rounded-lg bg-status-success-bg-subtle text-status-success-text\">\n <ShieldCheck className=\"h-5 w-5\" />\n </span>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('dashboard.needsAttention.allCaughtUp', \"You're all caught up\")}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {tr(\n 'dashboard.needsAttention.allCaughtUpDescription',\n 'Required actions across your channels will surface here.'\n )}\n </p>\n </div>\n </div>\n ) : (\n <ul className=\"space-y-1\">\n {actionMessages.slice(0, 5).map((message) => (\n <li key={message.id}>\n <button\n type=\"button\"\n onClick={() => navigateTo('/inbox')}\n className=\"flex w-full items-center gap-3 rounded-lg border-l-2 border-status-warning-text/60 bg-status-warning-bg-subtle/50 p-3 text-left transition-colors hover:bg-status-warning-bg-subtle\"\n >\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {message.subject ||\n tr('dashboard.needsAttention.noSubject', '(no subject)')}\n </p>\n {message.preview && (\n <p className=\"truncate text-xs text-text-secondary\">\n {message.preview}\n </p>\n )}\n </div>\n <StatusBadge status={message.category} className=\"shrink-0\" />\n </button>\n </li>\n ))}\n </ul>\n )}\n </GlassCard>\n </Section>\n </div>\n </div>\n\n {/* Open-invoice call-out (only when there is real outstanding balance) */}\n {stats.openInvoiceCount > 0 && (\n <button\n type=\"button\"\n onClick={() => navigateTo('/invoices')}\n className=\"mt-6 flex w-full items-center justify-between gap-3 rounded-xl border border-status-warning-text/30 bg-status-warning-bg-subtle px-5 py-4 text-left transition-shadow hover:shadow-md\"\n >\n <div className=\"flex items-center gap-3\">\n <AlertTriangle className=\"h-5 w-5 shrink-0 text-status-warning-text\" />\n <span className=\"text-sm font-medium text-status-warning-text\">\n {tr(\n 'dashboard.openInvoices.label',\n '{{count}} open invoice · {{amount}} outstanding',\n {\n count: stats.openInvoiceCount,\n amount: formatCurrency(stats.openInvoiceTotal, stats.primaryCurrency),\n }\n )}\n </span>\n </div>\n <ArrowRight className=\"h-4 w-4 shrink-0 text-status-warning-text\" />\n </button>\n )}\n\n {/* Quick actions */}\n <Section\n title={tr('dashboard.quickActions.title', 'Quick actions')}\n description={tr('dashboard.quickActions.description', 'Jump straight into your workflow')}\n >\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4\">\n {quickActions.map((qa) => (\n <button\n key={qa.path}\n type=\"button\"\n onClick={() => navigateTo(qa.path)}\n className=\"group flex items-center gap-3 rounded-xl border border-border-subtle bg-bg-surface p-4 text-left transition-all hover:-translate-y-0.5 hover:border-border-seam hover:shadow-md\"\n >\n <span className=\"flex size-10 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n {qa.icon}\n </span>\n <span className=\"min-w-0 flex-1\">\n <span className=\"block text-sm font-medium text-text-primary\">\n {tr(qa.labelKey, qa.fallbackLabel)}\n </span>\n <span className=\"block text-xs text-text-secondary\">\n {tr(qa.descriptionKey, qa.fallbackDescription)}\n </span>\n </span>\n <ArrowRight className=\"h-4 w-4 shrink-0 text-text-secondary transition-transform group-hover:translate-x-0.5\" />\n </button>\n ))}\n </div>\n </Section>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"InboxPage.d.ts","sourceRoot":"","sources":["../../../src/subscriberbot/pages/InboxPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAy1BH,wBAAgB,SAAS,gCAgOxB"}
1
+ {"version":3,"file":"InboxPage.d.ts","sourceRoot":"","sources":["../../../src/subscriberbot/pages/InboxPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA01BH,wBAAgB,SAAS,gCAkOxB"}