@burdenoff/microfe-subscriberbot 2026.801.1 → 2026.829.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/subscriberbot/admin/AdminDashboardPage.d.ts.map +1 -1
- package/dist/subscriberbot/admin/AdminDashboardPage.js +80 -83
- package/dist/subscriberbot/admin/AdminDashboardPage.js.map +1 -1
- package/dist/subscriberbot/components/viz.js +51 -27
- package/dist/subscriberbot/components/viz.js.map +1 -1
- package/dist/subscriberbot/hooks/index.d.ts +2 -1
- package/dist/subscriberbot/hooks/index.d.ts.map +1 -1
- package/dist/subscriberbot/hooks/index.js +1 -0
- package/dist/subscriberbot/hooks/useSubscriberBotApi.d.ts +12 -1
- package/dist/subscriberbot/hooks/useSubscriberBotApi.d.ts.map +1 -1
- package/dist/subscriberbot/hooks/useSubscriberBotApi.js +82 -56
- package/dist/subscriberbot/hooks/useSubscriberBotApi.js.map +1 -1
- package/dist/subscriberbot/hooks/useTr.d.ts +17 -0
- package/dist/subscriberbot/hooks/useTr.d.ts.map +1 -0
- package/dist/subscriberbot/hooks/useTr.js +14 -0
- package/dist/subscriberbot/hooks/useTr.js.map +1 -0
- package/dist/subscriberbot/pages/AssetVaultPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/AssetVaultPage.js +155 -97
- package/dist/subscriberbot/pages/AssetVaultPage.js.map +1 -1
- package/dist/subscriberbot/pages/DashboardPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/DashboardPage.js +113 -105
- package/dist/subscriberbot/pages/DashboardPage.js.map +1 -1
- package/dist/subscriberbot/pages/InboxPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/InboxPage.js +404 -369
- package/dist/subscriberbot/pages/InboxPage.js.map +1 -1
- package/dist/subscriberbot/pages/InsightsPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/InsightsPage.js +94 -93
- package/dist/subscriberbot/pages/InsightsPage.js.map +1 -1
- package/dist/subscriberbot/pages/InvoicesPage.d.ts +3 -2
- package/dist/subscriberbot/pages/InvoicesPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/InvoicesPage.js +368 -362
- package/dist/subscriberbot/pages/InvoicesPage.js.map +1 -1
- package/dist/subscriberbot/pages/WorkflowsPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/WorkflowsPage.js +209 -193
- package/dist/subscriberbot/pages/WorkflowsPage.js.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentDetailPage.d.ts +3 -1
- package/dist/subscriberbot/pages/agents/AgentDetailPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentDetailPage.js +296 -228
- package/dist/subscriberbot/pages/agents/AgentDetailPage.js.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentNewPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentNewPage.js +96 -76
- package/dist/subscriberbot/pages/agents/AgentNewPage.js.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentsListPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentsListPage.js +145 -161
- package/dist/subscriberbot/pages/agents/AgentsListPage.js.map +1 -1
- package/dist/subscriberbot/pages/providers/ProviderDetailPage.d.ts +4 -2
- package/dist/subscriberbot/pages/providers/ProviderDetailPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/providers/ProviderDetailPage.js +577 -402
- package/dist/subscriberbot/pages/providers/ProviderDetailPage.js.map +1 -1
- package/dist/subscriberbot/pages/providers/ProviderNewPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/providers/ProviderNewPage.js +167 -129
- package/dist/subscriberbot/pages/providers/ProviderNewPage.js.map +1 -1
- package/dist/subscriberbot/pages/providers/ProvidersListPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/providers/ProvidersListPage.js +171 -218
- package/dist/subscriberbot/pages/providers/ProvidersListPage.js.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js +340 -279
- package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipNewPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipNewPage.js +185 -166
- package/dist/subscriberbot/pages/relationships/RelationshipNewPage.js.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipsListPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipsListPage.js +153 -182
- package/dist/subscriberbot/pages/relationships/RelationshipsListPage.js.map +1 -1
- package/dist/subscriberbot/types/index.d.ts +37 -0
- package/dist/subscriberbot/types/index.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InsightsPage.js","names":[],"sources":["../../../src/subscriberbot/pages/InsightsPage.tsx"],"sourcesContent":["/**\n * Insights — spend analytics + renewal forecast, aggregated entirely from the\n * real subscription portfolio (`useRelationships`). Nothing is mocked: every\n * figure is computed from live relationship data (amount normalized to a monthly\n * equivalent by billing interval). Covers the concept mocks for cost insight,\n * renewal cashflow forecast, and spend management.\n * @module subscriberbot/pages\n */\nimport { useMemo, type FC } from 'react';\nimport { TrendingUp, Wallet, CalendarClock, HeartPulse, Layers } from 'lucide-react';\nimport { GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n MetricsCard,\n EmptyState,\n LoadingView,\n ErrorView,\n Section,\n Donut,\n Legend,\n BarList,\n MiniProgress,\n} from '../components';\nimport { useRelationships } from '../hooks/useSubscriberBotApi';\nimport { formatCurrency, formatEnumLabel } from '../utils/formatters';\nimport type { SubBillingInterval, SubRelationship } from '../types';\nimport { cn } from '../utils/cn';\n\nconst MONTHLY_FACTOR: Record<SubBillingInterval, number> = {\n DAILY: 30,\n WEEKLY: 4.345,\n MONTHLY: 1,\n QUARTERLY: 1 / 3,\n YEARLY: 1 / 12,\n LIFETIME: 0,\n FREE: 0,\n USAGE_BASED: 1,\n};\n\nconst monthly = (r: SubRelationship): number => r.amount * (MONTHLY_FACTOR[r.interval] ?? 1);\nconst isActive = (r: SubRelationship): boolean => r.status === 'ACTIVE';\n\nexport const InsightsPage: FC = () => {\n const query = useRelationships({ pagination: { limit: 100 } });\n const rels = useMemo(() => query.data?.items ?? [], [query.data]);\n\n const stats = useMemo(() => {\n const active = rels.filter(isActive);\n const currency = active[0]?.currency ?? rels[0]?.currency ?? 'USD';\n const monthlySpend = active.reduce((s, r) => s + monthly(r), 0);\n const scored = rels.filter((r) => r.healthScore != null);\n const avgHealth = scored.length\n ? scored.reduce((s, r) => s + (r.healthScore ?? 0), 0) / scored.length\n : null;\n\n // by status (donut)\n const byStatus = new Map<string, number>();\n for (const r of rels) byStatus.set(r.status, (byStatus.get(r.status) ?? 0) + 1);\n\n // spend by kind (bar)\n const byKind = new Map<string, number>();\n for (const r of active) byKind.set(r.kind, (byKind.get(r.kind) ?? 0) + monthly(r));\n\n // top monthly costs\n const topCosts = [...active].sort((a, b) => monthly(b) - monthly(a)).slice(0, 6);\n\n // renewal forecast — next 6 calendar months by renewsAt\n const buckets: { label: string; value: number }[] = [];\n const now = new Date();\n for (let i = 0; i < 6; i += 1) {\n const d = new Date(now.getFullYear(), now.getMonth() + i, 1);\n const key = `${d.getFullYear()}-${d.getMonth()}`;\n const label = d.toLocaleString(undefined, { month: 'short' });\n const value = rels.reduce((s, r) => {\n if (!r.renewsAt) return s;\n const rd = new Date(r.renewsAt);\n if (Number.isNaN(rd.getTime())) return s;\n return `${rd.getFullYear()}-${rd.getMonth()}` === key ? s + r.amount : s;\n }, 0);\n buckets.push({ label, value });\n }\n\n // low-health watchlist\n const watchlist = rels\n .filter((r) => r.healthScore != null && (r.healthScore as number) < 40)\n .sort((a, b) => (a.healthScore ?? 0) - (b.healthScore ?? 0))\n .slice(0, 6);\n\n const renewals30 = rels.filter((r) => {\n if (!r.renewsAt) return false;\n const t = new Date(r.renewsAt).getTime();\n if (Number.isNaN(t)) return false;\n const days = (t - Date.now()) / 86_400_000;\n return days >= 0 && days <= 30;\n }).length;\n\n return {\n currency,\n monthlySpend,\n activeCount: active.length,\n avgHealth,\n renewals30,\n statusData: [...byStatus.entries()].map(([label, value]) => ({\n label: formatEnumLabel(label),\n value,\n })),\n kindData: [...byKind.entries()]\n .map(([label, value]) => ({ label: formatEnumLabel(label), value }))\n .sort((a, b) => b.value - a.value),\n topCosts,\n forecast: buckets,\n watchlist,\n total: rels.length,\n };\n }, [rels]);\n\n if (query.isLoading) return <LoadingView label=\"Crunching your spend insights…\" />;\n if (query.isError)\n return <ErrorView message=\"Could not load insights.\" onRetry={() => void query.refetch()} />;\n\n const fmt = (n: number) => formatCurrency(n, stats.currency);\n\n return (\n <PageLayout\n title=\"Insights\"\n description=\"Where your recurring money goes, what renews next, and what to review — computed live from your portfolio.\"\n >\n <PagePurpose className=\"mb-2\">\n Understand and optimize your recurring spend. Every figure here is aggregated from your real\n subscriptions — normalized to a monthly equivalent so plans on different billing cycles are\n comparable.\n </PagePurpose>\n\n {stats.total === 0 ? (\n <EmptyState\n icon={<TrendingUp className=\"h-8 w-8\" />}\n title=\"No data to analyze yet\"\n description=\"Add a few subscriptions and your spend breakdown, renewal forecast and health watchlist will appear here.\"\n />\n ) : (\n <>\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n <MetricsCard\n icon={<Wallet className=\"h-5 w-5\" />}\n value={fmt(stats.monthlySpend)}\n label=\"Recurring spend / month\"\n featured\n />\n <MetricsCard\n icon={<Layers className=\"h-5 w-5\" />}\n value={stats.activeCount}\n label=\"Active subscriptions\"\n />\n <MetricsCard\n icon={<CalendarClock className=\"h-5 w-5\" />}\n value={stats.renewals30}\n label=\"Renewing in 30 days\"\n />\n <MetricsCard\n icon={<HeartPulse className=\"h-5 w-5\" />}\n value={stats.avgHealth != null ? Math.round(stats.avgHealth) : '—'}\n label=\"Avg. health score\"\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 lg:grid-cols-3\">\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <h3 className=\"mb-4 text-sm font-semibold text-text-primary\">Portfolio by status</h3>\n <div className=\"flex flex-col items-center gap-6 sm:flex-row\">\n <Donut\n data={stats.statusData}\n centerLabel={String(stats.total)}\n centerSub=\"subscriptions\"\n />\n <Legend className=\"w-full sm:flex-1\" data={stats.statusData} />\n </div>\n </GlassCard>\n\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <h3 className=\"mb-4 text-sm font-semibold text-text-primary\">\n Monthly spend by type\n </h3>\n {stats.kindData.length ? (\n <BarList data={stats.kindData} valueFormat={fmt} />\n ) : (\n <p className=\"text-sm text-text-secondary\">No active spend.</p>\n )}\n </GlassCard>\n\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <h3 className=\"mb-4 text-sm font-semibold text-text-primary\">\n Renewal forecast (6 mo)\n </h3>\n {stats.forecast.some((f) => f.value > 0) ? (\n <BarList data={stats.forecast} valueFormat={fmt} />\n ) : (\n <p className=\"text-sm text-text-secondary\">\n No dated renewals in the next 6 months.\n </p>\n )}\n </GlassCard>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 lg:grid-cols-2\">\n <Section title=\"Top monthly costs\">\n <GlassCard treatment=\"plain\" className=\"divide-y divide-border-subtle\">\n {stats.topCosts.map((r) => (\n <div key={r.id} className=\"flex items-center justify-between p-4\">\n <span className=\"truncate text-sm text-text-primary\">{r.label}</span>\n <span className=\"ml-3 shrink-0 text-sm font-semibold tabular-nums text-text-primary\">\n {fmt(monthly(r))}\n <span className=\"text-xs font-normal text-text-secondary\">/mo</span>\n </span>\n </div>\n ))}\n </GlassCard>\n </Section>\n\n <Section title=\"Health watchlist\">\n {stats.watchlist.length ? (\n <GlassCard treatment=\"plain\" className=\"divide-y divide-border-subtle\">\n {stats.watchlist.map((r) => (\n <div key={r.id} className=\"flex items-center gap-3 p-4\">\n <span className=\"w-32 shrink-0 truncate text-sm text-text-primary\">\n {r.label}\n </span>\n <MiniProgress className=\"flex-1\" value={r.healthScore ?? 0} tone=\"negative\" />\n <span\n className={cn(\n 'w-8 shrink-0 text-right text-sm font-medium tabular-nums text-status-warning-text'\n )}\n >\n {r.healthScore}\n </span>\n </div>\n ))}\n </GlassCard>\n ) : (\n <GlassCard treatment=\"plain\" className=\"p-6 text-sm text-text-secondary\">\n Nothing needs review — all scored subscriptions look healthy.\n </GlassCard>\n )}\n </Section>\n </div>\n </>\n )}\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"InsightsPage.js","names":[],"sources":["../../../src/subscriberbot/pages/InsightsPage.tsx"],"sourcesContent":["/**\n * Insights — spend analytics + renewal forecast, aggregated entirely from the\n * real subscription portfolio (`useRelationships`). Nothing is mocked: every\n * figure is computed from live relationship data (amount normalized to a monthly\n * equivalent by billing interval). Covers the concept mocks for cost insight,\n * renewal cashflow forecast, and spend management.\n * @module subscriberbot/pages\n */\nimport { useMemo, type FC } from 'react';\nimport { TrendingUp, Wallet, CalendarClock, HeartPulse, Layers } from 'lucide-react';\nimport { GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n MetricsCard,\n EmptyState,\n LoadingView,\n ErrorView,\n Section,\n Donut,\n Legend,\n BarList,\n MiniProgress,\n} from '../components';\nimport { useRelationships } from '../hooks/useSubscriberBotApi';\nimport { useTr } from '../hooks/useTr';\nimport { formatCurrency, formatEnumLabel } from '../utils/formatters';\nimport type { SubBillingInterval, SubRelationship } from '../types';\nimport { cn } from '../utils/cn';\n\nconst MONTHLY_FACTOR: Record<SubBillingInterval, number> = {\n DAILY: 30,\n WEEKLY: 4.345,\n MONTHLY: 1,\n QUARTERLY: 1 / 3,\n YEARLY: 1 / 12,\n LIFETIME: 0,\n FREE: 0,\n USAGE_BASED: 1,\n};\n\nconst monthly = (r: SubRelationship): number => r.amount * (MONTHLY_FACTOR[r.interval] ?? 1);\nconst isActive = (r: SubRelationship): boolean => r.status === 'ACTIVE';\n\nexport const InsightsPage: FC = () => {\n const query = useRelationships({ pagination: { limit: 100 } });\n const tr = useTr();\n const rels = useMemo(() => query.data?.items ?? [], [query.data]);\n\n const stats = useMemo(() => {\n const active = rels.filter(isActive);\n const currency = active[0]?.currency ?? rels[0]?.currency ?? 'USD';\n const monthlySpend = active.reduce((s, r) => s + monthly(r), 0);\n const scored = rels.filter((r) => r.healthScore != null);\n const avgHealth = scored.length\n ? scored.reduce((s, r) => s + (r.healthScore ?? 0), 0) / scored.length\n : null;\n\n // by status (donut)\n const byStatus = new Map<string, number>();\n for (const r of rels) byStatus.set(r.status, (byStatus.get(r.status) ?? 0) + 1);\n\n // spend by kind (bar)\n const byKind = new Map<string, number>();\n for (const r of active) byKind.set(r.kind, (byKind.get(r.kind) ?? 0) + monthly(r));\n\n // top monthly costs\n const topCosts = [...active].sort((a, b) => monthly(b) - monthly(a)).slice(0, 6);\n\n // renewal forecast — next 6 calendar months by renewsAt\n const buckets: { label: string; value: number }[] = [];\n const now = new Date();\n for (let i = 0; i < 6; i += 1) {\n const d = new Date(now.getFullYear(), now.getMonth() + i, 1);\n const key = `${d.getFullYear()}-${d.getMonth()}`;\n const label = d.toLocaleString(undefined, { month: 'short' });\n const value = rels.reduce((s, r) => {\n if (!r.renewsAt) return s;\n const rd = new Date(r.renewsAt);\n if (Number.isNaN(rd.getTime())) return s;\n return `${rd.getFullYear()}-${rd.getMonth()}` === key ? s + r.amount : s;\n }, 0);\n buckets.push({ label, value });\n }\n\n // low-health watchlist\n const watchlist = rels\n .filter((r) => r.healthScore != null && (r.healthScore as number) < 40)\n .sort((a, b) => (a.healthScore ?? 0) - (b.healthScore ?? 0))\n .slice(0, 6);\n\n const renewals30 = rels.filter((r) => {\n if (!r.renewsAt) return false;\n const t = new Date(r.renewsAt).getTime();\n if (Number.isNaN(t)) return false;\n const days = (t - Date.now()) / 86_400_000;\n return days >= 0 && days <= 30;\n }).length;\n\n return {\n currency,\n monthlySpend,\n activeCount: active.length,\n avgHealth,\n renewals30,\n statusData: [...byStatus.entries()].map(([label, value]) => ({\n label: formatEnumLabel(label),\n value,\n })),\n kindData: [...byKind.entries()]\n .map(([label, value]) => ({ label: formatEnumLabel(label), value }))\n .sort((a, b) => b.value - a.value),\n topCosts,\n forecast: buckets,\n watchlist,\n total: rels.length,\n };\n }, [rels]);\n\n if (query.isLoading)\n return <LoadingView label={tr('insights.loading', 'Crunching your spend insights…')} />;\n if (query.isError)\n return (\n <ErrorView\n message={tr('insights.error', 'Could not load insights.')}\n onRetry={() => void query.refetch()}\n />\n );\n\n const fmt = (n: number) => formatCurrency(n, stats.currency);\n\n return (\n <PageLayout\n title={tr('insights.title', 'Insights')}\n description={tr(\n 'insights.description',\n 'Where your recurring money goes, what renews next, and what to review — computed live from your portfolio.'\n )}\n >\n <PagePurpose className=\"mb-2\">\n {tr(\n 'insights.purpose',\n 'Understand and optimize your recurring spend. Every figure here is aggregated from your real subscriptions — normalized to a monthly equivalent so plans on different billing cycles are comparable.'\n )}\n </PagePurpose>\n\n {stats.total === 0 ? (\n <EmptyState\n icon={<TrendingUp className=\"h-8 w-8\" />}\n title={tr('insights.emptyTitle', 'No data to analyze yet')}\n description={tr(\n 'insights.emptyDescription',\n 'Add a few subscriptions and your spend breakdown, renewal forecast and health watchlist will appear here.'\n )}\n />\n ) : (\n <>\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n <MetricsCard\n icon={<Wallet className=\"h-5 w-5\" />}\n value={fmt(stats.monthlySpend)}\n label={tr('insights.metrics.recurringSpend', 'Recurring spend / month')}\n featured\n />\n <MetricsCard\n icon={<Layers className=\"h-5 w-5\" />}\n value={stats.activeCount}\n label={tr('insights.metrics.activeSubscriptions', 'Active subscriptions')}\n />\n <MetricsCard\n icon={<CalendarClock className=\"h-5 w-5\" />}\n value={stats.renewals30}\n label={tr('insights.metrics.renewing30Days', 'Renewing in 30 days')}\n />\n <MetricsCard\n icon={<HeartPulse className=\"h-5 w-5\" />}\n value={\n stats.avgHealth != null\n ? Math.round(stats.avgHealth)\n : tr('insights.metrics.noScore', '—')\n }\n label={tr('insights.metrics.avgHealthScore', 'Avg. health score')}\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 lg:grid-cols-3\">\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <h3 className=\"mb-4 text-sm font-semibold text-text-primary\">\n {tr('insights.portfolioByStatus', 'Portfolio by status')}\n </h3>\n <div className=\"flex flex-col items-center gap-6 sm:flex-row\">\n <Donut\n data={stats.statusData}\n centerLabel={String(stats.total)}\n centerSub={tr('insights.subscriptions', 'subscriptions')}\n />\n <Legend className=\"w-full sm:flex-1\" data={stats.statusData} />\n </div>\n </GlassCard>\n\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <h3 className=\"mb-4 text-sm font-semibold text-text-primary\">\n {tr('insights.spendByType', 'Monthly spend by type')}\n </h3>\n {stats.kindData.length ? (\n <BarList data={stats.kindData} valueFormat={fmt} />\n ) : (\n <p className=\"text-sm text-text-secondary\">\n {tr('insights.noActiveSpend', 'No active spend.')}\n </p>\n )}\n </GlassCard>\n\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <h3 className=\"mb-4 text-sm font-semibold text-text-primary\">\n {tr('insights.renewalForecast', 'Renewal forecast (6 mo)')}\n </h3>\n {stats.forecast.some((f) => f.value > 0) ? (\n <BarList data={stats.forecast} valueFormat={fmt} />\n ) : (\n <p className=\"text-sm text-text-secondary\">\n {tr('insights.noRenewals', 'No dated renewals in the next 6 months.')}\n </p>\n )}\n </GlassCard>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 lg:grid-cols-2\">\n <Section title={tr('insights.topCosts', 'Top monthly costs')}>\n <GlassCard treatment=\"plain\" className=\"divide-y divide-border-subtle\">\n {stats.topCosts.map((r) => (\n <div key={r.id} className=\"flex items-center justify-between p-4\">\n <span className=\"truncate text-sm text-text-primary\">{r.label}</span>\n <span className=\"ml-3 shrink-0 text-sm font-semibold tabular-nums text-text-primary\">\n {fmt(monthly(r))}\n <span className=\"text-xs font-normal text-text-secondary\">\n {tr('insights.perMonth', '/mo')}\n </span>\n </span>\n </div>\n ))}\n </GlassCard>\n </Section>\n\n <Section title={tr('insights.healthWatchlist', 'Health watchlist')}>\n {stats.watchlist.length ? (\n <GlassCard treatment=\"plain\" className=\"divide-y divide-border-subtle\">\n {stats.watchlist.map((r) => (\n <div key={r.id} className=\"flex items-center gap-3 p-4\">\n <span className=\"w-32 shrink-0 truncate text-sm text-text-primary\">\n {r.label}\n </span>\n <MiniProgress className=\"flex-1\" value={r.healthScore ?? 0} tone=\"negative\" />\n <span\n className={cn(\n 'w-8 shrink-0 text-right text-sm font-medium tabular-nums text-status-warning-text'\n )}\n >\n {r.healthScore}\n </span>\n </div>\n ))}\n </GlassCard>\n ) : (\n <GlassCard treatment=\"plain\" className=\"p-6 text-sm text-text-secondary\">\n {tr(\n 'insights.allHealthy',\n 'Nothing needs review — all scored subscriptions look healthy.'\n )}\n </GlassCard>\n )}\n </Section>\n </div>\n </>\n )}\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* The Universal Billing Layer's invoice ledger across every relationship, plus
|
|
6
6
|
* the passive payment-method annotations that pay for them. Filter by status,
|
|
7
7
|
* mark issued/overdue invoices paid, create invoices, and manage payment
|
|
8
|
-
* methods. Reads `subListInvoices` / `subListPaymentInstruments
|
|
9
|
-
* `
|
|
8
|
+
* methods. Reads `subListInvoices` / `subListPaymentInstruments` /
|
|
9
|
+
* `subListRelationships` (to name the subscription each invoice bills for);
|
|
10
|
+
* writes `subMarkInvoicePaid`, `subCreateInvoice`, `subCreatePaymentInstrument`,
|
|
10
11
|
* `subRemovePaymentInstrument`.
|
|
11
12
|
*/
|
|
12
13
|
export declare function InvoicesPage(): import("react").JSX.Element;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InvoicesPage.d.ts","sourceRoot":"","sources":["../../../src/subscriberbot/pages/InvoicesPage.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"InvoicesPage.d.ts","sourceRoot":"","sources":["../../../src/subscriberbot/pages/InvoicesPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAsmBH,wBAAgB,YAAY,gCAyS3B"}
|