@burdenoff/microfe-subscriberbot 2026.626.1 → 2026.628.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 (141) hide show
  1. package/dist/index.js +4 -4
  2. package/dist/subscriberbot/SubscriberBotRoot.js +61 -133
  3. package/dist/subscriberbot/SubscriberBotRoot.js.map +1 -1
  4. package/dist/subscriberbot/SubscriberBotRoutes.js +56 -187
  5. package/dist/subscriberbot/SubscriberBotRoutes.js.map +1 -1
  6. package/dist/subscriberbot/components/StateViews.js +49 -0
  7. package/dist/subscriberbot/components/StateViews.js.map +1 -0
  8. package/dist/subscriberbot/components/StatusBadge.js +45 -47
  9. package/dist/subscriberbot/components/StatusBadge.js.map +1 -1
  10. package/dist/subscriberbot/context/SubscriberBotContext.js +22 -30
  11. package/dist/subscriberbot/context/SubscriberBotContext.js.map +1 -1
  12. package/dist/subscriberbot/hooks/useSubscriberBotApi.js +342 -195
  13. package/dist/subscriberbot/hooks/useSubscriberBotApi.js.map +1 -1
  14. package/dist/subscriberbot/index.js +1 -1
  15. package/dist/subscriberbot/pages/DashboardPage.js +269 -0
  16. package/dist/subscriberbot/pages/DashboardPage.js.map +1 -0
  17. package/dist/subscriberbot/pages/InboxPage.js +186 -0
  18. package/dist/subscriberbot/pages/InboxPage.js.map +1 -0
  19. package/dist/subscriberbot/pages/InvoicesPage.js +211 -0
  20. package/dist/subscriberbot/pages/InvoicesPage.js.map +1 -0
  21. package/dist/subscriberbot/pages/WorkflowsPage.js +236 -0
  22. package/dist/subscriberbot/pages/WorkflowsPage.js.map +1 -0
  23. package/dist/subscriberbot/pages/agents/AgentDetailPage.js +160 -0
  24. package/dist/subscriberbot/pages/agents/AgentDetailPage.js.map +1 -0
  25. package/dist/subscriberbot/pages/agents/AgentNewPage.js +140 -0
  26. package/dist/subscriberbot/pages/agents/AgentNewPage.js.map +1 -0
  27. package/dist/subscriberbot/pages/agents/AgentsListPage.js +178 -0
  28. package/dist/subscriberbot/pages/agents/AgentsListPage.js.map +1 -0
  29. package/dist/subscriberbot/pages/providers/ProviderDetailPage.js +179 -0
  30. package/dist/subscriberbot/pages/providers/ProviderDetailPage.js.map +1 -0
  31. package/dist/subscriberbot/pages/providers/ProviderNewPage.js +185 -0
  32. package/dist/subscriberbot/pages/providers/ProviderNewPage.js.map +1 -0
  33. package/dist/subscriberbot/pages/providers/ProvidersListPage.js +254 -0
  34. package/dist/subscriberbot/pages/providers/ProvidersListPage.js.map +1 -0
  35. package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js +306 -0
  36. package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js.map +1 -0
  37. package/dist/subscriberbot/pages/relationships/RelationshipNewPage.js +253 -0
  38. package/dist/subscriberbot/pages/relationships/RelationshipNewPage.js.map +1 -0
  39. package/dist/subscriberbot/pages/relationships/RelationshipsListPage.js +248 -0
  40. package/dist/subscriberbot/pages/relationships/RelationshipsListPage.js.map +1 -0
  41. package/dist/subscriberbot/utils/formatters.js +50 -32
  42. package/dist/subscriberbot/utils/formatters.js.map +1 -1
  43. package/package.json +1 -1
  44. package/dist/subscriberbot/components/InvestmentCard.js +0 -97
  45. package/dist/subscriberbot/components/InvestmentCard.js.map +0 -1
  46. package/dist/subscriberbot/components/RatingDisplay.js +0 -35
  47. package/dist/subscriberbot/components/RatingDisplay.js.map +0 -1
  48. package/dist/subscriberbot/mocks/data.js +0 -1517
  49. package/dist/subscriberbot/mocks/data.js.map +0 -1
  50. package/dist/subscriberbot/mocks/users.js +0 -34
  51. package/dist/subscriberbot/mocks/users.js.map +0 -1
  52. package/dist/subscriberbot/pages/HomePage.js +0 -930
  53. package/dist/subscriberbot/pages/HomePage.js.map +0 -1
  54. package/dist/subscriberbot/pages/admin/AdminAnalyticsPage.js +0 -651
  55. package/dist/subscriberbot/pages/admin/AdminAnalyticsPage.js.map +0 -1
  56. package/dist/subscriberbot/pages/admin/AdminDashboard.js +0 -679
  57. package/dist/subscriberbot/pages/admin/AdminDashboard.js.map +0 -1
  58. package/dist/subscriberbot/pages/admin/AdminFinancialPage.js +0 -427
  59. package/dist/subscriberbot/pages/admin/AdminFinancialPage.js.map +0 -1
  60. package/dist/subscriberbot/pages/admin/AdminOrdersPage.js +0 -665
  61. package/dist/subscriberbot/pages/admin/AdminOrdersPage.js.map +0 -1
  62. package/dist/subscriberbot/pages/admin/AdminProfilePage.js +0 -261
  63. package/dist/subscriberbot/pages/admin/AdminProfilePage.js.map +0 -1
  64. package/dist/subscriberbot/pages/admin/AdminSettingsPage.js +0 -654
  65. package/dist/subscriberbot/pages/admin/AdminSettingsPage.js.map +0 -1
  66. package/dist/subscriberbot/pages/admin/CartManagementPage.js +0 -874
  67. package/dist/subscriberbot/pages/admin/CartManagementPage.js.map +0 -1
  68. package/dist/subscriberbot/pages/admin/UserManagementPage.js +0 -898
  69. package/dist/subscriberbot/pages/admin/UserManagementPage.js.map +0 -1
  70. package/dist/subscriberbot/pages/cart-owner/AnalyticsDashboard.js +0 -839
  71. package/dist/subscriberbot/pages/cart-owner/AnalyticsDashboard.js.map +0 -1
  72. package/dist/subscriberbot/pages/cart-owner/BusinessSettingsPage.js +0 -705
  73. package/dist/subscriberbot/pages/cart-owner/BusinessSettingsPage.js.map +0 -1
  74. package/dist/subscriberbot/pages/cart-owner/CartOwnerDashboard.js +0 -802
  75. package/dist/subscriberbot/pages/cart-owner/CartOwnerDashboard.js.map +0 -1
  76. package/dist/subscriberbot/pages/cart-owner/CartOwnerProfilePage.js +0 -891
  77. package/dist/subscriberbot/pages/cart-owner/CartOwnerProfilePage.js.map +0 -1
  78. package/dist/subscriberbot/pages/cart-owner/CartServiceConfigPage.js +0 -633
  79. package/dist/subscriberbot/pages/cart-owner/CartServiceConfigPage.js.map +0 -1
  80. package/dist/subscriberbot/pages/cart-owner/InventoryManagementPage.js +0 -910
  81. package/dist/subscriberbot/pages/cart-owner/InventoryManagementPage.js.map +0 -1
  82. package/dist/subscriberbot/pages/cart-owner/OrderManagementPage.js +0 -879
  83. package/dist/subscriberbot/pages/cart-owner/OrderManagementPage.js.map +0 -1
  84. package/dist/subscriberbot/pages/cart-owner/StaffManagementPage.js +0 -838
  85. package/dist/subscriberbot/pages/cart-owner/StaffManagementPage.js.map +0 -1
  86. package/dist/subscriberbot/pages/common/HelpSupportPage.js +0 -1323
  87. package/dist/subscriberbot/pages/common/HelpSupportPage.js.map +0 -1
  88. package/dist/subscriberbot/pages/common/NotificationCenterPage.js +0 -672
  89. package/dist/subscriberbot/pages/common/NotificationCenterPage.js.map +0 -1
  90. package/dist/subscriberbot/pages/common/SettingsPage.js +0 -973
  91. package/dist/subscriberbot/pages/common/SettingsPage.js.map +0 -1
  92. package/dist/subscriberbot/pages/customer/BookingDetailsPage.js +0 -626
  93. package/dist/subscriberbot/pages/customer/BookingDetailsPage.js.map +0 -1
  94. package/dist/subscriberbot/pages/customer/CartDetailPage.js +0 -654
  95. package/dist/subscriberbot/pages/customer/CartDetailPage.js.map +0 -1
  96. package/dist/subscriberbot/pages/customer/CartDiscoveryPage.js +0 -555
  97. package/dist/subscriberbot/pages/customer/CartDiscoveryPage.js.map +0 -1
  98. package/dist/subscriberbot/pages/customer/CheckoutPage.js +0 -529
  99. package/dist/subscriberbot/pages/customer/CheckoutPage.js.map +0 -1
  100. package/dist/subscriberbot/pages/customer/CustomerDashboard.js +0 -532
  101. package/dist/subscriberbot/pages/customer/CustomerDashboard.js.map +0 -1
  102. package/dist/subscriberbot/pages/customer/CustomerProfilePage.js +0 -630
  103. package/dist/subscriberbot/pages/customer/CustomerProfilePage.js.map +0 -1
  104. package/dist/subscriberbot/pages/customer/DirectServiceBookingPage.js +0 -633
  105. package/dist/subscriberbot/pages/customer/DirectServiceBookingPage.js.map +0 -1
  106. package/dist/subscriberbot/pages/customer/FavoritesPage.js +0 -484
  107. package/dist/subscriberbot/pages/customer/FavoritesPage.js.map +0 -1
  108. package/dist/subscriberbot/pages/customer/OrderHistoryPage.js +0 -446
  109. package/dist/subscriberbot/pages/customer/OrderHistoryPage.js.map +0 -1
  110. package/dist/subscriberbot/pages/customer/OrderTrackingPage.js +0 -394
  111. package/dist/subscriberbot/pages/customer/OrderTrackingPage.js.map +0 -1
  112. package/dist/subscriberbot/pages/customer/PaymentMethodsPage.js +0 -495
  113. package/dist/subscriberbot/pages/customer/PaymentMethodsPage.js.map +0 -1
  114. package/dist/subscriberbot/pages/customer/ReviewsPage.js +0 -613
  115. package/dist/subscriberbot/pages/customer/ReviewsPage.js.map +0 -1
  116. package/dist/subscriberbot/pages/customer/SavedAddressesPage.js +0 -450
  117. package/dist/subscriberbot/pages/customer/SavedAddressesPage.js.map +0 -1
  118. package/dist/subscriberbot/pages/investor/InvestmentDetailsPage.js +0 -886
  119. package/dist/subscriberbot/pages/investor/InvestmentDetailsPage.js.map +0 -1
  120. package/dist/subscriberbot/pages/investor/InvestmentOpportunitiesPage.js +0 -947
  121. package/dist/subscriberbot/pages/investor/InvestmentOpportunitiesPage.js.map +0 -1
  122. package/dist/subscriberbot/pages/investor/InvestorAnalyticsPage.js +0 -687
  123. package/dist/subscriberbot/pages/investor/InvestorAnalyticsPage.js.map +0 -1
  124. package/dist/subscriberbot/pages/investor/InvestorDashboard.js +0 -451
  125. package/dist/subscriberbot/pages/investor/InvestorDashboard.js.map +0 -1
  126. package/dist/subscriberbot/pages/investor/InvestorInvestmentsPage.js +0 -406
  127. package/dist/subscriberbot/pages/investor/InvestorInvestmentsPage.js.map +0 -1
  128. package/dist/subscriberbot/pages/investor/InvestorPortfolioPage.js +0 -575
  129. package/dist/subscriberbot/pages/investor/InvestorPortfolioPage.js.map +0 -1
  130. package/dist/subscriberbot/pages/investor/InvestorProfilePage.js +0 -1070
  131. package/dist/subscriberbot/pages/investor/InvestorProfilePage.js.map +0 -1
  132. package/dist/subscriberbot/pages/worker/TaskDetailsPage.js +0 -535
  133. package/dist/subscriberbot/pages/worker/TaskDetailsPage.js.map +0 -1
  134. package/dist/subscriberbot/pages/worker/WorkerDashboard.js +0 -666
  135. package/dist/subscriberbot/pages/worker/WorkerDashboard.js.map +0 -1
  136. package/dist/subscriberbot/pages/worker/WorkerOrdersPage.js +0 -681
  137. package/dist/subscriberbot/pages/worker/WorkerOrdersPage.js.map +0 -1
  138. package/dist/subscriberbot/pages/worker/WorkerProfilePage.js +0 -692
  139. package/dist/subscriberbot/pages/worker/WorkerProfilePage.js.map +0 -1
  140. package/dist/subscriberbot/pages/worker/WorkerSchedulePage.js +0 -588
  141. package/dist/subscriberbot/pages/worker/WorkerSchedulePage.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProvidersListPage.js","names":[],"sources":["../../../../src/subscriberbot/pages/providers/ProvidersListPage.tsx"],"sourcesContent":["/**\n * Providers list (Global Provider Registry)\n * @module subscriberbot/pages/providers\n *\n * Discovery: search and browse the provider registry. Server-side search +\n * category/status filters + pagination via `subListProviders`\n * (a SubProviderConnection).\n */\n\nimport { useState } from 'react';\nimport { Plus, Search, Building2, ShieldCheck } from 'lucide-react';\nimport { EmphasisPanel, 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 { useProviders } from '../../hooks/useSubscriberBotApi';\nimport { cn } from '../../utils/cn';\nimport { formatEnumLabel } from '../../utils/formatters';\nimport type { SubProviderCategory, SubProviderStatus } from '../../types';\n\nconst PAGE_SIZE = 24;\n\nconst CATEGORY_OPTIONS: Array<{ value: '' | SubProviderCategory; label: string }> = [\n { value: '', label: 'All categories' },\n { value: 'SAAS', label: 'SaaS' },\n { value: 'AI_SERVICE', label: 'AI service' },\n { value: 'STREAMING', label: 'Streaming' },\n { value: 'NEWSLETTER', label: 'Newsletter' },\n { value: 'NEWS_DIGEST', label: 'News digest' },\n { value: 'MEMBERSHIP', label: 'Membership' },\n { value: 'COMMUNITY', label: 'Community' },\n { value: 'EDUCATION', label: 'Education' },\n { value: 'FINANCIAL', label: 'Financial' },\n { value: 'INSURANCE', label: 'Insurance' },\n { value: 'TELECOM', label: 'Telecom' },\n { value: 'UTILITY', label: 'Utility' },\n { value: 'ASSOCIATION', label: 'Association' },\n { value: 'LICENSE', label: 'License' },\n { value: 'CONTRACT', label: 'Contract' },\n { value: 'OTHER', label: 'Other' },\n];\n\nconst STATUS_OPTIONS: Array<{ value: '' | SubProviderStatus; label: string }> = [\n { value: '', label: 'Any status' },\n { value: 'VERIFIED', label: 'Verified' },\n { value: 'CLAIMED', label: 'Claimed' },\n { value: 'UNVERIFIED', label: 'Unverified' },\n { value: 'DEPRECATED', label: 'Deprecated' },\n];\n\nexport function ProvidersListPage() {\n const { navigateTo } = useSubscriberBot();\n const [page, setPage] = useState(0);\n const [search, setSearch] = useState('');\n const [searchInput, setSearchInput] = useState('');\n const [category, setCategory] = useState<'' | SubProviderCategory>('');\n const [status, setStatus] = useState<'' | SubProviderStatus>('');\n\n const { data, isLoading, isError, refetch, isFetching } = useProviders({\n search: search || undefined,\n category: category || undefined,\n status: status || undefined,\n pagination: { limit: PAGE_SIZE, offset: page * PAGE_SIZE },\n });\n\n const items = data?.items ?? [];\n const hasMore = data?.pageInfo.hasMore ?? false;\n\n const submitSearch = (e: React.FormEvent) => {\n e.preventDefault();\n setSearch(searchInput.trim());\n setPage(0);\n };\n\n return (\n <PageLayout\n title=\"Providers\"\n description=\"Discover and browse the global provider registry\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/providers/new')}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n <Plus className=\"h-4 w-4\" /> Add provider\n </button>\n }\n >\n <PagePurpose className=\"mb-4\">\n The registry is the public face of the Subscription Knowledge Graph — every provider exists\n here whether or not it integrates. Search and filter to find the ones you have relationships\n with, or add a new one.\n </PagePurpose>\n\n <EmphasisPanel>\n <form onSubmit={submitSearch} className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <input\n type=\"text\"\n placeholder=\"Search providers by name…\"\n value={searchInput}\n onChange={(e) => setSearchInput(e.target.value)}\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface py-2.5 pl-10 pr-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </form>\n </EmphasisPanel>\n\n <div className=\"mt-4 flex flex-wrap items-center gap-3\">\n <select\n value={category}\n onChange={(e) => {\n setCategory(e.target.value as '' | SubProviderCategory);\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 <select\n value={status}\n onChange={(e) => {\n setStatus(e.target.value as '' | SubProviderStatus);\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 {STATUS_OPTIONS.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n </div>\n\n {isLoading ? (\n <LoadingView label=\"Loading providers…\" />\n ) : isError ? (\n <ErrorView message=\"Could not load the provider registry.\" onRetry={() => void refetch()} />\n ) : items.length === 0 ? (\n <div className=\"mt-6\">\n <EmptyState\n illustration=\"empty-search\"\n icon={<Building2 className=\"h-12 w-12\" />}\n title=\"No providers found\"\n description={\n search\n ? `No providers match \"${search}\".`\n : 'No providers in the registry with these filters yet.'\n }\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/providers/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 Add provider\n </button>\n }\n />\n </div>\n ) : (\n <>\n <div className=\"mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {items.map((p) => (\n <button\n key={p.id}\n type=\"button\"\n onClick={() => navigateTo(`/providers/${p.id}`)}\n className=\"flex flex-col rounded-lg border border-border-subtle bg-bg-surface p-4 text-left transition-shadow hover:shadow-md\"\n >\n <div className=\"flex items-start justify-between gap-2\">\n <span className=\"flex size-9 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <Building2 className=\"h-4 w-4\" />\n </span>\n <StatusBadge status={p.status} />\n </div>\n <p className=\"mt-3 truncate text-sm font-semibold text-text-primary\">{p.name}</p>\n <p className=\"text-xs text-text-secondary\">{formatEnumLabel(p.category)}</p>\n {p.description && (\n <p className=\"mt-2 line-clamp-2 text-xs text-text-secondary\">{p.description}</p>\n )}\n <div className=\"mt-3 flex flex-wrap items-center gap-2 text-xs text-text-secondary\">\n {p.region && <span>{p.region}</span>}\n {p.vendorHealthScore != null && (\n <span className=\"inline-flex items-center gap-1\">\n <ShieldCheck className=\"h-3 w-3\" />\n {p.vendorHealthScore}/100\n </span>\n )}\n </div>\n </button>\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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"}
@@ -0,0 +1,306 @@
1
+ import { useSubscriberBot as e } from "../../context/SubscriberBotContext.js";
2
+ import { PageLayout as t } from "../../components/PageLayout.js";
3
+ import { formatCurrency as n, formatDate as r, formatEnumLabel as i, formatIntervalSuffix as a } from "../../utils/formatters.js";
4
+ import { StatusBadge as o } from "../../components/StatusBadge.js";
5
+ import { EmptyState as s } from "../../components/EmptyState.js";
6
+ import { ErrorView as c, LoadingView as l, Section as u } from "../../components/StateViews.js";
7
+ import { useCancelRelationship as d, useRelationship as f, useTransitionRelationship as p } from "../../hooks/useSubscriberBotApi.js";
8
+ import { useState as m } from "react";
9
+ import { Activity as h, ArrowLeft as g, Building2 as _, CalendarClock as v, CreditCard as y, Paperclip as b, ShieldCheck as x, Tag as S } from "lucide-react";
10
+ import { useParams as C } from "react-router-dom";
11
+ import { jsx as w, jsxs as T } from "react/jsx-runtime";
12
+ import { GlassCard as E } from "@burdenoff/fe-libs/ui";
13
+ //#region src/subscriberbot/pages/relationships/RelationshipDetailPage.tsx
14
+ var D = [
15
+ {
16
+ status: "ACTIVE",
17
+ label: "Activate"
18
+ },
19
+ {
20
+ status: "PAUSED",
21
+ label: "Pause"
22
+ },
23
+ {
24
+ status: "EXPIRED",
25
+ label: "Mark expired"
26
+ }
27
+ ];
28
+ function O() {
29
+ let { id: O } = C(), { navigateTo: k } = e(), { data: A, isLoading: j, isError: M, refetch: N } = f(O), P = p(), F = d(), [I, L] = m(null);
30
+ if (j) return /* @__PURE__ */ w(t, {
31
+ title: "Relationship",
32
+ children: /* @__PURE__ */ w(l, { label: "Loading relationship…" })
33
+ });
34
+ if (M) return /* @__PURE__ */ w(t, {
35
+ title: "Relationship",
36
+ children: /* @__PURE__ */ w(c, {
37
+ message: "Could not load this relationship.",
38
+ onRetry: () => void N()
39
+ })
40
+ });
41
+ if (!A) return /* @__PURE__ */ w(t, {
42
+ title: "Relationship",
43
+ children: /* @__PURE__ */ w(s, {
44
+ illustration: "empty-data",
45
+ title: "Relationship not found",
46
+ description: "It may have been removed, or you may not have access to it.",
47
+ action: /* @__PURE__ */ w("button", {
48
+ type: "button",
49
+ onClick: () => k("/relationships"),
50
+ className: "rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90",
51
+ children: "Back to relationships"
52
+ })
53
+ })
54
+ });
55
+ let R = A, z = P.isPending || F.isPending, B = (e) => {
56
+ L(null), P.mutate({
57
+ id: R.id,
58
+ status: e
59
+ }, { onError: (e) => L(e instanceof Error ? e.message : "Transition failed") });
60
+ };
61
+ return /* @__PURE__ */ T(t, {
62
+ title: R.label,
63
+ description: R.provider?.name ?? i(R.kind),
64
+ actions: /* @__PURE__ */ T("button", {
65
+ type: "button",
66
+ onClick: () => k("/relationships"),
67
+ className: "inline-flex items-center gap-2 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken",
68
+ children: [/* @__PURE__ */ w(g, { className: "h-4 w-4" }), " Back"]
69
+ }),
70
+ children: [
71
+ /* @__PURE__ */ T("div", {
72
+ className: "flex flex-wrap items-center gap-3",
73
+ children: [
74
+ /* @__PURE__ */ w(o, { status: R.status }),
75
+ /* @__PURE__ */ w("span", {
76
+ className: "text-sm text-text-secondary",
77
+ children: i(R.kind)
78
+ }),
79
+ /* @__PURE__ */ w("span", {
80
+ className: "text-sm text-text-secondary",
81
+ children: "·"
82
+ }),
83
+ /* @__PURE__ */ T("span", {
84
+ className: "text-sm font-semibold text-text-primary",
85
+ children: [n(R.amount, R.currency), /* @__PURE__ */ w("span", {
86
+ className: "text-xs font-normal text-text-secondary",
87
+ children: a(R.interval)
88
+ })]
89
+ }),
90
+ R.autoRenew && /* @__PURE__ */ w("span", {
91
+ className: "text-xs text-text-secondary",
92
+ children: "Auto-renews"
93
+ })
94
+ ]
95
+ }),
96
+ I && /* @__PURE__ */ w("div", {
97
+ className: "mt-4 rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text",
98
+ children: I
99
+ }),
100
+ /* @__PURE__ */ T("div", {
101
+ className: "mt-5 flex flex-wrap gap-2",
102
+ children: [D.filter((e) => e.status !== R.status).map((e) => /* @__PURE__ */ w("button", {
103
+ type: "button",
104
+ disabled: z,
105
+ onClick: () => B(e.status),
106
+ 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",
107
+ children: e.label
108
+ }, e.status)), R.status !== "CANCELLED" && /* @__PURE__ */ w("button", {
109
+ type: "button",
110
+ disabled: z,
111
+ onClick: () => {
112
+ L(null), F.mutate(R.id, { onError: (e) => L(e instanceof Error ? e.message : "Cancel failed") });
113
+ },
114
+ className: "rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-3 py-1.5 text-xs font-medium text-status-error-text hover:opacity-90 disabled:opacity-50",
115
+ children: "Cancel relationship"
116
+ })]
117
+ }),
118
+ /* @__PURE__ */ T("div", {
119
+ className: "mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
120
+ children: [
121
+ /* @__PURE__ */ T(E, {
122
+ treatment: "metric",
123
+ className: "p-4",
124
+ children: [/* @__PURE__ */ T("div", {
125
+ className: "flex items-center gap-2 text-xs font-medium text-text-secondary",
126
+ children: [/* @__PURE__ */ w(_, { className: "h-3.5 w-3.5" }), " Provider"]
127
+ }), /* @__PURE__ */ w("p", {
128
+ className: "mt-1.5 text-sm text-text-primary",
129
+ children: R.provider?.name ?? "—"
130
+ })]
131
+ }),
132
+ /* @__PURE__ */ T(E, {
133
+ treatment: "metric",
134
+ className: "p-4",
135
+ children: [/* @__PURE__ */ T("div", {
136
+ className: "flex items-center gap-2 text-xs font-medium text-text-secondary",
137
+ children: [/* @__PURE__ */ w(y, { className: "h-3.5 w-3.5" }), " Plan"]
138
+ }), /* @__PURE__ */ w("p", {
139
+ className: "mt-1.5 text-sm text-text-primary",
140
+ children: R.plan?.name ?? "—"
141
+ })]
142
+ }),
143
+ /* @__PURE__ */ T(E, {
144
+ treatment: "metric",
145
+ className: "p-4",
146
+ children: [/* @__PURE__ */ T("div", {
147
+ className: "flex items-center gap-2 text-xs font-medium text-text-secondary",
148
+ children: [/* @__PURE__ */ w(v, { className: "h-3.5 w-3.5" }), " Next renewal"]
149
+ }), /* @__PURE__ */ w("p", {
150
+ className: "mt-1.5 text-sm text-text-primary",
151
+ children: R.renewsAt ? r(R.renewsAt) : "—"
152
+ })]
153
+ }),
154
+ /* @__PURE__ */ T(E, {
155
+ treatment: "metric",
156
+ className: "p-4",
157
+ children: [/* @__PURE__ */ T("div", {
158
+ className: "flex items-center gap-2 text-xs font-medium text-text-secondary",
159
+ children: [/* @__PURE__ */ w(h, { className: "h-3.5 w-3.5" }), " Health score"]
160
+ }), /* @__PURE__ */ w("p", {
161
+ className: "mt-1.5 text-sm text-text-primary",
162
+ children: R.healthScore == null ? "—" : `${R.healthScore}/100`
163
+ })]
164
+ }),
165
+ /* @__PURE__ */ T(E, {
166
+ treatment: "metric",
167
+ className: "p-4",
168
+ children: [/* @__PURE__ */ T("div", {
169
+ className: "flex items-center gap-2 text-xs font-medium text-text-secondary",
170
+ children: [/* @__PURE__ */ w(x, { className: "h-3.5 w-3.5" }), " Discovery"]
171
+ }), /* @__PURE__ */ w("p", {
172
+ className: "mt-1.5 text-sm text-text-primary",
173
+ children: i(R.discoverySource)
174
+ })]
175
+ }),
176
+ /* @__PURE__ */ T(E, {
177
+ treatment: "metric",
178
+ className: "p-4",
179
+ children: [/* @__PURE__ */ T("div", {
180
+ className: "flex items-center gap-2 text-xs font-medium text-text-secondary",
181
+ children: [/* @__PURE__ */ w(v, { className: "h-3.5 w-3.5" }), " Started"]
182
+ }), /* @__PURE__ */ w("p", {
183
+ className: "mt-1.5 text-sm text-text-primary",
184
+ children: R.startedAt ? r(R.startedAt) : "—"
185
+ })]
186
+ })
187
+ ]
188
+ }),
189
+ R.tags.length > 0 && /* @__PURE__ */ T("div", {
190
+ className: "mt-5 flex flex-wrap items-center gap-2",
191
+ children: [/* @__PURE__ */ w(S, { className: "h-3.5 w-3.5 text-text-secondary" }), R.tags.map((e) => /* @__PURE__ */ w("span", {
192
+ className: "rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs text-text-secondary",
193
+ children: e
194
+ }, e))]
195
+ }),
196
+ R.notes && /* @__PURE__ */ T("div", {
197
+ className: "mt-5 rounded-lg border border-border-subtle bg-bg-surface p-4",
198
+ children: [/* @__PURE__ */ w("p", {
199
+ className: "text-xs font-medium text-text-secondary",
200
+ children: "Notes"
201
+ }), /* @__PURE__ */ w("p", {
202
+ className: "mt-1 whitespace-pre-wrap text-sm text-text-primary",
203
+ children: R.notes
204
+ })]
205
+ }),
206
+ /* @__PURE__ */ w(u, {
207
+ title: "Entitlements",
208
+ description: "What this relationship grants you",
209
+ children: !R.entitlements || R.entitlements.length === 0 ? /* @__PURE__ */ w(s, {
210
+ illustration: "empty-data",
211
+ title: "No entitlements",
212
+ description: "Entitlements (seats, quotas, feature access) will appear here."
213
+ }) : /* @__PURE__ */ w("div", {
214
+ className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3",
215
+ children: R.entitlements.map((e) => /* @__PURE__ */ T("div", {
216
+ className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
217
+ children: [
218
+ /* @__PURE__ */ T("div", {
219
+ className: "flex items-start justify-between gap-2",
220
+ children: [/* @__PURE__ */ w("p", {
221
+ className: "text-sm font-medium text-text-primary",
222
+ children: e.name
223
+ }), /* @__PURE__ */ w(o, { status: e.isActive ? "ACTIVE" : "DISABLED" })]
224
+ }),
225
+ /* @__PURE__ */ w("p", {
226
+ className: "mt-1 text-xs text-text-secondary",
227
+ children: i(e.type)
228
+ }),
229
+ e.quota != null && /* @__PURE__ */ T("p", {
230
+ className: "mt-2 text-xs text-text-secondary",
231
+ children: [
232
+ "Used ",
233
+ e.used,
234
+ " of ",
235
+ e.quota,
236
+ e.unit ? ` ${e.unit}` : ""
237
+ ]
238
+ })
239
+ ]
240
+ }, e.id))
241
+ })
242
+ }),
243
+ /* @__PURE__ */ w(u, {
244
+ title: "Assets",
245
+ description: "Invoices, contracts, licenses and credentials",
246
+ children: !R.assets || R.assets.length === 0 ? /* @__PURE__ */ w(s, {
247
+ illustration: "empty-data",
248
+ title: "No assets attached",
249
+ description: "Attach invoices, contracts, licenses or receipts to this relationship."
250
+ }) : /* @__PURE__ */ w("div", {
251
+ className: "divide-y divide-border-subtle rounded-lg border border-border-subtle bg-bg-surface",
252
+ children: R.assets.map((e) => /* @__PURE__ */ T("div", {
253
+ className: "flex items-center justify-between gap-3 p-3",
254
+ children: [/* @__PURE__ */ T("div", {
255
+ className: "flex min-w-0 items-center gap-2",
256
+ children: [/* @__PURE__ */ w(b, { className: "h-4 w-4 shrink-0 text-text-secondary" }), /* @__PURE__ */ T("div", {
257
+ className: "min-w-0",
258
+ children: [/* @__PURE__ */ w("p", {
259
+ className: "truncate text-sm font-medium text-text-primary",
260
+ children: e.name
261
+ }), /* @__PURE__ */ w("p", {
262
+ className: "text-xs text-text-secondary",
263
+ children: i(e.type)
264
+ })]
265
+ })]
266
+ }), e.expiresAt && /* @__PURE__ */ T("span", {
267
+ className: "shrink-0 text-xs text-text-secondary",
268
+ children: ["Expires ", r(e.expiresAt)]
269
+ })]
270
+ }, e.id))
271
+ })
272
+ }),
273
+ /* @__PURE__ */ w(u, {
274
+ title: "Renewal timeline",
275
+ description: "Detected renewals and price changes",
276
+ children: !R.renewalEvents || R.renewalEvents.length === 0 ? /* @__PURE__ */ w(s, {
277
+ illustration: "empty-calendar",
278
+ title: "No renewal events",
279
+ description: "Renewals and price changes detected for this relationship will appear here."
280
+ }) : /* @__PURE__ */ w("div", {
281
+ className: "divide-y divide-border-subtle rounded-lg border border-border-subtle bg-bg-surface",
282
+ children: R.renewalEvents.map((e) => /* @__PURE__ */ T("div", {
283
+ className: "flex items-center justify-between gap-3 p-3",
284
+ children: [/* @__PURE__ */ T("div", {
285
+ className: "min-w-0",
286
+ children: [/* @__PURE__ */ w("p", {
287
+ className: "truncate text-sm font-medium text-text-primary",
288
+ children: i(e.eventType)
289
+ }), /* @__PURE__ */ T("p", {
290
+ className: "text-xs text-text-secondary",
291
+ children: [r(e.occursAt), e.detail ? ` · ${e.detail}` : ""]
292
+ })]
293
+ }), e.amount != null && /* @__PURE__ */ w("span", {
294
+ className: "shrink-0 text-sm font-semibold tabular-nums text-text-primary",
295
+ children: n(e.amount, e.currency ?? R.currency)
296
+ })]
297
+ }, e.id))
298
+ })
299
+ })
300
+ ]
301
+ });
302
+ }
303
+ //#endregion
304
+ export { O as RelationshipDetailPage };
305
+
306
+ //# sourceMappingURL=RelationshipDetailPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RelationshipDetailPage.js","names":[],"sources":["../../../../src/subscriberbot/pages/relationships/RelationshipDetailPage.tsx"],"sourcesContent":["/**\n * Relationship detail\n * @module subscriberbot/pages/relationships\n *\n * Full per-relationship view: plan, price, billing period, next renewal,\n * health score, entitlements, attached assets and renewal events — plus\n * lifecycle transitions (validated server-side) and cancel.\n * Reads `subRelationship(id)`; writes `subTransitionRelationship` /\n * `subCancelRelationship`.\n */\n\nimport { useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n Building2,\n CreditCard,\n ShieldCheck,\n CalendarClock,\n Paperclip,\n Activity,\n Tag,\n} from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView, Section } from '../../components/StateViews';\nimport { useSubscriberBot } from '../../context/SubscriberBotContext';\nimport {\n useRelationship,\n useTransitionRelationship,\n useCancelRelationship,\n} from '../../hooks/useSubscriberBotApi';\nimport {\n formatCurrency,\n formatDate,\n formatEnumLabel,\n formatIntervalSuffix,\n} from '../../utils/formatters';\nimport type { SubRelationshipStatus } from '../../types';\n\n// Lifecycle actions surfaced as buttons; the server validates the transition.\nconst TRANSITIONS: Array<{ status: SubRelationshipStatus; label: string }> = [\n { status: 'ACTIVE', label: 'Activate' },\n { status: 'PAUSED', label: 'Pause' },\n { status: 'EXPIRED', label: 'Mark expired' },\n];\n\nexport function RelationshipDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useSubscriberBot();\n const { data: relationship, isLoading, isError, refetch } = useRelationship(id);\n const transition = useTransitionRelationship();\n const cancel = useCancelRelationship();\n const [actionError, setActionError] = useState<string | null>(null);\n\n if (isLoading) {\n return (\n <PageLayout title=\"Relationship\">\n <LoadingView label=\"Loading relationship…\" />\n </PageLayout>\n );\n }\n\n if (isError) {\n return (\n <PageLayout title=\"Relationship\">\n <ErrorView message=\"Could not load this relationship.\" onRetry={() => void refetch()} />\n </PageLayout>\n );\n }\n\n if (!relationship) {\n return (\n <PageLayout title=\"Relationship\">\n <EmptyState\n illustration=\"empty-data\"\n title=\"Relationship not found\"\n description=\"It may have been removed, or you may not have access to it.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/relationships')}\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 Back to relationships\n </button>\n }\n />\n </PageLayout>\n );\n }\n\n const r = relationship;\n const isBusy = transition.isPending || cancel.isPending;\n\n const runTransition = (status: SubRelationshipStatus) => {\n setActionError(null);\n transition.mutate(\n { id: r.id, status },\n { onError: (e) => setActionError(e instanceof Error ? e.message : 'Transition failed') }\n );\n };\n\n const runCancel = () => {\n setActionError(null);\n cancel.mutate(r.id, {\n onError: (e) => setActionError(e instanceof Error ? e.message : 'Cancel failed'),\n });\n };\n\n return (\n <PageLayout\n title={r.label}\n description={r.provider?.name ?? formatEnumLabel(r.kind)}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/relationships')}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back\n </button>\n }\n >\n {/* Header summary */}\n <div className=\"flex flex-wrap items-center gap-3\">\n <StatusBadge status={r.status} />\n <span className=\"text-sm text-text-secondary\">{formatEnumLabel(r.kind)}</span>\n <span className=\"text-sm text-text-secondary\">·</span>\n <span className=\"text-sm font-semibold text-text-primary\">\n {formatCurrency(r.amount, r.currency)}\n <span className=\"text-xs font-normal text-text-secondary\">\n {formatIntervalSuffix(r.interval)}\n </span>\n </span>\n {r.autoRenew && <span className=\"text-xs text-text-secondary\">Auto-renews</span>}\n </div>\n\n {actionError && (\n <div className=\"mt-4 rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text\">\n {actionError}\n </div>\n )}\n\n {/* Lifecycle actions */}\n <div className=\"mt-5 flex flex-wrap gap-2\">\n {TRANSITIONS.filter((t) => t.status !== r.status).map((t) => (\n <button\n key={t.status}\n type=\"button\"\n disabled={isBusy}\n onClick={() => runTransition(t.status)}\n 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\"\n >\n {t.label}\n </button>\n ))}\n {r.status !== 'CANCELLED' && (\n <button\n type=\"button\"\n disabled={isBusy}\n onClick={runCancel}\n className=\"rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-3 py-1.5 text-xs font-medium text-status-error-text hover:opacity-90 disabled:opacity-50\"\n >\n Cancel relationship\n </button>\n )}\n </div>\n\n {/* Key facts grid */}\n <div className=\"mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-2 text-xs font-medium text-text-secondary\">\n <Building2 className=\"h-3.5 w-3.5\" /> Provider\n </div>\n <p className=\"mt-1.5 text-sm text-text-primary\">{r.provider?.name ?? '—'}</p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-2 text-xs font-medium text-text-secondary\">\n <CreditCard className=\"h-3.5 w-3.5\" /> Plan\n </div>\n <p className=\"mt-1.5 text-sm text-text-primary\">{r.plan?.name ?? '—'}</p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-2 text-xs font-medium text-text-secondary\">\n <CalendarClock className=\"h-3.5 w-3.5\" /> Next renewal\n </div>\n <p className=\"mt-1.5 text-sm text-text-primary\">\n {r.renewsAt ? formatDate(r.renewsAt) : '—'}\n </p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-2 text-xs font-medium text-text-secondary\">\n <Activity className=\"h-3.5 w-3.5\" /> Health score\n </div>\n <p className=\"mt-1.5 text-sm text-text-primary\">\n {r.healthScore != null ? `${r.healthScore}/100` : '—'}\n </p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-2 text-xs font-medium text-text-secondary\">\n <ShieldCheck className=\"h-3.5 w-3.5\" /> Discovery\n </div>\n <p className=\"mt-1.5 text-sm text-text-primary\">\n {formatEnumLabel(r.discoverySource)}\n </p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-2 text-xs font-medium text-text-secondary\">\n <CalendarClock className=\"h-3.5 w-3.5\" /> Started\n </div>\n <p className=\"mt-1.5 text-sm text-text-primary\">\n {r.startedAt ? formatDate(r.startedAt) : '—'}\n </p>\n </GlassCard>\n </div>\n\n {r.tags.length > 0 && (\n <div className=\"mt-5 flex flex-wrap items-center gap-2\">\n <Tag className=\"h-3.5 w-3.5 text-text-secondary\" />\n {r.tags.map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs text-text-secondary\"\n >\n {tag}\n </span>\n ))}\n </div>\n )}\n\n {r.notes && (\n <div className=\"mt-5 rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <p className=\"text-xs font-medium text-text-secondary\">Notes</p>\n <p className=\"mt-1 whitespace-pre-wrap text-sm text-text-primary\">{r.notes}</p>\n </div>\n )}\n\n {/* Entitlements */}\n <Section title=\"Entitlements\" description=\"What this relationship grants you\">\n {!r.entitlements || r.entitlements.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No entitlements\"\n description=\"Entitlements (seats, quotas, feature access) will appear here.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3\">\n {r.entitlements.map((e) => (\n <div key={e.id} className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <div className=\"flex items-start justify-between gap-2\">\n <p className=\"text-sm font-medium text-text-primary\">{e.name}</p>\n <StatusBadge status={e.isActive ? 'ACTIVE' : 'DISABLED'} />\n </div>\n <p className=\"mt-1 text-xs text-text-secondary\">{formatEnumLabel(e.type)}</p>\n {e.quota != null && (\n <p className=\"mt-2 text-xs text-text-secondary\">\n Used {e.used} of {e.quota}\n {e.unit ? ` ${e.unit}` : ''}\n </p>\n )}\n </div>\n ))}\n </div>\n )}\n </Section>\n\n {/* Assets */}\n <Section title=\"Assets\" description=\"Invoices, contracts, licenses and credentials\">\n {!r.assets || r.assets.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No assets attached\"\n description=\"Attach invoices, contracts, licenses or receipts to this relationship.\"\n />\n ) : (\n <div className=\"divide-y divide-border-subtle rounded-lg border border-border-subtle bg-bg-surface\">\n {r.assets.map((a) => (\n <div key={a.id} className=\"flex items-center justify-between gap-3 p-3\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <Paperclip 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\">{a.name}</p>\n <p className=\"text-xs text-text-secondary\">{formatEnumLabel(a.type)}</p>\n </div>\n </div>\n {a.expiresAt && (\n <span className=\"shrink-0 text-xs text-text-secondary\">\n Expires {formatDate(a.expiresAt)}\n </span>\n )}\n </div>\n ))}\n </div>\n )}\n </Section>\n\n {/* Renewal events */}\n <Section title=\"Renewal timeline\" description=\"Detected renewals and price changes\">\n {!r.renewalEvents || r.renewalEvents.length === 0 ? (\n <EmptyState\n illustration=\"empty-calendar\"\n title=\"No renewal events\"\n description=\"Renewals and price changes detected for this relationship will appear here.\"\n />\n ) : (\n <div className=\"divide-y divide-border-subtle rounded-lg border border-border-subtle bg-bg-surface\">\n {r.renewalEvents.map((event) => (\n <div key={event.id} className=\"flex items-center justify-between gap-3 p-3\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {formatEnumLabel(event.eventType)}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {formatDate(event.occursAt)}\n {event.detail ? ` · ${event.detail}` : ''}\n </p>\n </div>\n {event.amount != null && (\n <span className=\"shrink-0 text-sm font-semibold tabular-nums text-text-primary\">\n {formatCurrency(event.amount, event.currency ?? r.currency)}\n </span>\n )}\n </div>\n ))}\n </div>\n )}\n </Section>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,253 @@
1
+ import { useSubscriberBot as e } from "../../context/SubscriberBotContext.js";
2
+ import { PageLayout as t } from "../../components/PageLayout.js";
3
+ import { ErrorView as n } from "../../components/StateViews.js";
4
+ import { useCreateRelationship as r, usePlans as i, useProviders as a } from "../../hooks/useSubscriberBotApi.js";
5
+ import { useState as o } from "react";
6
+ import { ArrowLeft as s } from "lucide-react";
7
+ import { jsx as c, jsxs as l } from "react/jsx-runtime";
8
+ import { PagePurpose as u } from "@burdenoff/fe-libs/ui";
9
+ //#region src/subscriberbot/pages/relationships/RelationshipNewPage.tsx
10
+ var d = [
11
+ "SUBSCRIPTION",
12
+ "MEMBERSHIP",
13
+ "LICENSE",
14
+ "CONTRACT",
15
+ "NEWSLETTER",
16
+ "RECURRING_PAYMENT",
17
+ "ENROLLMENT",
18
+ "TRIAL"
19
+ ], f = [
20
+ "MONTHLY",
21
+ "YEARLY",
22
+ "QUARTERLY",
23
+ "WEEKLY",
24
+ "DAILY",
25
+ "USAGE_BASED",
26
+ "LIFETIME",
27
+ "FREE"
28
+ ], p = "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";
29
+ function h() {
30
+ let { navigateTo: h } = e(), g = r(), _ = a({ pagination: {
31
+ limit: 100,
32
+ offset: 0
33
+ } }), [v, y] = o(""), [b, x] = o("SUBSCRIPTION"), [S, C] = o(""), [w, T] = o(""), [E, D] = o(""), [O, k] = o("USD"), [A, j] = o("MONTHLY"), [M, N] = o(""), [P, F] = o(!0), [I, L] = o(""), [R, z] = o(null), B = i(S || void 0), V = _.data?.items ?? [], H = B.data ?? [];
34
+ return _.isError ? /* @__PURE__ */ c(t, {
35
+ title: "Add relationship",
36
+ children: /* @__PURE__ */ c(n, {
37
+ message: "Could not load the provider registry.",
38
+ onRetry: () => void _.refetch()
39
+ })
40
+ }) : /* @__PURE__ */ l(t, {
41
+ title: "Add relationship",
42
+ description: "Track a new recurring digital relationship",
43
+ actions: /* @__PURE__ */ l("button", {
44
+ type: "button",
45
+ onClick: () => h("/relationships"),
46
+ className: "inline-flex items-center gap-2 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken",
47
+ children: [/* @__PURE__ */ c(s, { className: "h-4 w-4" }), " Back"]
48
+ }),
49
+ children: [/* @__PURE__ */ c(u, {
50
+ className: "mb-6",
51
+ children: "Manually add a subscription, membership, license or contract. Link it to a provider and plan from the registry, set the price and renewal date, and Subscriber Bot will track its lifecycle from here."
52
+ }), /* @__PURE__ */ l("form", {
53
+ onSubmit: (e) => {
54
+ if (e.preventDefault(), z(null), !v.trim()) {
55
+ z("A label is required.");
56
+ return;
57
+ }
58
+ let t = {
59
+ label: v.trim(),
60
+ kind: b,
61
+ interval: A,
62
+ autoRenew: P,
63
+ currency: O.trim() || "USD",
64
+ ...S ? { providerId: S } : {},
65
+ ...w ? { planId: w } : {},
66
+ ...E ? { amount: Number(E) } : {},
67
+ ...M ? { renewsAt: new Date(M).toISOString() } : {},
68
+ ...I.trim() ? { notes: I.trim() } : {}
69
+ };
70
+ g.mutate(t, {
71
+ onSuccess: (e) => h(`/relationships/${e.id}`),
72
+ onError: (e) => z(e instanceof Error ? e.message : "Could not create relationship.")
73
+ });
74
+ },
75
+ className: "max-w-2xl space-y-5",
76
+ children: [
77
+ R && /* @__PURE__ */ c("div", {
78
+ className: "rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text",
79
+ children: R
80
+ }),
81
+ /* @__PURE__ */ l("div", { children: [/* @__PURE__ */ l("label", {
82
+ htmlFor: "rel-label",
83
+ className: m,
84
+ children: ["Label ", /* @__PURE__ */ c("span", {
85
+ className: "text-status-error-text",
86
+ children: "*"
87
+ })]
88
+ }), /* @__PURE__ */ c("input", {
89
+ id: "rel-label",
90
+ type: "text",
91
+ value: v,
92
+ onChange: (e) => y(e.target.value),
93
+ placeholder: "e.g. Netflix Premium, AWS Production",
94
+ className: p,
95
+ required: !0
96
+ })] }),
97
+ /* @__PURE__ */ l("div", {
98
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
99
+ children: [/* @__PURE__ */ l("div", { children: [/* @__PURE__ */ c("label", {
100
+ htmlFor: "rel-kind",
101
+ className: m,
102
+ children: "Kind"
103
+ }), /* @__PURE__ */ c("select", {
104
+ id: "rel-kind",
105
+ value: b,
106
+ onChange: (e) => x(e.target.value),
107
+ className: p,
108
+ children: d.map((e) => /* @__PURE__ */ c("option", {
109
+ value: e,
110
+ children: e.replace(/_/g, " ")
111
+ }, e))
112
+ })] }), /* @__PURE__ */ l("div", { children: [/* @__PURE__ */ c("label", {
113
+ htmlFor: "rel-provider",
114
+ className: m,
115
+ children: "Provider"
116
+ }), /* @__PURE__ */ l("select", {
117
+ id: "rel-provider",
118
+ value: S,
119
+ onChange: (e) => {
120
+ C(e.target.value), T("");
121
+ },
122
+ className: p,
123
+ children: [/* @__PURE__ */ c("option", {
124
+ value: "",
125
+ children: "— None —"
126
+ }), V.map((e) => /* @__PURE__ */ c("option", {
127
+ value: e.id,
128
+ children: e.name
129
+ }, e.id))]
130
+ })] })]
131
+ }),
132
+ S && H.length > 0 && /* @__PURE__ */ l("div", { children: [/* @__PURE__ */ c("label", {
133
+ htmlFor: "rel-plan",
134
+ className: m,
135
+ children: "Plan"
136
+ }), /* @__PURE__ */ l("select", {
137
+ id: "rel-plan",
138
+ value: w,
139
+ onChange: (e) => T(e.target.value),
140
+ className: p,
141
+ children: [/* @__PURE__ */ c("option", {
142
+ value: "",
143
+ children: "— None —"
144
+ }), H.map((e) => /* @__PURE__ */ c("option", {
145
+ value: e.id,
146
+ children: e.name
147
+ }, e.id))]
148
+ })] }),
149
+ /* @__PURE__ */ l("div", {
150
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-3",
151
+ children: [
152
+ /* @__PURE__ */ l("div", { children: [/* @__PURE__ */ c("label", {
153
+ htmlFor: "rel-amount",
154
+ className: m,
155
+ children: "Amount"
156
+ }), /* @__PURE__ */ c("input", {
157
+ id: "rel-amount",
158
+ type: "number",
159
+ min: "0",
160
+ step: "0.01",
161
+ value: E,
162
+ onChange: (e) => D(e.target.value),
163
+ placeholder: "0.00",
164
+ className: p
165
+ })] }),
166
+ /* @__PURE__ */ l("div", { children: [/* @__PURE__ */ c("label", {
167
+ htmlFor: "rel-currency",
168
+ className: m,
169
+ children: "Currency"
170
+ }), /* @__PURE__ */ c("input", {
171
+ id: "rel-currency",
172
+ type: "text",
173
+ value: O,
174
+ onChange: (e) => k(e.target.value.toUpperCase()),
175
+ placeholder: "USD",
176
+ maxLength: 3,
177
+ className: p
178
+ })] }),
179
+ /* @__PURE__ */ l("div", { children: [/* @__PURE__ */ c("label", {
180
+ htmlFor: "rel-interval",
181
+ className: m,
182
+ children: "Billing interval"
183
+ }), /* @__PURE__ */ c("select", {
184
+ id: "rel-interval",
185
+ value: A,
186
+ onChange: (e) => j(e.target.value),
187
+ className: p,
188
+ children: f.map((e) => /* @__PURE__ */ c("option", {
189
+ value: e,
190
+ children: e.replace(/_/g, " ")
191
+ }, e))
192
+ })] })
193
+ ]
194
+ }),
195
+ /* @__PURE__ */ l("div", {
196
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
197
+ children: [/* @__PURE__ */ l("div", { children: [/* @__PURE__ */ c("label", {
198
+ htmlFor: "rel-renews",
199
+ className: m,
200
+ children: "Renews on"
201
+ }), /* @__PURE__ */ c("input", {
202
+ id: "rel-renews",
203
+ type: "date",
204
+ value: M,
205
+ onChange: (e) => N(e.target.value),
206
+ className: p
207
+ })] }), /* @__PURE__ */ c("div", {
208
+ className: "flex items-end",
209
+ children: /* @__PURE__ */ l("label", {
210
+ className: "flex items-center gap-2 text-sm text-text-primary",
211
+ children: [/* @__PURE__ */ c("input", {
212
+ type: "checkbox",
213
+ checked: P,
214
+ onChange: (e) => F(e.target.checked),
215
+ className: "size-4 rounded border-border-subtle"
216
+ }), "Auto-renews"]
217
+ })
218
+ })]
219
+ }),
220
+ /* @__PURE__ */ l("div", { children: [/* @__PURE__ */ c("label", {
221
+ htmlFor: "rel-notes",
222
+ className: m,
223
+ children: "Notes"
224
+ }), /* @__PURE__ */ c("textarea", {
225
+ id: "rel-notes",
226
+ value: I,
227
+ onChange: (e) => L(e.target.value),
228
+ rows: 3,
229
+ placeholder: "Anything worth remembering about this relationship…",
230
+ className: p
231
+ })] }),
232
+ /* @__PURE__ */ l("div", {
233
+ className: "flex gap-3",
234
+ children: [/* @__PURE__ */ c("button", {
235
+ type: "submit",
236
+ disabled: g.isPending,
237
+ className: "rounded-lg bg-action-primary-bg px-5 py-2.5 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50",
238
+ children: g.isPending ? "Creating…" : "Create relationship"
239
+ }), /* @__PURE__ */ c("button", {
240
+ type: "button",
241
+ onClick: () => h("/relationships"),
242
+ className: "rounded-lg border border-border-subtle px-5 py-2.5 text-sm font-medium text-text-primary hover:bg-bg-sunken",
243
+ children: "Cancel"
244
+ })]
245
+ })
246
+ ]
247
+ })]
248
+ });
249
+ }
250
+ //#endregion
251
+ export { h as RelationshipNewPage };
252
+
253
+ //# sourceMappingURL=RelationshipNewPage.js.map