@burdenoff/microfe-subscriberbot 2026.626.2 → 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.
- package/dist/index.js +4 -4
- package/dist/subscriberbot/SubscriberBotRoot.js +61 -133
- package/dist/subscriberbot/SubscriberBotRoot.js.map +1 -1
- package/dist/subscriberbot/SubscriberBotRoutes.js +56 -187
- package/dist/subscriberbot/SubscriberBotRoutes.js.map +1 -1
- package/dist/subscriberbot/components/StateViews.js +49 -0
- package/dist/subscriberbot/components/StateViews.js.map +1 -0
- package/dist/subscriberbot/components/StatusBadge.js +45 -47
- package/dist/subscriberbot/components/StatusBadge.js.map +1 -1
- package/dist/subscriberbot/context/SubscriberBotContext.js +22 -30
- package/dist/subscriberbot/context/SubscriberBotContext.js.map +1 -1
- package/dist/subscriberbot/hooks/useSubscriberBotApi.js +342 -195
- package/dist/subscriberbot/hooks/useSubscriberBotApi.js.map +1 -1
- package/dist/subscriberbot/index.js +1 -1
- package/dist/subscriberbot/pages/DashboardPage.js +269 -0
- package/dist/subscriberbot/pages/DashboardPage.js.map +1 -0
- package/dist/subscriberbot/pages/InboxPage.js +186 -0
- package/dist/subscriberbot/pages/InboxPage.js.map +1 -0
- package/dist/subscriberbot/pages/InvoicesPage.js +211 -0
- package/dist/subscriberbot/pages/InvoicesPage.js.map +1 -0
- package/dist/subscriberbot/pages/WorkflowsPage.js +236 -0
- package/dist/subscriberbot/pages/WorkflowsPage.js.map +1 -0
- package/dist/subscriberbot/pages/agents/AgentDetailPage.js +160 -0
- package/dist/subscriberbot/pages/agents/AgentDetailPage.js.map +1 -0
- package/dist/subscriberbot/pages/agents/AgentNewPage.js +140 -0
- package/dist/subscriberbot/pages/agents/AgentNewPage.js.map +1 -0
- package/dist/subscriberbot/pages/agents/AgentsListPage.js +178 -0
- package/dist/subscriberbot/pages/agents/AgentsListPage.js.map +1 -0
- package/dist/subscriberbot/pages/providers/ProviderDetailPage.js +179 -0
- package/dist/subscriberbot/pages/providers/ProviderDetailPage.js.map +1 -0
- package/dist/subscriberbot/pages/providers/ProviderNewPage.js +185 -0
- package/dist/subscriberbot/pages/providers/ProviderNewPage.js.map +1 -0
- package/dist/subscriberbot/pages/providers/ProvidersListPage.js +254 -0
- package/dist/subscriberbot/pages/providers/ProvidersListPage.js.map +1 -0
- package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js +306 -0
- package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js.map +1 -0
- package/dist/subscriberbot/pages/relationships/RelationshipNewPage.js +253 -0
- package/dist/subscriberbot/pages/relationships/RelationshipNewPage.js.map +1 -0
- package/dist/subscriberbot/pages/relationships/RelationshipsListPage.js +248 -0
- package/dist/subscriberbot/pages/relationships/RelationshipsListPage.js.map +1 -0
- package/dist/subscriberbot/utils/formatters.js +50 -32
- package/dist/subscriberbot/utils/formatters.js.map +1 -1
- package/package.json +1 -1
- package/dist/subscriberbot/components/InvestmentCard.js +0 -97
- package/dist/subscriberbot/components/InvestmentCard.js.map +0 -1
- package/dist/subscriberbot/components/RatingDisplay.js +0 -35
- package/dist/subscriberbot/components/RatingDisplay.js.map +0 -1
- package/dist/subscriberbot/mocks/data.js +0 -1517
- package/dist/subscriberbot/mocks/data.js.map +0 -1
- package/dist/subscriberbot/mocks/users.js +0 -34
- package/dist/subscriberbot/mocks/users.js.map +0 -1
- package/dist/subscriberbot/pages/HomePage.js +0 -930
- package/dist/subscriberbot/pages/HomePage.js.map +0 -1
- package/dist/subscriberbot/pages/admin/AdminAnalyticsPage.js +0 -651
- package/dist/subscriberbot/pages/admin/AdminAnalyticsPage.js.map +0 -1
- package/dist/subscriberbot/pages/admin/AdminDashboard.js +0 -679
- package/dist/subscriberbot/pages/admin/AdminDashboard.js.map +0 -1
- package/dist/subscriberbot/pages/admin/AdminFinancialPage.js +0 -427
- package/dist/subscriberbot/pages/admin/AdminFinancialPage.js.map +0 -1
- package/dist/subscriberbot/pages/admin/AdminOrdersPage.js +0 -665
- package/dist/subscriberbot/pages/admin/AdminOrdersPage.js.map +0 -1
- package/dist/subscriberbot/pages/admin/AdminProfilePage.js +0 -261
- package/dist/subscriberbot/pages/admin/AdminProfilePage.js.map +0 -1
- package/dist/subscriberbot/pages/admin/AdminSettingsPage.js +0 -654
- package/dist/subscriberbot/pages/admin/AdminSettingsPage.js.map +0 -1
- package/dist/subscriberbot/pages/admin/CartManagementPage.js +0 -874
- package/dist/subscriberbot/pages/admin/CartManagementPage.js.map +0 -1
- package/dist/subscriberbot/pages/admin/UserManagementPage.js +0 -898
- package/dist/subscriberbot/pages/admin/UserManagementPage.js.map +0 -1
- package/dist/subscriberbot/pages/cart-owner/AnalyticsDashboard.js +0 -839
- package/dist/subscriberbot/pages/cart-owner/AnalyticsDashboard.js.map +0 -1
- package/dist/subscriberbot/pages/cart-owner/BusinessSettingsPage.js +0 -705
- package/dist/subscriberbot/pages/cart-owner/BusinessSettingsPage.js.map +0 -1
- package/dist/subscriberbot/pages/cart-owner/CartOwnerDashboard.js +0 -802
- package/dist/subscriberbot/pages/cart-owner/CartOwnerDashboard.js.map +0 -1
- package/dist/subscriberbot/pages/cart-owner/CartOwnerProfilePage.js +0 -891
- package/dist/subscriberbot/pages/cart-owner/CartOwnerProfilePage.js.map +0 -1
- package/dist/subscriberbot/pages/cart-owner/CartServiceConfigPage.js +0 -633
- package/dist/subscriberbot/pages/cart-owner/CartServiceConfigPage.js.map +0 -1
- package/dist/subscriberbot/pages/cart-owner/InventoryManagementPage.js +0 -910
- package/dist/subscriberbot/pages/cart-owner/InventoryManagementPage.js.map +0 -1
- package/dist/subscriberbot/pages/cart-owner/OrderManagementPage.js +0 -879
- package/dist/subscriberbot/pages/cart-owner/OrderManagementPage.js.map +0 -1
- package/dist/subscriberbot/pages/cart-owner/StaffManagementPage.js +0 -838
- package/dist/subscriberbot/pages/cart-owner/StaffManagementPage.js.map +0 -1
- package/dist/subscriberbot/pages/common/HelpSupportPage.js +0 -1323
- package/dist/subscriberbot/pages/common/HelpSupportPage.js.map +0 -1
- package/dist/subscriberbot/pages/common/NotificationCenterPage.js +0 -672
- package/dist/subscriberbot/pages/common/NotificationCenterPage.js.map +0 -1
- package/dist/subscriberbot/pages/common/SettingsPage.js +0 -973
- package/dist/subscriberbot/pages/common/SettingsPage.js.map +0 -1
- package/dist/subscriberbot/pages/customer/BookingDetailsPage.js +0 -626
- package/dist/subscriberbot/pages/customer/BookingDetailsPage.js.map +0 -1
- package/dist/subscriberbot/pages/customer/CartDetailPage.js +0 -654
- package/dist/subscriberbot/pages/customer/CartDetailPage.js.map +0 -1
- package/dist/subscriberbot/pages/customer/CartDiscoveryPage.js +0 -555
- package/dist/subscriberbot/pages/customer/CartDiscoveryPage.js.map +0 -1
- package/dist/subscriberbot/pages/customer/CheckoutPage.js +0 -529
- package/dist/subscriberbot/pages/customer/CheckoutPage.js.map +0 -1
- package/dist/subscriberbot/pages/customer/CustomerDashboard.js +0 -532
- package/dist/subscriberbot/pages/customer/CustomerDashboard.js.map +0 -1
- package/dist/subscriberbot/pages/customer/CustomerProfilePage.js +0 -630
- package/dist/subscriberbot/pages/customer/CustomerProfilePage.js.map +0 -1
- package/dist/subscriberbot/pages/customer/DirectServiceBookingPage.js +0 -633
- package/dist/subscriberbot/pages/customer/DirectServiceBookingPage.js.map +0 -1
- package/dist/subscriberbot/pages/customer/FavoritesPage.js +0 -484
- package/dist/subscriberbot/pages/customer/FavoritesPage.js.map +0 -1
- package/dist/subscriberbot/pages/customer/OrderHistoryPage.js +0 -446
- package/dist/subscriberbot/pages/customer/OrderHistoryPage.js.map +0 -1
- package/dist/subscriberbot/pages/customer/OrderTrackingPage.js +0 -394
- package/dist/subscriberbot/pages/customer/OrderTrackingPage.js.map +0 -1
- package/dist/subscriberbot/pages/customer/PaymentMethodsPage.js +0 -495
- package/dist/subscriberbot/pages/customer/PaymentMethodsPage.js.map +0 -1
- package/dist/subscriberbot/pages/customer/ReviewsPage.js +0 -613
- package/dist/subscriberbot/pages/customer/ReviewsPage.js.map +0 -1
- package/dist/subscriberbot/pages/customer/SavedAddressesPage.js +0 -450
- package/dist/subscriberbot/pages/customer/SavedAddressesPage.js.map +0 -1
- package/dist/subscriberbot/pages/investor/InvestmentDetailsPage.js +0 -886
- package/dist/subscriberbot/pages/investor/InvestmentDetailsPage.js.map +0 -1
- package/dist/subscriberbot/pages/investor/InvestmentOpportunitiesPage.js +0 -947
- package/dist/subscriberbot/pages/investor/InvestmentOpportunitiesPage.js.map +0 -1
- package/dist/subscriberbot/pages/investor/InvestorAnalyticsPage.js +0 -687
- package/dist/subscriberbot/pages/investor/InvestorAnalyticsPage.js.map +0 -1
- package/dist/subscriberbot/pages/investor/InvestorDashboard.js +0 -451
- package/dist/subscriberbot/pages/investor/InvestorDashboard.js.map +0 -1
- package/dist/subscriberbot/pages/investor/InvestorInvestmentsPage.js +0 -406
- package/dist/subscriberbot/pages/investor/InvestorInvestmentsPage.js.map +0 -1
- package/dist/subscriberbot/pages/investor/InvestorPortfolioPage.js +0 -575
- package/dist/subscriberbot/pages/investor/InvestorPortfolioPage.js.map +0 -1
- package/dist/subscriberbot/pages/investor/InvestorProfilePage.js +0 -1070
- package/dist/subscriberbot/pages/investor/InvestorProfilePage.js.map +0 -1
- package/dist/subscriberbot/pages/worker/TaskDetailsPage.js +0 -535
- package/dist/subscriberbot/pages/worker/TaskDetailsPage.js.map +0 -1
- package/dist/subscriberbot/pages/worker/WorkerDashboard.js +0 -666
- package/dist/subscriberbot/pages/worker/WorkerDashboard.js.map +0 -1
- package/dist/subscriberbot/pages/worker/WorkerOrdersPage.js +0 -681
- package/dist/subscriberbot/pages/worker/WorkerOrdersPage.js.map +0 -1
- package/dist/subscriberbot/pages/worker/WorkerProfilePage.js +0 -692
- package/dist/subscriberbot/pages/worker/WorkerProfilePage.js.map +0 -1
- package/dist/subscriberbot/pages/worker/WorkerSchedulePage.js +0 -588
- 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
|